1. 程式人生 > >struts2入門到精通教程

struts2入門到精通教程

為什麼要使用struts2以及Servlet的缺點

servlet的缺點:

   1、寫一個servlet需要在web.xml檔案中配置8行,如果一個系統中servlet很多,則會導致
       web.xml檔案中的內容很多
   2、在專案中很多人編輯一個web.xml檔案會出現版本衝突的問題
   3、在一個servlet中方法的入口只有一個,如果在servlet中寫很多方法,這些方法應該傳遞引數,根據每次
       請求的引數不一致來判斷執行哪個方法
   4、servlet中的方法都有兩個引數request,response,這兩個引數具有嚴重的容器依賴性,所以在
       servlet中寫的程式碼是不能單獨測試的
   5、現在寫一個servlet,為註冊servlet
           ResigterServlet{
               public void doPost(){
                1、許可權的操作
            2、獲取表單中的資料
            3、檔案的上傳的功能
            4、表單上的元素進行驗證
            5、儲存一個使用者
           }
           }
    6、如果在表單中的元素很多,在servlet中要想獲取表單中的資料,那麼在servlet的方法中必要有大量的
        request.getParameter程式碼
    7、在一個servlet的屬性中宣告一個數據,會存線上程安全的問題

Servlet的優點:

因為是最低層的mvc,所以效率比較高

struts2中action是否安全

struts2的action的說明:
1、action是多例項的,每請求一次將會建立一個物件
2、是不存線上程安全的問題的

servletde 重構

第一步:寫一個監聽器在tomcat初始化的時候執行

