溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

struts2 自實(shí)現(xiàn)

發(fā)布時(shí)間:2020-10-12 21:21:39 來源:網(wǎng)絡(luò) 閱讀:227 作者:a13243168981 欄目:web開發(fā)
  1. Struts2 自實(shí)現(xiàn):

1). 搭建 Struts2 的開發(fā)環(huán)境

2). 不需要顯式的定義 Filter, 而使用的是 struts2 的配置文件.

3). details.jsp 比先前變得簡(jiǎn)單了.

${requestScope.product.productName} -> ${productName}

4). 步驟:

I. 由 product-input.action 轉(zhuǎn)到 /WEB-INF/pages/input.jsp

在 struts2 中配置一個(gè) action

<action name="product-input">
    <result>/WEB-INF/pages/input.jsp</result>
</action>

II. 由 input.jsp 頁面的 action: product-save.action 到 Product's save, 再到 /WEB-INF/pages/details.jsp

<action name="product-save" class="com.atguigu.struts2.helloworld.Product"
    method="save">
    <result name="details">/WEB-INF/pages/details.jsp</result>  
</action>

在 Prodcut 中定義一個(gè) save 方法, 且返回值為 details

5. result:

1). result 是 action 節(jié)點(diǎn)的子節(jié)點(diǎn)

2). result 代表 action 方法執(zhí)行后, 可能去的一個(gè)目的地

3). 一個(gè) action 節(jié)點(diǎn)可以配置多個(gè) result 子節(jié)點(diǎn).

4). result 的 name 屬性值對(duì)應(yīng)著 action 方法可能有的一個(gè)返回值.

<result name="index">/index.jsp</result>

5). result 一共有 2 個(gè)屬性, 還有一個(gè)是 type: 表示結(jié)果的響應(yīng)類型

6). result 的 type 屬性值在 struts-default 包的 result-types 節(jié)點(diǎn)的 name 屬性中定義.
常用的有

dispatcher(默認(rèn)的): 轉(zhuǎn)發(fā). 同 Servlet 中的轉(zhuǎn)發(fā).
redirect: 重定向
redirectAction: 重定向到一個(gè) Action
注意: 通過 redirect 的響應(yīng)類型也可以便捷的實(shí)現(xiàn) redirectAction 的功能!

<result name="index" type="redirectAction">
    <param name="actionName">testAction</param>
    <param name="namespace">/atguigu</param>
</result>

OR

<result name="index" type="redirect">/atguigu/testAction.do</result>

> chain: 轉(zhuǎn)發(fā)到一個(gè) Action
    注意: 不能通過 type=dispatcher 的方式轉(zhuǎn)發(fā)到一個(gè) Action

     只能是:

<result name="test" type="chain">
    <param name="actionName">testAction</param>
    <param name="namespace">/atguigu</param>
</result>

不能是:

<result name="test">/atguigu/testAction.do</result>
  1. ActionSupport

1). ActionSupport 是默認(rèn)的 Action 類: 若某個(gè) action 節(jié)點(diǎn)沒有配置 class 屬性, 則 ActionSupport 即為
待執(zhí)行的 Action 類. 而 execute 方法即為要默認(rèn)執(zhí)行的 action 方法

<action name="testActionSupport">
<result>/testActionSupport.jsp</result>
</action>

等同于

<action name="testActionSupport"
class="com.opensymphony.xwork2.ActionSupport"
method="execute">
<result>/testActionSupport.jsp</result>
</action>

2). 在手工完成字段驗(yàn)證, 顯示錯(cuò)誤消息, 國際化等情況下, 推薦繼承 ActionSupport.

  1. 關(guān)于 Struts2 請(qǐng)求的擴(kuò)展名問題

1). org.apache.struts2 包下的 default.properties 中配置了 Struts2 應(yīng)用個(gè)的一些常量

2). struts.action.extension 定義了當(dāng)前 Struts2 應(yīng)用可以接受的請(qǐng)求的擴(kuò)展名.

3). 可以在 struts.xml 文件中以常量配置的方式修改 default.properties 所配置的常量.

<constant name="struts.action.extension" value="action,do,"></constant>

  1. 在 Action 中訪問 WEB 資源:

1). 什么是 WEB 資源 ?

HttpServletRequest, HttpSession, ServletContext 等原生的 Servlet API。 

2). 為什么訪問 WEB 資源?