public class ActionListener implements ServletContextListener{
    /**
     * tomcat銷燬的時候執行
     */
    @Override
    public
void contextDestroyed(ServletContextEvent arg0) { // TODO Auto-generated method stub /** * 在tomcat銷燬的時候,清空application域中的所有的action的配置 */ arg0.getServletContext().setAttribute("actions", null); } /** * tomcat初始化的時候執行 */ @Override public void
contextInitialized(ServletContextEvent arg0) { /** * 1、建立一個map * 2、把key,value放入到map中 * 3、把map放入到application域中 */ Map<String, String> map = new HashMap<String, String>(); map.put("userAction", "cn.itcast.sh08.action.UserAction"); arg0.getServletContext().setAttribute("actions", map); } }

第二步:寫一個ActionServlet:

/**
         * 1、獲取url
         * 2、對url進行解析,把"userAction"的部分解析出來
         * 3、獲取到application
         * 4、從application中把map提取出來
         * 5、根據"userAction"從map中把value提取出來
         * 6、利用java的反射機制進行呼叫
         */
        //itcastsh08_super_servlet/userAction.action
        String uri = request.getRequestURI();
        /**
         * 把uri傳遞進去,解析出來userAction
         */
        String actionName = StringUtils.parse(uri);
        /**
         * 通過key:userAction,得到value:userAction的類名
         */
        Map<String, String> map = (HashMap<String, String>)this.getServletContext().getAttribute("actions");
        String actionClassName = map.get(actionName);
        try {
            /**
             * 得到了請求的action所對應的execute方法
             */
            Class classt = Class.forName(actionClassName);
            String httprequest = "javax.servlet.http.HttpServletRequest";
            Class requestClass = Class.forName(httprequest);
            String httpresponse = "javax.servlet.http.HttpServletResponse";
            Class responseClass = Class.forName(httpresponse);
            Method method = classt.getMethod("execute",requestClass,responseClass);
            String result = (String)method.invoke(classt.newInstance(),request,response);
            request.getRequestDispatcher(result).forward(request, response);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 

這裡的StringUtils是用來獲取userAction的

struts.xml詳解

關於包的name的作用

 <package name="system" namespace="/" extends="struts-default">:其中的name=“”的作用是用來區分不同模組功能下的不同包( package代表一個模組,name為模組的名稱,而且值是唯一的)

關於extend的功能

 extends="struts-default"實際上是把package中name為"struts-default"的包中的所有的功能
如果想要使用某一個包的action,可以使用繼承<package name="aa" namespace="/world" extends="helloworld">
</package>這裡面extend="這裡面是某一個包的名字"

<package name="helloworld" namespace="/base" extends="struts-default">
    <action name="helloWorldAction" class="cn.itcast.sh08.struts2.action.HelloWorldAction">
        <result name="index">index.jsp</result>
    </action>

關於名稱空間:

    1、上述的namespace和url有關係
    2、如果namespace為"/"
            那麼在url中專案名稱後面緊接著跟action中name的名稱
        namespace="/"
        http://localhost:8088/itcastsh08_struts2_package_namespace/helloWorldAction.action
    3、如果namespace為"/base"
        http://localhost:8088/itcastsh08_struts2_package_namespace/base/helloWorldAction.action
    4、如果namespace為"/base"
         itcastsh08_struts2_package_namespace/helloWorldAction.action  
                 該url是請求不到的
         itcastsh08_struts2_package_namespace/base/helloWorldAction.action
                 該url可以請求到
         itcastsh08_struts2_package_namespace/base/a/helloWorldAction.action
                                       也可以請求到 ,查詢規則:
                  1、先找base/a下的helloWorldAction
                  2、再查詢base下的helloWorldAction
    5、在url中加了幾層名稱空間,則在轉向到jsp頁面時,jsp的路徑也會加幾個名稱空間的名字的路徑
            base/a/helloWorldAction.action
            base/a/b/index.jsp
    6、如果採用上述的名稱空間的形式,名稱空間有什麼的名稱,在webroot下就應該建立什麼樣的資料夾

關於兩個配置檔案struts.xml和struts-default.xml

1、這兩個配置檔案都是在伺服器啟動的時候載入的
2、這兩個配置檔案都放在了classpath的根目錄
     struts.xml檔案放在了src的根目錄
     struts-default.xml檔案是在struts2核心包的根目錄下
3、struts.xml檔案是供程式設計師使用的
   struts-default.xml檔案是執行struts2低層的xml檔案
4、先載入struts-default.xml檔案,後加載struts.xml檔案
5、因為dtd都一樣,如果出現相同的元素,後者覆蓋前者

關於result

<result name="list" >/admin/jsp/userAction/list.jsp</result>
        <result name="toList" type="redirectAction">user_list</result>
        一般用的就兩種,result中間的是jsp頁面這樣的是轉發的方式,或者是user_list這表示重定向,重新請求一個方法

關於include的使用

include保證了可以有很多個xml檔案
<struts>
    <include file="struts-helloworld.xml"></include>
</struts>
在企業裡最好保證人手一份xml檔案,這樣不會出現衝突的問題

關於action的幾種寫法

action的寫法
   1、就是一個最簡單的javabean
        public class HelloWorldAction {
            public String execute(){
                System.out.println("hello world");
                return "index";
            }
        }
   2、讓一個action實現介面Action
        public class HelloWorld2Action implements Action{
            /**
             * 如果實現了Action介面,應該把方法寫在execute裡
             */
            public String execute(){
                System.out.println("hello world");
                return SUCCESS;
            }
        }
        <result>index.jsp</result>
    3、讓一個類繼承ActionSupport
        ActionSupport中含有一些功能:
           1、國際化
           2、表單驗證
        如果讓一個action類繼承了ActionSupport就能直接使用ActionSupport中的國際化、表單驗證等功能
    4、如果在action的配置中這樣配置:
         <action name="helloWorld3Action">
            <result>index.jsp</result>
        </action>
        那麼在struts-default.xml檔案中有一句話將起作用:
           <default-class-ref class="com.opensymphony.xwork2.ActionSupport" />
        這個時候將執行ActionSupport中的execute方法

萬用字元的對映

 1、第一種情況
   url:
      http://localhost:8088/itcastsh08_struts2_action/helloWorldAction.action
                 配置檔案:
      <package name="helloworld" namespace="/" extends="struts-default">
        <action name="helloWorldAction" class="cn.itcast.sh08.struts2.action.HelloWorldAction">
            <result name="index">index.jsp</result>
        </action>
      </package>
         如果採用該結構,只能執行helloWorldAction中的execute方法
   2、第二種情況
        在配置檔案的action標籤中可以指定method屬性
        <action name="userAction" method="saveUser" 
            class="cn.itcast.sh08.struts2.action.UserAction">
            <result>index.jsp</result>
        </action>
        url:http://localhost:8088/itcastsh08_struts2_methodpattern/userAction.action
        這個時候,將請求userAction中method屬性的值指定的方法:saveUser
       缺點:
               如果在UserAction中出現很多個方法,因為每一個方法配置一個action,所以有幾個方法就得配置幾個action
   3、第三種情況
        <package name="method2" namespace="/m2" extends="struts-default">
            <action name="userAction" class="cn.itcast.sh08.struts2.action.UserAction">
                <result>index.jsp</result>
            </action>
        </package>
        url:
            http://localhost:8088/itcastsh08_struts2_methodpattern/m2/userAction!deleteUser.action
        UserAction中的其中的一個方法和userAction!deleteUser中的歎號後面的內容一樣
        為動態呼叫該方法
    4、請求UserAction中的saveUser方法:
            要求:a_add.action,b_add.action,c_add.action都可以請求
    5、配置檔案
        <action name="*_add" method="{1}" class="cn.itcast.sh08.struts2.action.UserAction">
            <result>index.jsp</result>
        </action>
    6、請求UserAction和PersonAction中的pattern方法
        <action name="*_pattern" method="pattern" class="cn.itcast.sh08.struts2.action.{1}">
            <result>index.jsp</result>
        </action>
    7、配置檔案中
        <package name="method6" namespace="/" extends="struts-default">
            <action name="UserAction_*" method="{1}" class="cn.itcast.sh08.struts2.action.UserAction">
                <result>{1}.jsp</result>
            </action>
        </package>
    8、配置檔案中
        <action name="*_*" method="{2}" class="cn.itcast.sh08.struts2.action.{1}">
            <result>{2}.jsp</result>
        </action>

        統配的程度越高,匹配的範圍越大,越容易出問題
我通常情況下用的是這樣的
    <action name="user_*" method="{1}" class="cn.itcast.sh08.struts2.action.userAction">
<result name=“list”>list.jsp</result>
</action>
    jsp頁面請求的方式類似於user_list,method表示的是list,list是action中的list方法,result中的name中的list表示的是action中list方法中的return list

先介紹如何使用valueStack儲存資料以及其儲存資料的結構圖

struts2的值棧valueStack (儲存值)

前言

在servlet中解決資料的儲存和顯示
把一個數據放在request,session,application域中,在頁面上利用ognl表示式就可以顯示出來

概述

    1、ValueStack是一個介面,在struts2中使用OGNL表示式實際上是使用實現了ValueStack介面的類OgnlValueStack,這個類是OgnlValueStack的基礎。
    2、ValueStack貫穿整個action的生命週期。每一個action例項都擁有一個ValueStack物件。其中儲存了當前action物件和其他相關物件。

記憶體圖

這裡寫圖片描述

組織結構

這裡寫圖片描述

從圖上可以看出OgnlValueStack和我們有關的內容有兩部分:即OgnlContext和CompoundRoot。所以把這兩部分搞清楚很重要。

valueStack總體結構圖

這裡寫圖片描述

說明:
1、上圖是ognl完整的資料結構圖,可以清晰得看出資料的組成。
2、Context中的_root和ValueStack中的root(物件棧)裡的資料結構和值是一樣的。
3、這就意味著我們只需要操作OgnlContext就可以完成對資料的存和取的操作。
4、ValueStack內部有兩個邏輯的組成部分:
a)ObjectStack
Struts會把動作和相關的物件壓入到ObjectStack中。
b)ContextMap
Struts會把一些對映關係壓入到ContextMap中

獲取valueStack的三種方式

ValueStack valueStack = ActionContext.getContext().getValueStack();
    ValueStack valueStack2 = ServletActionContext.getContext().getValueStack();
    ValueStack valueStack3 = (ValueStack)ServletActionContext.getRequest().getAttribute("struts.valueStack");

valueStack的記憶體結構:

 root:物件棧
 context:OgnlContext
      _root:物件棧
      _values:map棧

物件棧的操作

 1、把資料放入到物件棧中
    valueStack.push  放入到物件棧的棧頂
    valueStack.getRoot().add("aaaa");  放入到了物件棧的棧底
    ActionContext.getContext().getValueStack().set("aaa", "asfd");  把一個map放入到了物件棧的棧頂
    valueStack.getRoot().add(0,"aaaa"); 放入到了物件棧的棧頂
2、從物件棧中把資料提取出來
    ActionContext.getContext().getValueStack().getRoot().get(0);
    ActionContext.getContext().getValueStack().peek();
       獲取物件棧的棧頂的元素
3、移除棧頂元素
     ActionContext.getContext().getValueStack().getRoot().remove(0);
     ActionContext.getContext().getValueStack().pop();

map棧的操作

 1、可以把一個物件放入到map棧中的reuqest域中
    ServletActionContext.getRequest().setAttribute("aaa", "aaa");