B\S 的應(yīng)用的 Controller 中必然需要訪問 WEB 資源: 向域?qū)ο笾凶x寫屬性, 讀寫 Cookie, 獲取 realPath ....

3). 如何訪問 ?

I. 和 Servlet API 解耦的方式: 只能訪問有限的 Servlet API 對(duì)象, 且只能訪問其有限的方法(讀取請(qǐng)求參數(shù), 讀寫域?qū)ο蟮膶傩? 使 session 失效...).

> 使用 ActionContext

> 實(shí)現(xiàn) XxxAware 接口

> 選用的建議: 若一個(gè) Action 類中有多個(gè) action 方法, 且多個(gè)方法都需要使用域?qū)ο蟮?Map 或 parameters, 則建議使用
Aware 接口的方式

> session 對(duì)應(yīng)的 Map 實(shí)際上是 SessionMap 類型的! 強(qiáng)轉(zhuǎn)后若調(diào)用其 invalidate() 方法, 可以使其 session 失效!

II. 和 Servlet API 耦合的方式: 可以訪問更多的 Servlet API 對(duì)象, 且可以調(diào)用其原生的方法.

> 使用 ServletActionContext

> 實(shí)現(xiàn) ServletXxxAware 接口.
  1. 復(fù)習(xí)搭建 Struts2 的開發(fā)環(huán)境: 3 個(gè)步驟

  2. action VS Action 類

1). action: 代表一個(gè) Struts2 的請(qǐng)求.

2). Action 類: 能夠處理 Struts2 請(qǐng)求的類.

> 屬性的名字必須遵守與 JavaBeans 屬性名相同的命名規(guī)則. 
    屬性的類型可以是任意類型. 從字符串到非字符串(基本數(shù)據(jù)庫類型)之間的數(shù)據(jù)轉(zhuǎn)換可以自動(dòng)發(fā)生

> 必須有一個(gè)不帶參的構(gòu)造器: 通過反射創(chuàng)建實(shí)例 

> 至少有一個(gè)供 struts 在執(zhí)行這個(gè) action 時(shí)調(diào)用的方法

> 同一個(gè) Action 類可以包含多個(gè) action 方法. 

> Struts2 會(huì)為每一個(gè) HTTP 請(qǐng)求創(chuàng)建一個(gè)新的 Action 實(shí)例, 即 Action 不是單例的, 是線程安全的. 
  1. 關(guān)于值棧:

1). helloWorld 時(shí), ${productName} 讀取 productName 值, 實(shí)際上該屬性并不在 request 等域?qū)ο笾? 而是從值棧中獲取的.

2). ValueStack:

I. 可以從 ActionContext 中獲取值棧對(duì)象
II. 值棧分為兩個(gè)邏輯部分

> Map 棧: 實(shí)際上是 OgnlContext 類型, 是個(gè) Map, 也是對(duì) ActionContext 的一個(gè)引用. 里邊保存著各種 Map:
         requestMap, sessionMap, applicationMap, parametersMap, attr

> 對(duì)象棧: 實(shí)際上是 CompoundRoot 類型, 是一個(gè)使用 ArrayList 定義的棧. 里邊保存各種和當(dāng)前 Action 實(shí)例相關(guān)的對(duì)象.
                   是一個(gè)數(shù)據(jù)結(jié)構(gòu)意義的棧.
  1. Struts2 利用 s:property 標(biāo)簽和 OGNL 表達(dá)式來讀取值棧中的屬性值

    1). 值棧中的屬性值:

    對(duì)于對(duì)象棧: 對(duì)象棧中某一個(gè)對(duì)象的屬性值

    Map 棧: request, session, application 的一個(gè)屬性值 或 一個(gè)請(qǐng)求參數(shù)的值.

    2). 讀取對(duì)象棧中對(duì)象的屬性:

    若想訪問 Object Stack 里的某個(gè)對(duì)象的屬性. 可以使用以下幾種形式之一:

    object.propertyName ; object['propertyName'] ; object["propertyName"]

    ObjectStack 里的對(duì)象可以通過一個(gè)從零開始的下標(biāo)來引用. ObjectStack 里的棧頂對(duì)象可以用 [0] 來引用,
    它下面的那個(gè)對(duì)象可以用 [1] 引用.

    [0].message

    [n] 的含義是從第 n 個(gè)開始搜索, 而不是只搜索第 n 個(gè)對(duì)象

    若從棧頂對(duì)象開始搜索, 則可以省略下標(biāo)部分: message

    結(jié)合 s:property 標(biāo)簽: <s:property value="[0].message" /> <s:property value="message" />

    3). 默認(rèn)情況下, Action 對(duì)象會(huì)被 Struts2 自動(dòng)的放到值棧的棧頂.

  2. 使用 paramsPrepareParamsStack 攔截器棧后的運(yùn)行流程

1). paramsPrepareParamsStack 和 defaultStack 一樣都是攔截器棧. 而 struts-default 包默認(rèn)使用的是
defaultStack

2). 可以在 Struts 配置文件中通過以下方式修改使用的默認(rèn)的攔截器棧

<default-interceptor-ref name="paramsPrepareParamsStack"></default-interceptor-ref>

3). paramsPrepareParamsStack 攔截器在于

params -> modelDriven -> params

所以可以先把請(qǐng)求參數(shù)賦給 Action 對(duì)應(yīng)的屬性, 再根據(jù)賦給 Action 的那個(gè)屬性值決定壓到值棧棧頂?shù)膶?duì)象, 最后再為棧頂對(duì)象的屬性賦值.

對(duì)于 edit 操作而言:

I. 先為 EmployeeAction 的 employeeId 賦值
II. 根據(jù) employeeId 從數(shù)據(jù)庫中加載對(duì)應(yīng)的對(duì)象, 并放入到值棧的棧頂
III. 再為棧頂對(duì)象的 employeeId 賦值(實(shí)際上此時(shí) employeeId 屬性值已經(jīng)存在)
IV. 把棧頂對(duì)象的屬性回顯在表單中.

4). 關(guān)于回顯: Struts2 表單標(biāo)簽會(huì)從值棧中獲取對(duì)應(yīng)的屬性值進(jìn)行回顯.

5). 存在的問題:

getModel 方法

public Employee getModel() {
if(employeeId == null)
employee = new Employee();
else
employee = dao.get(employeeId);

return employee;

}

I. 在執(zhí)行刪除的時(shí)候, employeeId 不為 null, 但 getModel 方法卻從數(shù)據(jù)庫加載了一個(gè)對(duì)象. 不該加載!
II. 指向查詢?nèi)啃畔r(shí), 也 new Employee() 對(duì)象. 浪費(fèi)!

6). 解決方案: 使用 PrepareInterceptor 和 Preparable 接口.

7). 關(guān)于 PrepareInterceptor

[分析后得到的結(jié)論]

若 Action 實(shí)現(xiàn)了 Preparable 接口, 則 Struts 將嘗試執(zhí)行 prepare[ActionMethodName] 方法,
若 prepare[ActionMethodName] 不存在, 則將嘗試執(zhí)行 prepareDo[ActionMethodName] 方法.
若都不存在, 就都不執(zhí)行.

若 PrepareInterceptor 的 alwaysInvokePrepare 屬性為 false,
則 Struts2 將不會(huì)調(diào)用實(shí)現(xiàn)了 Preparable 接口的 Action 的 prepare() 方法

[能解決 5) 的問題的方案]

可以為每一個(gè) ActionMethod 準(zhǔn)備 prepare[ActionMethdName] 方法, 而拋棄掉原來的 prepare() 方法
將 PrepareInterceptor 的 alwaysInvokePrepare 屬性置為 false, 以避免 Struts2 框架再調(diào)用 prepare() 方法.

如何在配置文件中為攔截器棧的屬性賦值: 參看 /struts-2.3.15.3/docs/WW/docs/interceptors.html

<interceptors>
<interceptor-stack name="parentStack">
<interceptor-ref name="defaultStack">
<param name="params.excludeParams">token</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>

<default-interceptor-ref name="parentStack"/>

----------------------------------源代碼解析---------------------------------

public String doIntercept(ActionInvocation invocation) throws Exception {
//獲取 Action 實(shí)例
Object action = invocation.getAction();

//判斷 Action 是否實(shí)現(xiàn)了 Preparable 接口
if (action instanceof Preparable) {
    try {
        String[] prefixes;
        //根據(jù)當(dāng)前攔截器的 firstCallPrepareDo(默認(rèn)為 false) 屬性確定 prefixes
        if (firstCallPrepareDo) {
            prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX};
        } else {
            prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX};
        }
        //若為 false, 則 prefixes: prepare, prepareDo
        //調(diào)用前綴方法.
        PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes);
    }
    catch (InvocationTargetException e) {

        Throwable cause = e.getCause();
        if (cause instanceof Exception) {
            throw (Exception) cause;
        } else if(cause instanceof Error) {
            throw (Error) cause;
        } else {
            throw e;
        }
    }

    //根據(jù)當(dāng)前攔截器的 alwaysInvokePrepare(默認(rèn)是 true) 決定是否調(diào)用 Action 的 prepare 方法
    if (alwaysInvokePrepare) {
        ((Preparable) action).prepare();
    }
}