2、可以把一個物件放入到map棧的application域中
    ServletActionContext.getServletContext().setAttribute("aaaa", "aaaa");
3、可以把一個物件放入到map棧中
    ActionContext.getContext().put("aaa", "aaaa");

如何將值放在valueStack

/*
     * 把資料放入物件棧中的第一種方式
     */
    public String addDataToObjectStack_1(){
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        /**
         * 把字串新增到了CompoundRoot的第一個位置,我們把第一個位置稱為物件棧的棧頂
         */
        valueStack.push("aaaaa");
        return "";
    }

    /*
     * 把資料放入物件棧中的第二種方式
     */
    public String addDataToObjectStack_2(){
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        /**
         * 把字串新增到了物件棧中
         */
        valueStack.getRoot().add("aaaa");
        return "";
    }

    /*
     * 把資料放入物件棧中的第三種方式
     */
    public String addDataToObjectStack_3(){
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        /**
         * 把字串新增到了物件棧中
         *    把一個map放入到了物件棧的棧頂,"aaa"作為key,"asfd"作為value
         */
        ActionContext.getContext().getValueStack().set("aaa", "asfd");
        return "";
    }

    /**
     * 從物件棧中把資料提取出來的方式
     *    第一種方式
     */
    public String addDataFromObjectStack_1(){
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        /**
         * 提取棧頂的元素
         */
        ActionContext.getContext().getValueStack().getRoot().get(0);
        return "";
    }

    /**
     * 從物件棧中把資料提取出來的方式
     *    第二種方式
     */
    public String addDataFromObjectStack_2(){
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        /**
         * 提取棧頂的元素
         */
        ActionContext.getContext().getValueStack().peek();
        return "";
    }

    /*
     * 把物件棧的棧頂的元素移除
     */
    public String removeDataFromObjectStack_1(){
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        /**
         * 移除棧頂的元素
         */
        ActionContext.getContext().getValueStack().getRoot().remove(0);
        return "";
    }

    /*
     * 把物件棧的棧頂的元素移除
     */
    public String removeDataFromObjectStack_2(){
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        /**
         * 移除棧頂的元素
         */
        ActionContext.getContext().getValueStack().pop();
        return "";
    }

    /**
     * 把一個key,value鍵值對放入到request域中
     */
    public String putObjectToRequest(){
        ServletActionContext.getRequest().setAttribute("aaa", "aaa");
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        return "";
    }

    /**
     * 把一個key,value鍵值對放入到application域中
     */
    public String putObjectToApplication(){
        ServletActionContext.getServletContext().setAttribute("aaaa", "aaaa");
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        return "";
    }

    /**
     * 把一個key,value直接放在map棧中
     */
    public String putDataToMapStack_1(){
        ValueStack valueStack = ActionContext.getContext().getValueStack();
        ActionContext.getContext().put("aaa", "aaaa");
        return "";
    }

ognlContext組織結構(第一部分 資料儲存主要在這裡面)

_values(這裡介紹如何把值放在map中的request application以及session中)
從上述可以看出,OgnlContext實際上有一部分功能是Map。所以可以看出_values就是一個Map屬性。而執行一下下面的程式碼就可以看到:
//在request域中設定一個引數
ServletActionContext.getRequest().setAttribute("req_username","req_username");
//在request域中設定一個引數
ServletActionContext.getRequest().setAttribute("req_psw", "req_psw");
//在session域中設定一個引數
ActionContext.getContext().getSession().put("session_username", "session_username");
//在session域中設定一個引數
ActionContext.getContext().getSession().put("session_psw", "session_psw");

在_values的map中:
主要儲存的地方是
        application     在 ApplicationMap
        request 在       RequestMap
        action  在       自己寫的action
        session 在       SessionMap

_root(第二部分)

這裡寫圖片描述

從圖中可以看出_root實際上CompoundRoot類,從類的組織結構圖中可以看出,這個類實際上是繼承了ArrayList類,也就是說這個類具有集合的功能。而且在預設情況下,集合類的第一個為ValueStackAction,也就是我們自己寫的action。

ognl表示式(顯示操作標籤)

使用方法:

 1、引入標籤庫
    <%@ taglib prefix="s" uri="/struts-tags" %>
        標籤庫的位置在struts2-core-2.3.1.2.jar包中的META-INF/struts-tags.tld
   2、s:debug
            是一個超級連結,當點選該超級連結的時候,valueStack中的內容顯示出來了
   3、訪問valueStack中的資料
       1、如果要訪問map棧中的資料,加"#"
       2、如果要訪問物件棧中的資料,直接訪問屬性即可
   4、s:property標籤
                     說明:
          1、是一個輸出標籤
          2、如果不寫value屬性,輸出棧頂的元素
          3、如果執行了下面的程式碼
                Person person = new Person();
                person.setPid(1L);
                person.setName("王二麻子");
                //把person物件放入到了棧頂
                ActionContext.getContext().getValueStack().push(person);
                       把物件放入到棧頂,其屬性會直接暴漏出來,在頁面上可以直接訪問其屬性
                <s:property value="name"/>
                    其頁面上的物件棧中的屬性的名稱來自於方法
          4、如果一個物件在物件棧中,那麼該物件如果有set或者get方法,例如:
               getAaa(){
                 return "xxx";
               }
                那麼aaa會作為屬性的名稱,xxx會作為屬性的值儲存在物件棧中
          5、如果物件棧中出現相同的屬性,則會從上向下找,直到找到就停止了
          6、如果把一個物件放入到request域中
               ServletActionContext.getRequest().setAttribute("person", person);
                頁面上可以
                <s:property value="#request.person.name"/>
                <s:property value="#request.person.getName()"/>
          7、如果把一個物件放入到各種域中
                ServletActionContext.getServletContext().setAttribute("a_app", "a_app");
                在頁面上可以利用
                   <s:property value="#attr.a_app"/>從各種域中查詢相應的key值
          8、可以利用parameters輸出表單中的內容
                <s:property value="#parameters.a[0]"/>
   5、s:iterator標籤
        1、當value屬性不寫,則預設迭代棧頂的元素
        2、value屬性指向了要迭代的集合List,Set,Map,[]
        3、當前正在迭代的元素在棧頂
        4、var屬性的值表示正在遍歷的元素,該值在map棧中
        5、status屬性表示正在遍歷的那一行的狀態
            int getCount() 返回當前迭代的元素個數
            int getIndex() 返回當前迭代元素的索引
            boolean isEven() 返回當前迭代元素的索引是否是偶數
            boolean isOdd()  返回當前迭代元素的索引是否是奇數
            boolean isFirst()  返回當前迭代元素是否為第一個元素
            boolean isLast()  返回當前迭代元素是否為最後一個元素
        6、使行變色

ognl表示式(UI標籤)

使用方法

1、在頁面上可以寫struts2的標籤,但是瀏覽器是不識別struts2標籤的
2、當在頁面上寫完struts2標籤的時候,struts2核心會對標籤進行翻譯成html標籤,在翻譯的過程中會多增加很多內容
            <s:form action="">
                <s:textfield name="username" value="111"></s:textfield>
            </s:form>

            翻譯成html:
            <table class="wwFormTable">
                <tbody>
                    <tr>
                        <td class="tdLabel"></td>
                    <td>
                        <input id="_username" type="text" value="111" name="username">
                    </td>
                    </tr>
                </tbody>
            </table>
3、修改方案:
          在伺服器啟動的時候,struts2內部會去org/apache/struts2下面載入一個properties檔案:default.properties檔案
          一些引數的說明:
          struts.i18n.encoding=UTF-8  預設的編碼
          struts.action.extension=action,,  預設的副檔名
          struts.devMode = false   開發模式  
            預設值為false   改了配置檔案以後必須重新啟動
            值為true       改了配置檔案以後,struts2內部會自動檢查,重新載入
          struts.ui.theme=xhtml  ui的主題 
                可以把xhtml的值改成
4、改變default.properties檔案中的配置
        在xml檔案中,有一個元素為constant,為常量元素,該元素的作用就是為了改變default.properties檔案中的值
          <constant name="struts.devMode" value="true"/>    開發模式
          <constant name="struts.ui.theme" value="simple"/>  簡單樣式

5、兩個比較重要的標籤
      s:select
          value屬性    指向集合的位置
          listKey   option中的value
          listValue  option標籤的內容
          headerKey  第一個option的value
          headerValue  第一個option的內容
      s:checkboxlist
            屬性同上
            必須有name屬性
6、ui標籤的好處:
     1、在頁面上如果使用struts2的ui標籤,不用再寫過濾器進行編碼的處理
     2、使用struts2的ui標籤對於資料的回顯示很方便的
     3、一般情況下,在頁面上需要對資料進行回顯,則資料放在物件棧中
            ActionContext.getContext().getValueStack().push(person1);
     4、頁面上可以根據struts2標籤中的name屬性進行回顯
           <s:textfield name="name"></s:textfield>
     5、如果把資料放入到了map棧中,則頁面上必須根據value進行回顯
     6、在s:form標籤的各種元素中:
           s:textfield
           s:textarea
           s:password
           ....
            如果要想用value屬性進行回顯,也就是說value屬性內容要跟ognl表示式
                value="%{ognl表示式}"

攔截器interceptor

攔截器的目的:

    如果在一個業務邏輯方法中設計到的邏輯相當複雜,可以把這些業務分離開:
       例如:儲存使用者
            1、啟動日誌
            2、檢查許可權
            3、檔案的上傳
            4、儲存使用者
          如果用傳統的方法做,以上4點都在同一個方法中,這樣耦合性很強

目標:

          把這四方面的內容分開,完全鬆耦合

不用攔截器實現步驟:

1、準備頁面:
        在頁面中準備一個文字框,該文字框在一個表單中
2、準備action
                          在action中有一個方法:saveUser
                          在action中準備一個屬性,該屬性是為了獲取文字框的值
                          在saveUser中做如下的工作:
                對該屬性的值進行判斷,如果值為"admin",輸出"save user"
                                                         如果值不為"admin",輸出"沒有許可權進行訪問"

用攔截器實現的步驟:

1、準備頁面
2、準備action
    public class InterceptorAction extends ActionSupport{
        public String saveUser(){
            ActionContext.getContext().put("message", "save user");
            return "privilege";
        }
    }
    說明:該action的saveUser方法和許可權沒有任何聯絡
3、建立一個攔截器
     public class PrivilegeInterceptor implements Interceptor{
        @Override
        public String intercept(ActionInvocation arg0) throws Exception {
            /**
                * 接受頁面的引數進行判斷
             */
            String username = ServletActionContext.getRequest().getParameter("username");
            if("admin".equals(username)){
                return arg0.invoke();
            }else{
                ActionContext.getContext().put("message", "許可權不足,沒有辦法訪問");
                return "privilege";
            }
        }
     }
  4、配置
        <interceptors>
            <!-- 
                宣告一個攔截器
             -->
            <interceptor name="privilege" class="cn.itcast.sh08.struts2.interceptor.PrivilegeInterceptor"></interceptor>
            <!-- 
                聲明瞭一個攔截器棧
             -->
            <interceptor-stack name="privilegeStack">
                <!-- 
                    引用預設的攔截器棧
                 -->
                <interceptor-ref name="defaultStack"></interceptor-ref>
                <!-- 
                    引用自己建立的攔截器
                 -->
                <interceptor-ref name="privilege"></interceptor-ref>
            </interceptor-stack>
        </interceptors>
        <default-interceptor-ref name="privilegeStack"></default-interceptor-ref>

攔截器的意義以及其引數解析

攔截器的意義在於:可以把一些和業務邏輯沒有關係的程式碼放入到攔截器中,做到這些程式碼和業務邏輯的鬆耦合

概念:
        1、攔截器:實質上是一個類,實現了Interceptor介面的一個類
        2、攔截器棧:把很多個攔截器集中在一起就是攔截器棧
        3、struts2有一個預設的攔截器棧,該棧在struts-default.xml檔案中的struts-default包中
            結構為:
                <package name="struts-default">
                    <interceptors>
                        //宣告一個攔截器
                        <interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
                        //宣告一個攔截器棧
                        <interceptor-stack name="defaultStack">
                            //引用上面宣告的攔截器
                            <interceptor-ref name="exception"/>
                            <interceptor-ref name="alias"/>
                            <interceptor-ref name="servletConfig"/>
                            <interceptor-ref name="i18n"/>
                            <interceptor-ref name="prepare"/>
                            <interceptor-ref name="chain"/>
                            <interceptor-ref name="debugging"/>
                            <interceptor-ref name="scopedModelDriven"/>
                            <interceptor-ref name="modelDriven"/>
                            <interceptor-ref name="fileUpload"/>
                            <interceptor-ref name="checkbox"/>
                            <interceptor-ref name="multiselect"/>
                            <interceptor-ref name="staticParams"/>
                            <interceptor-ref name="actionMappingParams"/>
                            <interceptor-ref name="params">
                              <param name="excludeParams">dojo\..*,^struts\..*</param>
                            </interceptor-ref>
                            <interceptor-ref name="conversionError"/>
                            <interceptor-ref name="validation">
                                <param name="excludeMethods">input,back,cancel,browse</param>
                            </interceptor-ref>
                            <interceptor-ref name="workflow">
                                <param name="excludeMethods">input,back,cancel,browse</param>
                            </interceptor-ref>
                        </interceptor-stack>
                    </interceptors>
                    //讓struts2內部執行預設的攔截器棧或者攔截器
                    <default-interceptor-ref name="defaultStack"/>
                </package>
       4、攔截器的執行順序:
                按照攔截器棧從上到下執行,執行完攔截器以後,再執行action,例如:
                  <interceptor-stack name="privilegeStack">
                    <interceptor-ref name="defaultStack"></interceptor-ref>
                    <interceptor-ref name="privilege"></interceptor-ref>
                  </interceptor-stack>
                 先執行預設的攔截器棧,後執行privilege
                 <interceptor-stack name="privilegeStack">
                    <interceptor-ref name="privilege"></interceptor-ref>
                    <interceptor-ref name="defaultStack"></interceptor-ref>
                  </interceptor-stack>
                  先執行privilege,後執行預設的攔截器棧

屬性驅動(可用但不常用)

解決的問題就是在jsp頁面寫一個表單,通過name就可以在action中獲取jsp頁面中表單的值

屬性驅動:
1、目的:在action中宣告一些屬性,這些屬效能夠獲取到表單中的值
2、步驟:
1、在action中宣告一些屬性,屬性的名稱和頁面上name屬性的名稱一致
2、這些屬性在action中必須有setter和getter方法
3、原理:
在瀏覽器提交一個url請求時,先建立一個action,並且把action放入到物件棧中,這個時候
action的屬性會出現在物件棧中,然後經過一個攔截器ParametersInterceptor攔截器
做的事情:
1、獲取頁面上表單中的name和value的值
2、把上述的name和value的值封裝成一個map
3、根據valueStack.setValue(name,value);來把頁面上的值設定到物件棧的name屬性中

模型驅動 (常用)

模型驅動和屬性驅動是的區別

區別:模型驅動是先建立一個bean將所有的欄位封裝進去,而屬性驅動是直接在action中寫欄位,然後實現get和set方法

屬性驅動:
    1、建立一個javabean,javabean中的屬性和頁面中表單中的name屬性的內容保持一致
    2、在action裡實現一個介面ModelDriven<Person>
    3、在action中宣告一個屬性,並且建立該屬性的物件
        private Person modle = new Person();
    4、在action中有一個方法:
        @Override
        public Person getModel() {
            // TODO Auto-generated method stub
            return this.modle;
        }
        該方法返回模型驅動物件

模型驅動的原理:
    模型驅動經過兩個攔截器:
      1、ModelDrivenInterceptor
          1、得到action
          2、由action強制轉化成ModelDriver
          3、由ModelDriver.getModel()獲取模型物件
          4、把模型物件放入到棧頂
      2、ParameterInterceptor
            把form表單的資料封裝到相應的物件棧中的屬性中

threadlocal解析(重要)

在ActionContext類中
static ThreadLocal actionContext = new ThreadLocal();
//把context放入到當前執行緒中
public static void setContext(ActionContext context) {
actionContext.set(context);
}
//從threadlocal中把context提取出來
public static ActionContext getContext() {
return (ActionContext) actionContext.get();
}

這樣做就不用引數的傳遞了,只要在同一個執行緒中就可以了