return invocation.invoke();

}

PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes) 方法:

public static void invokePrefixMethod(ActionInvocation actionInvocation, String[] prefixes) throws InvocationTargetException, IllegalAccessException {
//獲取 Action 實(shí)例
Object action = actionInvocation.getAction();
//獲取要調(diào)用的 Action 方法的名字(update)
String methodName = actionInvocation.getProxy().getMethod();

if (methodName == null) {
    // if null returns (possible according to the docs), use the default execute 
    methodName = DEFAULT_INVOCATION_METHODNAME;
}

//獲取前綴方法
Method method = getPrefixedMethod(prefixes, methodName, action);

//若方法不為 null, 則通過反射調(diào)用前綴方法
if (method != null) {
    method.invoke(action, new Object[0]);
}

}

PrefixMethodInvocationUtil.getPrefixedMethod 方法:

public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) {
assert(prefixes != null);
//把方法的首字母變?yōu)榇髮?br/>String capitalizedMethodName = capitalizeMethodName(methodName);

//遍歷前綴數(shù)組
for (String prefixe : prefixes) {
    //通過拼接的方式, 得到前綴方法名: 第一次 prepareUpdate, 第二次 prepareDoUpdate
    String prefixedMethodName = prefixe + capitalizedMethodName;
    try {
        //利用反射獲從 action 中獲取對(duì)應(yīng)的方法, 若有直接返回. 并結(jié)束循環(huán).
        return action.getClass().getMethod(prefixedMethodName, EMPTY_CLASS_ARRAY);
    }
    catch (NoSuchMethodException e) {
        // hmm -- OK, try next prefix
        if (LOG.isDebugEnabled()) {
            LOG.debug("cannot find method [#0] in action [#1]", prefixedMethodName, action.toString());
        }
    }
}
return null;

}

  1. Action 實(shí)現(xiàn) ModelDriven 接口后的運(yùn)行流程

1). 先會(huì)執(zhí)行 ModelDrivenInterceptor 的 intercept 方法.

public String intercept(ActionInvocation invocation) throws Exception {
    //獲取 Action 對(duì)象: EmployeeAction 對(duì)象, 此時(shí)該 Action 已經(jīng)實(shí)現(xiàn)了 ModelDriven 接口
    //public class EmployeeAction implements RequestAware, ModelDriven<Employee>
    Object action = invocation.getAction();

    //判斷 action 是否是 ModelDriven 的實(shí)例
    if (action instanceof ModelDriven) {
        //強(qiáng)制轉(zhuǎn)換為 ModelDriven 類型
        ModelDriven modelDriven = (ModelDriven) action;
        //獲取值棧
        ValueStack stack = invocation.getStack();
        //調(diào)用 ModelDriven 接口的 getModel() 方法
        //即調(diào)用 EmployeeAction 的 getModel() 方法
        /*
        public Employee getModel() {
            employee = new Employee();
            return employee;
        }
        */
        Object model = modelDriven.getModel();
        if (model !=  null) {
            //把 getModel() 方法的返回值壓入到值棧的棧頂. 實(shí)際壓入的是 EmployeeAction 的 employee 成員變量
            stack.push(model);
        }
        if (refreshModelBeforeResult) {
            invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));
        }
    }
    return invocation.invoke();
}

2). 執(zhí)行 ParametersInterceptor 的 intercept 方法: 把請(qǐng)求參數(shù)的值賦給棧頂對(duì)象對(duì)應(yīng)的屬性. 若棧頂對(duì)象沒有對(duì)應(yīng)的屬性, 則查詢
值棧中下一個(gè)對(duì)象對(duì)應(yīng)的屬性...

3). 注意: getModel 方法不能提供以下實(shí)現(xiàn). 的確會(huì)返回一個(gè) Employee 對(duì)象到值棧的棧頂. 但當(dāng)前 Action
的 employee 成員變量卻是 null.

public Employee getModel() {
return new Employee();
}

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI