Struts2 框架學(xué)習(xí)

概述

  1. 什么是Struts2的框架

    • Struts2是Struts1的下一代產(chǎn)品,是在 struts1和WebWork的技術(shù)基礎(chǔ)上進(jìn)行了合并的全新的Struts 2框架。
    • 其全新的Struts 2的體系結(jié)構(gòu)與Struts 1的體系結(jié)構(gòu)差別巨大。
    • Struts 2以WebWork為核心,采用攔截器的機制來處理用戶的請求,這樣的設(shè)計也使得業(yè)務(wù)邏輯控制器能夠與ServletAPI完全脫離開,所以Struts 2可以理解為WebWork的更新產(chǎn)品。
    • 雖然從Struts 1到Struts 2有著太大的變化,但是相對于WebWork,Struts 2的變化很小。
  2. Struts2是一個基于MVC設(shè)計模式的Web層框架

    • MVC和JavaEE的三層結(jié)構(gòu)
      • MVC設(shè)計模式:是由一些網(wǎng)站的開發(fā)人員提出來的
      • JavaEE三層結(jié)構(gòu):SUN公司為EE開發(fā)劃分的結(jié)構(gòu)
  3. 常見的Web層的框架

    • Struts1
    • Struts2
    • Webwork
    • SpringMVC
  4. Web層框架的特點

    • 都是一個特點,前端控制器模式
    • 記住:前端控制器(核心的控制器)
    • Struts2框架前端的控制器就是過濾器

Struts2快速入門的環(huán)境準(zhǔn)備

  1. 創(chuàng)建WEB項目,編寫JSP的頁面,編寫超鏈接,點擊超鏈接發(fā)送請求,請求服務(wù)器,讓服務(wù)器的方法去執(zhí)行?。?/p>

    <h3>Struts2的入門程序</h3>
    <a href="${ pageContext.request.contextPath }/hello.action">Struts2入門程序</a>
    
  2. 下載Struts2的開發(fā)包

  3. 解壓struts-2.3.24-all.zip包

    • 解壓后會看到有包和一些文件,大家需要掌握包相關(guān)的信息
      • apps -- Struts2框架提供了一些應(yīng)用
      • libs -- Struts2框架開發(fā)的jar包
      • docs -- Struts2框架開發(fā)文檔
      • src -- Struts2框架源碼
  4. 引入需要開發(fā)的jar包

    • Struts2框架的開發(fā)jar包非常多,但是不是所有都是必須要引入的,有一些必須要導(dǎo)入的jar包,這些jar包可以從Struts2框架提供的應(yīng)用中找到。
    • 大家可以打開apps目錄,然后找到struts2-blank.war應(yīng)用。war包和zip包的壓縮格式是一樣的,所以可以自己修改后綴名,解壓。
    • 找到解壓后的應(yīng)用,打開WEB-INF/lib目錄下所以的jar包。復(fù)制到工程中,就可以了。
  5. 需要配置Struts2的前端控制器,注意:這一步是必須要做的操作,這是Struts2核心的控制器。

    • Struts2的前端控制器就是一個過濾器,那么過濾器相關(guān)知識咱們都學(xué)習(xí)過,需要在web.xml中進(jìn)行配置。
    • 前端控制器的類的路徑和名稱:org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
    • 具體配置如下
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    

</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>



6. 編寫Action類
Action類是動作類,是Struts2處理請求,封裝數(shù)據(jù),響應(yīng)頁面的核心控制器。需要自己編寫。

 ```
 /**
  * Stuts2框架都使用Action類處理用戶的請求
  * @author Administrator
  */
 public class HelloAction {
     
     /**
      * Action類中的方法簽名有要求的,必須這么做
      * public 共有的
      * 必須有返回值,必須String類型
      * 方法名稱可以是任意的,但是不能有參數(shù)列表
      */
     public String sayHello(){
         // 編寫代碼 接收請求的參數(shù)
         System.out.println("Hello Struts2!!");
         return "ok";
     }
 ```

7. 編寫Struts的配置文件
 1. 配置文件名稱是struts.xml(名稱必須是struts.xml)
 2. 在src下引入struts.xml配置文件(配置文件的路徑必須是在src的目錄下)
 3. 配置如下

 ```
 <?xml version="1.0" encoding="UTF-8" ?>
     <!DOCTYPE struts PUBLIC
         "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
         "http://struts.apache.org/dtds/struts-2.3.dtd">
 
     <struts>
         <package name="default" namespace="/" extends="struts-default">
             <!-- 配置Action -->
               <action name="hello" class="com.itheima.action.HelloAction"
         method="sayHello">
                  <!-- 配置跳轉(zhuǎn)的頁面,路徑的寫法:在Struts2框架中,不管是轉(zhuǎn)發(fā)還是重定向,都不用寫項目名 -->
                  <result name="ok">/demo1/suc.jsp</result>
               </action>
             </action>
         </package>
     </struts>
 ```

###入門總結(jié)之Struts2的執(zhí)行流程

1. 執(zhí)行的流程
 * 編寫的頁面,點擊超鏈接,請求提交到服務(wù)器端。
 * 請求會先經(jīng)過Struts2的核心過濾器(StrutsPrepareAndExecuteFilter)
     * 過濾器的功能是完成了一部分代碼功能
     * 就是一系列的攔截器執(zhí)行了,進(jìn)行一些處理工作。
     * 咱們可以在struts-default.xml配置文件中看到有很多的攔截器。可以通過斷點的方式來演示。
     * 攔截器執(zhí)行完后,會根據(jù)struts.xml的配置文件找到請求路徑,找到具體的類,通過反射的方式讓方法執(zhí)行。

2. 總結(jié)
 * JSP頁面-->StrutsPrepereAndExecuteFilter過濾器-->執(zhí)行一系列攔截器(完成了部分代碼)-->執(zhí)行到目標(biāo)Action-->返回字符串-->結(jié)果頁面(result)-->頁面跳轉(zhuǎn)


![](media/14899060291645/14899102234172.jpg)

***
<br>
##Struts2框架配置文件加載的順序

0. 需要掌握
 * 加載了哪些個配置文件(重點的)
 * 配置文件的名稱是什么
 * 配置文件的位置
 * 配置文件的作用

1. Struts2框架的核心是StrutsPrepareAndExecuteFilter過濾器,該過濾器有兩個功能
 * Prepare       -- 預(yù)處理,加載核心的配置文件
 * Execute       -- 執(zhí)行,讓部分?jǐn)r截器執(zhí)行

2. StrutsPrepareAndExecuteFilter過濾器會加載哪些配置文件呢?
 * 通過源代碼可以看到具體加載的配置文件和加載配置文件的順序
     * init_DefaultProperties();                 -- 加載org/apache/struts2/default.properties
     * init_TraditionalXmlConfigurations();      -- 加載struts-default.xml,struts-plugin.xml,struts.xml
     * init_LegacyStrutsProperties();            -- 加載自定義的struts.properties.
     * init_CustomConfigurationProviders();      -- 加載用戶自定義配置提供者
     * init_FilterInitParameters() ;             -- 加載web.xml

3. 重點了解的配置文件
 * default.properties        -- 在org/apache/struts2/目錄下,代表的是配置的是Struts2的常量的值
 * struts-default.xml        -- 在Struts2的核心包下,代表的是Struts2核心功能的配置(Bean、攔截器、結(jié)果類型等)
 * struts.xml                -- 重點中的重點配置,代表WEB應(yīng)用的默認(rèn)配置,在工作中,基本就配置它就可以了?。。梢耘渲贸A浚? * web.xml                   -- 配置前端控制器(可以配置常量)

 * 注意:
     * 前3個配置文件是struts2框架的默認(rèn)配置文件,基本不用修改。
     * 后3個配置文件可以允許自己修改struts2的常量。但是有一個特點:后加載的配置文件修改的常量的值,會覆蓋掉前面修改的常量的值。

4. 總結(jié)(重點掌握的配置文件)
 * 先加載default.properties文件,在org/apache/struts2/default.properties文件,都是常量。
 * 又加載struts-default.xml配置文件,在核心的jar包最下方,struts2框架的核心功能都是在該配置文件中配置的。
 * 再加載struts.xml的配置文件,在src的目錄下,代表用戶自己配置的配置文件
 * 最后加載web.xml的配置文件

 * 后加載的配置文件會覆蓋掉之前加載的配置文件(在這些配置文件中可以配置常量)

5. 注意一個問題
 * 哪些配置文件中可以配置常量?
     * default.properties        -- 默認(rèn)值,咱們是不能修改的?。?     * struts.xml                -- 可以配置,開發(fā)中基本上都在該配置文件中配置常量
     * struts.properties         -- 可以配置,基本不會在該配置文件中配置
     * web.xml                   -- 可以配置,基本不會在該配置文件中配置

 * 后加載的配置文件會覆蓋掉之前加載的配置??!

***
<br>

##入門總結(jié)之struts.xml配置文件的配置

1. <package>標(biāo)簽,如果要配置<Action>的標(biāo)簽,那么必須要先配置<package>標(biāo)簽,代表的包的概念
 * 包含的屬性
     * name                  -- 包的名稱,要求是唯一的,管理action配置
     * extends               -- 繼承,可以繼承其他的包,只要繼承了,那么該包就包含了其他包的功能,一般都是繼承struts-default
     * namespace             -- 名稱空間,一般與<action>標(biāo)簽中的name屬性共同決定訪問路徑(通俗話:怎么來訪問action),常見的配置如下
         * namespace="/"     -- 根名稱空間
         * namespace="/aaa"  -- 帶有名稱的名稱空間
     * abstract              -- 抽象的。這個屬性基本很少使用,值如果是true,那么編寫的包是被繼承的

2. <action>標(biāo)簽
 * 代表配置action類,包含的屬性
     * name          -- 和<package>標(biāo)簽的namespace屬性一起來決定訪問路徑的
     * class         -- 配置Action類的全路徑(默認(rèn)值是ActionSupport類)
     * method        -- Action類中執(zhí)行的方法,如果不指定,默認(rèn)值是execute

3. <result>標(biāo)簽
 * action類中方法執(zhí)行,返回的結(jié)果跳轉(zhuǎn)的頁面
     * name      -- 結(jié)果頁面邏輯視圖名稱
     * type      -- 結(jié)果類型(默認(rèn)值是轉(zhuǎn)發(fā),也可以設(shè)置其他的值)

***
<br>

##入門總結(jié)之Struts2配置常量

1. 可以在Struts2框架中的哪些配置文件中配置常量?
 * struts.xml(必須要掌握,開發(fā)中基本上就在該配置文件中編寫常量)
     * `<constant name="key" value="value"></constant>`
     ![](media/14899060291645/14899135288275.jpg)

 * web.xml
     * 在StrutsPrepareAndExecuteFilter配置文件中配置初始化參數(shù)
     ![](media/14899060291645/14899134750699.jpg)

 * 注意:后加載的配置的文件的常量會覆蓋之前加載的常量??!

2. 需要大家了解的常量
 * struts.i18n.encoding=UTF-8            -- 指定默認(rèn)編碼集,作用于HttpServletRequest的setCharacterEncoding方法 
 * struts.action.extension=action,,      -- 該屬性指定需要Struts 2處理的請求后綴,該屬性的默認(rèn)值是action,即所有匹配*.action的請求都由Struts2處理。如果用戶需要指定多個請求后綴,則多個后綴之間以英文逗號(,)隔開
 * struts.serve.static.browserCache=true     -- 設(shè)置瀏覽器是否緩存靜態(tài)內(nèi)容,默認(rèn)值為true(生產(chǎn)環(huán)境下使用),開發(fā)階段最好關(guān)閉 
 * struts.configuration.xml.reload=false     -- 當(dāng)struts的配置文件修改后,系統(tǒng)是否自動重新加載該文件,默認(rèn)值為false(生產(chǎn)環(huán)境下使用) 
 * struts.devMode = false                    -- 開發(fā)模式下使用,這樣可以打印出更詳細(xì)的錯誤信息 

***
<br>

##入門總結(jié)之指定多個struts的配置文件(了解)

1. 在大部分應(yīng)用里,隨著應(yīng)用規(guī)模的增加,系統(tǒng)中Action的數(shù)量也會大量增加,導(dǎo)致struts.xml配置文件變得非常臃腫。
 為了避免struts.xml文件過于龐大、臃腫,提高struts.xml文件的可讀性,我們可以將一個struts.xml配置文件分解成多個配置文件,然后在struts.xml文件中包含其他配置文件。

2. 可以在<package>標(biāo)簽中,使用<include>標(biāo)簽來引入其他的struts_xx.xml的配置文件。例如:

<struts>
    <include file="struts-part1.xml"/>
    <include file="struts-part2.xml"/>
</struts>

3. 注意注意注意(重要的事情說三遍):<include file="cn/itcast/demo2/struts-part1.xml"/>

***
<br>

##Action類的三種寫法

1. 配置文件學(xué)習(xí)完成,下面的重點是Action類的三種寫法
    * Action類就是一個POJO類
        * 什么是POJO類,POJO(Plain Ordinary Java Object)簡單的Java對象.簡單記:沒有繼承某個類,沒有實現(xiàn)接口,就是POJO的類。

        ```
        /**
         * 就是POJO類:沒有任何繼承和實現(xiàn)
         * 
         * @author Administrator
         */
       public class Demo1Action {
       
            /**
             * execute是默認(rèn)方法 return null; 不會進(jìn)行跳轉(zhuǎn)
             * 
             * @return
             */
            public String execute() {
                System.out.println("Demo1Action就是POJO類...");
                return null;
            }
    }
    
        xml配置
        <!-- POJO類的方式 -->
        <action name="demo1Action" class="com.itheima.action1.Demo1Action" />
        ```
        
    * Action類可以實現(xiàn)Action接口
        * Action接口中定義了5個常量,5個常量的值對應(yīng)的是5個邏輯視圖跳轉(zhuǎn)頁面(跳轉(zhuǎn)的頁面還是需要自己來配置),還定義了一個方法,execute方法。
        * 大家需要掌握5個邏輯視圖的常量
            * SUCCESS       -- 成功.
            * INPUT         -- 用于數(shù)據(jù)表單校驗.如果校驗失敗,跳轉(zhuǎn)INPUT視圖.
            * LOGIN         -- 登錄.
            * ERROR         -- 錯誤.
            * NONE          -- 頁面不轉(zhuǎn)向.
        
        ```
        /**
         * 實現(xiàn)Action的接口,Action是框提供的接口
         * 
         * @author Administrator
         */
        public class Demo2Action implements Action {
        
            public String execute() throws Exception {
                System.out.println("Demo2Action實現(xiàn)了Action的接口");
        
                // return "success";
                // return LOGIN;
        
                // 表示頁面不跳轉(zhuǎn)
                return NONE;
            }
    }
    
        xml配置
        <!-- 實現(xiàn)Action接口的方式 -->
        <action name="demo2Action" class="com.itheima.action1.Demo2Action">
            <result name="login">/demo1/suc.jsp</result>
        </action>
        ```

    * Action類可以去繼承ActionSupport類(開發(fā)中這種方式使用最多)
        * 設(shè)置錯誤信息

        ```
        /**
         * 編寫Action類繼承ActionSupport類,ActionSupport類已經(jīng)實現(xiàn)了Action和一些其他接口
         * 
         * @author Administrator
         */
        public class Demo3Action extends ActionSupport {
        
            private static final long serialVersionUID = 2183101963251216722L;
        
            public String execute() throws Exception {
                System.out.println("Demo3Action繼承了ActionSupport類...");
                return NONE;
            }
        }
    
    
        xml 配置
        <!-- 繼承ActionSupport類的方式 -->
        <action name="demo3Action" class="com.itheima.action1.Demo3Action" />
        ```

***
<br>

##Action的訪問

1. 通過<action>標(biāo)簽中的method屬性,訪問到Action中的具體的方法。
    * 傳統(tǒng)的配置方式,配置更清晰更好理解!但是擴展需要修改配置文件等!
    * 具體的實例如下:
        * 頁面代碼
            * `<a href="${pageContext.request.contextPath}/addBook.action">添加圖書</a>`
            * `<a href="${pageContext.request.contextPath}/deleteBook.action">刪除圖書</a>`

        * 配置文件的代碼
            
           ```
           <package name="demo2" extends="struts-default" namespace="/">
                <action name="addBook" class="cn.itcast.demo2.BookAction" method="add"></action>
                <action name="deleteBook" class="cn.itcast.demo2.BookAction" method="delete"></action>
            </package>
           ```

        * Action的代碼
        
            ```
           public String add(){
               System.out.println("添加圖書");
               return NONE;
           }
           public String delete(){
               System.out.println("刪除圖書");
               return NONE;
           }
            ```

2. 通配符的訪問方式:(訪問的路徑和方法的名稱必須要有某種聯(lián)系.)  通配符就是 * 代表任意的字符
    * 使用通配符的方式可以簡化配置文件的代碼編寫,而且擴展和維護(hù)比較容易。
    * 具體實例如下:
        * 頁面代碼
            
            ```
            <a href="${pageContext.request.contextPath}/order_add.action">添加訂單</a>
            <a href="${pageContext.request.contextPath}/order_delete.action">刪除訂單</a>
            ```

        * 配置文件代碼
            * `<action name="order_*" class="cn.itcast.demo2.OrderAction" method="{1}"></action>`
            
            **{1} 表示 * 的取值**

        * Action的代碼
            
            ```
            public String add(){
                System.out.println("添加訂單");
                return NONE;
            }
            public String delete(){
                System.out.println("刪除訂單");
                return NONE;
            }
            ```

    * 具體理解:在JSP頁面發(fā)送請求,http://localhost/struts2_01/order_add.action,配置文件中的order_*可以匹配該請求,*就相當(dāng)于變成了add,method屬性的值使用{1}來代替,{1}就表示的是第一個*號的位置??!所以method的值就等于了add,那么就找到Action類中的add方法,那么add方法就執(zhí)行了!

3. 動態(tài)方法訪問的方式(有的開發(fā)中也會使用這種方式)
    * 如果想完成動態(tài)方法訪問的方式,需要開啟一個常量,`struts.enable.DynamicMethodInvocation = false`,把值設(shè)置成`true`。
        * 注意:不同的Struts2框架的版本,該常量的值不一定是true或者false,需要自己來看一下。如果是false,需要自己開啟。
        * 在struts.xml中開啟該常量。
            * `<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>`

    * 具體代碼如下
        * 頁面的代碼
            
        ```
        <a href="${pageContext.request.contextPath}/product!add.action">添加商品</a>
    <a href="${pageContext.request.contextPath}/product!delete.action">刪除商品</a>
        ```
路徑的寫法:`[packName]![methodName]`

        * 配置文件代碼
            * `<action name="product" class="cn.itcast.demo2.ProductAction"></action>`

        * Action的類的代碼
            
           ```
           public class ProductAction extends ActionSupport{
                    public String add(){
                        System.out.println("添加訂單");
                        return NONE;
                    }
                    public String delete(){
                        System.out.println("刪除訂單");
                        return NONE;
                    }
           }
           ```

***
<br>

##在Struts2框架中使用Servlet的API

1. 在Action類中也可以獲取到Servlet一些常用的API
    * 需求:提供JSP的表單頁面的數(shù)據(jù),在Action中使用Servlet的API接收到,然后保存到三個域?qū)ο笾?,最后再顯示到JSP的頁面上。
        * 提供JSP注冊的頁面,演示下面這三種方式
        
        ```
            <h3>注冊頁面</h3>
            <form action="${ pageContext.request.contextPath }/xxx.action" method="post">
                姓名:<input type="text" name="username" /><br/>
                密碼:<input type="password" name="password" /><br/>
                <input type="submit" value="注冊" />
            </form>
        ```

2. 完全解耦合的方式
    * 如果使用該種方式,Struts2框架中提供了一個類,ActionContext類,該類中提供一些方法,通過方法獲取Servlet的API
    * 一些常用的方法如下
        * `static ActionContext getContext()`                                         -- 獲取ActionContext對象實例
        * `java.util.Map<java.lang.String,java.lang.Object> getParameters()`          -- 獲取請求參數(shù),相當(dāng)于`request.getParameterMap()`;
        * `java.util.Map<java.lang.String,java.lang.Object> getSession()`             -- 獲取的代表session域的Map集合,就相當(dāng)于操作session域。
        * `java.util.Map<java.lang.String,java.lang.Object> getApplication()`         -- 獲取代表application域的Map集合
        * `void put(java.lang.String key, java.lang.Object value) `                   -- 注意:向request域中存入值。

    ```
    /**
     * 完全解耦合的方式,使用Servlet的API
     * @author Administrator
     */
    public class Demo1Action extends ActionSupport{
    
        private static final long serialVersionUID = -7255855724015241518L;
        
        public String execute() throws Exception {
            // 完全接耦合的方式
            ActionContext context = ActionContext.getContext();
            // 獲取到請求的參數(shù),封裝所有請求的參數(shù)
            Map<String, Object> map = context.getParameters();
            // 遍歷獲取數(shù)據(jù)
            Set<String> keys = map.keySet();
            for (String key : keys) {
                // 通過key,來獲取到值
                String [] vals = (String[]) map.get(key);
                System.out.println(key+" : "+Arrays.toString(vals));
            }
            
            // 如果向request對象中存入值
            context.put("msg", "小東東");
            // 獲取其他map集合
            context.getSession().put("msg", "小蒼");
            context.getApplication().put("msg", "小澤");
            
            return SUCCESS;
        }
    }
    
    
    jsp:
    
    <h3>使用EL表達(dá)式獲取值</h3>
    ${ sessionScope.msg }
    ${ requestScope.msg }
    ${ applicationScope.msg }
    ```


3. 使用原生Servlet的API的方式
    * Struts2框架提供了一個類,`ServletActionContext`,該類中提供了一些靜態(tài)的方法
    * 具體的方法如下
        * `getPageContext()`
        * `getRequest()`
        * `getResponse()`
        * `getServletContext()`

/**

  • 原生的API
  • @author Administrator

*/
public class Demo2Action extends ActionSupport{

private static final long serialVersionUID = -864657857993072618L;

public String execute() throws Exception {
    // 獲取到request對象
    HttpServletRequest request = ServletActionContext.getRequest();
    request.setAttribute("msg", "小東東");
    request.getSession().setAttribute("msg", "美美");
    ServletActionContext.getServletContext().setAttribute("msg", "小鳳");
    
    HttpServletResponse response = ServletActionContext.getResponse();
    // 使用輸出流,輸出內(nèi)容
    
    return SUCCESS;
}

}

jsp:

<h3>使用EL表達(dá)式獲取值</h3>
${ sessionScope.msg }
${ requestScope.msg }
${ applicationScope.msg }

***
<br>

##結(jié)果頁面的跳轉(zhuǎn)

1. 結(jié)果頁面存在兩種方式
    * 全局結(jié)果頁面
        > 條件:如果<package>包中的一些action都返回success,并且返回的頁面都是同一個JSP頁面,這樣就可以配置全局的結(jié)果頁面。
        > 全局結(jié)果頁面針對的當(dāng)前的包中的所有的Action,但是如果局部還有結(jié)果頁面,會優(yōu)先局部的。使用的標(biāo)簽是
    
        ```
         <global-results>
            <result>/demo3/suc.jsp</result>
         </global-results>
        ```

    * 局部結(jié)果頁面
        
        ```
        <result>/demo3/suc.jsp</result>
        ```

2. 結(jié)果頁面的類型
    * 結(jié)果頁面使用<result>標(biāo)簽進(jìn)行配置,包含兩個屬性
        * name  -- 邏輯視圖的名稱
        * type  -- 跳轉(zhuǎn)的類型,值一些,需要掌握一些常用的類型。
        
    常見的結(jié)果類型去struts-default.xml中查找。
            
       * `dispatcher`        -- 轉(zhuǎn)發(fā).type的**默認(rèn)值**。Action--->JSP
       * `redirect`          -- 重定向。 Action--->JSP
       * `chain`             -- 多個action之間跳轉(zhuǎn).從一個Action轉(zhuǎn)發(fā)到另一個Action。  Action---Action
       * `redirectAction`    -- 多個action之間跳轉(zhuǎn)。從一個Action重定向到另一個Action. Action---Action
       * `stream`            -- 文件下載時候使用的

代碼演示:

    ```
    public class Demo3Action extends ActionSupport{
    
        private static final long serialVersionUID = 7455069037223451306L;
        
        public String save(){
            System.out.println("save...");
            return SUCCESS;
        }
        
        // 訪問: demo3Action_update
        public String update(){
            System.out.println("update...");
            return NONE;
        }
    }
    
    
    struts.xml
    <!-- 演示重定向到Action -->
    <action name="demo3Action_*" class="com.itheima.demo1.Demo3Action" method="{1}">
        <result name="success" type="redirectAction">demo3Action_update</result>
    </action>
    
    ```
這里的`<result name="success" type="redirectAction">demo3Action_update</result>`,重定向路徑名稱直接是:demo3Action_update,不用寫其它路徑信息。


![](media/14899060291645/14899906246113.jpg)

##Struts2框架的數(shù)據(jù)封裝

1. 為什么要使用數(shù)據(jù)的封裝呢?
    * 作為MVC框架,必須要負(fù)責(zé)解析HTTP請求參數(shù),并將其封裝到Model對象中
    * 封裝數(shù)據(jù)為開發(fā)提供了很多方便
    * Struts2框架提供了很強大的數(shù)據(jù)封裝的功能,不再需要使用Servlet的API完成手動封裝了!!

2. Struts2中提供了兩類數(shù)據(jù)封裝的方式?
    * 第一種方式:屬性驅(qū)動
        > 提供對應(yīng)屬性的set方法進(jìn)行數(shù)據(jù)的封裝。
            * 表單的哪些屬性需要封裝數(shù)據(jù),那么在對應(yīng)的Action類中提供該屬性的set方法即可。
            * 表單中的數(shù)據(jù)提交,最終找到Action類中的setXxx的方法,最后賦值給全局變量。

       * 注意0:Struts2的框架采用的攔截器完成數(shù)據(jù)的封裝。
       * 注意1:這種方式不是特別好:因為屬性特別多,提供特別多的set方法,而且還需要手動將數(shù)據(jù)存入到對象中.
       * 注意2:這種情況下,Action類就相當(dāng)于一個JavaBean,就沒有體現(xiàn)出MVC的思想,Action類又封裝數(shù)據(jù),又接收請求處理,耦合性較高。

        ```
        /**
         * 屬性驅(qū)動
         * @author Administrator
         */
        public class Regist1Action extends ActionSupport{
            
            private static final long serialVersionUID = -966487869258031548L;
            
            private String username;
            private String password;
            private Integer age;
            public void setUsername(String username) {
                this.username = username;
            }
            public void setPassword(String password) {
                this.password = password;
            }
            public void setAge(Integer age) {
                this.age = age;
            }
            
            public String execute() throws Exception {
                System.out.println(username+" "+password+" "+age);
                return NONE;
            }
        }
        
        
        jsp:
        <h3>屬性驅(qū)動的方式</h3>
        <form action="${ pageContext.request.contextPath }/regist1.action" method="post">
            姓名:<input type="text" name="username" /><br/>
            密碼:<input type="password" name="password" /><br/>
            年齡:<input type="password" name="age" /><br/>
            <input type="submit" value="注冊" />
        </form>
        ```

        **在頁面上,使用OGNL表達(dá)式進(jìn)行數(shù)據(jù)封裝。**
        
       * 在頁面中使用OGNL表達(dá)式進(jìn)行數(shù)據(jù)的封裝,就可以直接把屬性封裝到某一個JavaBean的對象中。
       * 在頁面中定義一個JavaBean,并且提供set方法:例如:private User user;
       * 頁面中的編寫發(fā)生了變化,需要使用OGNL的方式,表單中的寫法:<input type="text" name="user.username">

       * 注意:只提供一個set方法還不夠,必須還需要提供user屬性的get和set方法?。?!
           > 先調(diào)用get方法,判斷一下是否有user對象的實例對象,如果沒有,調(diào)用set方法把攔截器創(chuàng)建的對象注入進(jìn)來, 
           
        ```
        
        /**
         * 屬性驅(qū)動方式,把數(shù)據(jù)封裝到JavaBean的對象中
         * @author Administrator
         */
        public class Regist2Action extends ActionSupport{
            
            private static final long serialVersionUID = 6556880331550390473L;
            
            // 注意二:屬性驅(qū)動的方式,現(xiàn)在,要提供是get和set方法
            private User user;
            public User getUser() {
                System.out.println("getUser...");
                return user;
            }
            public void setUser(User user) {
                System.out.println("setUser...");
                this.user = user;
            }
            
            public String execute() throws Exception {
                System.out.println(user);
                return NONE;
            }
        
        }
        
        jsp:
        <h3>屬性驅(qū)動的方式(把數(shù)據(jù)封裝到JavaBean的對象中)</h3>
        <!-- 注意一:頁面的編寫規(guī)則,發(fā)生了變化,使用的OGNL表達(dá)式的寫法 -->
        <form action="${ pageContext.request.contextPath }/regist2.action" method="post">
            姓名:<input type="text" name="user.username" /><br/>
            密碼:<input type="password" name="user.password" /><br/>
            年齡:<input type="password" name="user.age" /><br/>
            <input type="submit" value="注冊" />
        </form>
        
        javabean:
        public class User {
    
            private String username;
            private String password;
            private Integer age;
            
            public String getUsername() {
                return username;
            }
            public void setUsername(String username) {
                this.username = username;
            }
            public String getPassword() {
                return password;
            }
            public void setPassword(String password) {
                this.password = password;
            }
            public Integer getAge() {
                return age;
            }
            public void setAge(Integer age) {
                this.age = age;
            }
            @Override
            public String toString() {
                return "User [username=" + username + ", password=" + password + ", age=" + age + "]";
            }
        }
        
        ```

    * 第二種方式:模型驅(qū)動
    
        > 使用模型驅(qū)動的方式,也可以把表單中的數(shù)據(jù)直接封裝到一個JavaBean的對象中,并且表單的寫法和之前的寫法沒有區(qū)別!
        > 編寫的頁面不需要任何變化,正常編寫name屬性的值
        > 模型驅(qū)動的編寫步驟:
            * 手動實例化JavaBean,即:private User user = new User();
            * 必須實現(xiàn)ModelDriven<T>接口,實現(xiàn)getModel()的方法,在getModel()方法中返回user即可??!

        ```
        /**
         * 模型驅(qū)動的方式
         *  實現(xiàn)ModelDriven接口
         *  必須要手動實例化對象(需要自己new好)
         * @author Administrator
         */
        public class Regist3Action extends ActionSupport implements ModelDriven<User>{
            
            private static final long serialVersionUID = 6556880331550390473L;
            
            // 必須要手動實例化
            private User user = new User();
            // 獲取模型對象
            public User getModel() {
                return user;
            }
            
            public String execute() throws Exception {
                System.out.println(user);
                return NONE;
            }
        
        }
    
    
        jsp:
        
        <h3>模型驅(qū)動方式</h3>
        <form action="${ pageContext.request.contextPath }/regist3.action" method="post">
            姓名:<input type="text" name="username" /><br/>
            密碼:<input type="password" name="password" /><br/>
            年齡:<input type="password" name="age" /><br/>
            <input type="submit" value="注冊" />
        </form>
        
        
        ```

***
<br>

##Struts2把數(shù)據(jù)封裝到集合中

1. 封裝復(fù)雜類型的參數(shù)(集合類型 Collection 、Map接口等)
2. 需求:頁面中有可能想批量添加一些數(shù)據(jù),那么現(xiàn)在就可以使用上述的技術(shù)了。把數(shù)據(jù)封裝到集合中
3. 把數(shù)據(jù)封裝到Collection中
    * 因為Collection接口都會有下標(biāo)值,所有頁面的寫法會有一些區(qū)別,注意:
        > <input type="text" name="products[0].name" />
    * 在Action中的寫法,需要提供products的集合,并且提供get和set方法。
    
    ```
    /**
     * 屬性驅(qū)動的方式,把數(shù)據(jù)封裝到List集合中
     * @author Administrator
     */
    public class Regist4Action extends ActionSupport{
        private static final long serialVersionUID = 6556880331550390473L;
        
        private List<User> list;
        public List<User> getList() {
            return list;
        }
        public void setList(List<User> list) {
            this.list = list;
        }
        
        public String execute() throws Exception {
            for (User user : list) {
                System.out.println(user);
            }
            return NONE;
        }
    }

    
    jsp:
    <h3>向List集合封裝數(shù)據(jù)(默認(rèn)情況下,采用是屬性驅(qū)動的方式)</h3>
    <!-- 后臺:List<User> list -->
    <form action="${ pageContext.request.contextPath }/regist4.action" method="post">
        姓名:<input type="text" name="list[0].username" /><br/>
        密碼:<input type="password" name="list[0].password" /><br/>
        年齡:<input type="password" name="list[0].age" /><br/>
        
        姓名:<input type="text" name="list[1].username" /><br/>
        密碼:<input type="password" name="list[1].password" /><br/>
        年齡:<input type="password" name="list[1].age" /><br/>
        <input type="submit" value="注冊" />
    </form>
    ```

4. 把數(shù)據(jù)封裝到Map中
    * Map集合是鍵值對的形式,頁面的寫法
        > <input type="text" name="map['one'].name" />
    * Action中提供map集合,并且提供get和set方法

    ```
    /**
     * 屬性驅(qū)動的方式,把數(shù)據(jù)封裝到map集合中
     * @author Administrator
     */
    public class Regist5Action extends ActionSupport{
        
        private static final long serialVersionUID = 6556880331550390473L;
        
        private Map<String, User> map;
        public Map<String, User> getMap() {
            return map;
        }
        public void setMap(Map<String, User> map) {
            this.map = map;
        }
    
        public String execute() throws Exception {
            System.out.println(map);
            return NONE;
        }
    }

    
    jsp:
    <h3>向Map集合封裝數(shù)據(jù)(默認(rèn)情況下,采用是屬性驅(qū)動的方式)</h3>
    <form action="${ pageContext.request.contextPath }/regist5.action" method="post">
        姓名:<input type="text" name="map['one'].username" /><br/>
        密碼:<input type="password" name="map['one'].password" /><br/>
        年齡:<input type="password" name="map['one'].age" /><br/>
        
        姓名:<input type="text" name="map['two'].username" /><br/>
        密碼:<input type="password" name="map['two'].password" /><br/>
        年齡:<input type="password" name="map['two'].age" /><br/>
        <input type="submit" value="注冊" />
    </form>

    ```

***
<br>

##Struts2的攔截器技術(shù)

1. 攔截器的概述
    * 攔截器就是AOP(Aspect-Oriented Programming)的一種實現(xiàn)。(AOP是指用于在某個方法或字段被訪問之前,進(jìn)行攔截然后在之前或之后加入某些操作。)
    * 過濾器:過濾從客服端發(fā)送到服務(wù)器端請求的

    * 攔截器:攔截對目標(biāo)Action中的某些方法進(jìn)行攔截
        * 攔截器不能攔截JSP
        * 攔截到Action中某些方法

2. 攔截器和過濾器的區(qū)別
    1)攔截器是基于JAVA反射機制的,而過濾器是基于函數(shù)回調(diào)的
    2)過濾器依賴于Servlet容器,而攔截器不依賴于Servlet容器
    3)攔截器只能對Action請求起作用(Action中的方法),而過濾器可以對幾乎所有的請求起作用(CSS JSP JS)

    * 攔截器 采用 責(zé)任鏈 模式
        > 在責(zé)任鏈模式里,很多對象由每一個對象對其下家的引用而連接起來形成一條鏈
        > 責(zé)任鏈每一個節(jié)點,都可以繼續(xù)調(diào)用下一個節(jié)點,也可以阻止流程繼續(xù)執(zhí)行

    * 在struts2 中可以定義很多個攔截器,將多個攔截器按照特定順序 組成攔截器棧 (順序調(diào)用 棧中的每一個攔截器 )

3. Struts2的核心是攔截器,看一下Struts2的運行流程
![](media/14899060291645/14899987602642.jpg)


##自定義攔截器和配置

###方式一:

/**

  • 編寫簡單的攔截器

  • @author Administrator
    */
    public class DemoInterceptor extends AbstractInterceptor{

    private static final long serialVersionUID = 4360482836123790624L;

    /**

    • intercept用來進(jìn)行攔截的
      */
      public String intercept(ActionInvocation invocation) throws Exception {
      System.out.println("Action方法執(zhí)行之前...");
      // 執(zhí)行下一個攔截器
      String result = invocation.invoke();

      System.out.println("Action方法執(zhí)行之后...");

      return result;
      }

}

Struts.xml

<package name="demo3" extends="struts-default" namespace="/">

    <!-- 定義了攔截器 第一種方式 -->
    <interceptors>
        <interceptor name="DemoInterceptor" class="com.itheima.interceptor.DemoInterceptor"/>
    </interceptors>
    
    <action name="userAction" class="com.itheima.demo3.UserAction">
        <!-- 只要是引用自己的攔截器,默認(rèn)棧的攔截器就不執(zhí)行了,必須要手動引入默認(rèn)棧 -->
        <interceptor-ref name="DemoInterceptor"/>
        <interceptor-ref name="defaultStack"/>
    </action>
</package>

Action:

public class UserAction extends ActionSupport{
public String execute() throws Exception {
System.out.println("我是Action,我正常執(zhí)行...");
return NONE;
}
}


###方式二:

攔截器的另一種方式配置

<package name="demo3" extends="struts-default" namespace="/">

    <!-- 第二種方式:定義攔截器棧 -->
    <interceptors>
        <interceptor name="DemoInterceptor" class="com.itheima.interceptor.DemoInterceptor"/>
        <!-- 定義攔截器棧 -->
        <interceptor-stack name="myStack">
            <interceptor-ref name="DemoInterceptor"/>
            <interceptor-ref name="defaultStack"/>
        </interceptor-stack>
    </interceptors>
    
    <action name="userAction" class="com.itheima.demo3.UserAction">
        
        <!-- 引入攔截器棧就OK -->
        <interceptor-ref name="myStack"/>
    </action>

</package>


***
<br>


##OGNL表達(dá)式概述(了解)

1. OGNL是Object Graphic Navigation Language(對象圖導(dǎo)航語言)的縮寫
    * 所謂對象圖,即以任意一個對象為根,通過OGNL可以訪問與這個對象關(guān)聯(lián)的其它對象
    * 通過它簡單一致的表達(dá)式語法,可以存取對象的任意屬性,調(diào)用對象的方法,遍歷整個對象的結(jié)構(gòu)圖,實現(xiàn)字段類型轉(zhuǎn)化等功能。它使用相同的表達(dá)式去存取對象的屬性

2. Struts2框架使用OGNL作為默認(rèn)的表達(dá)式語言
    * OGNL是一種比EL強大很多倍的語言
    * xwork提供 OGNL表達(dá)式
    * ognl-3.0.5.jar

3. OGNL 提供五大類功能
   * 支持對象方法調(diào)用
   * 支持類靜態(tài)的方法調(diào)用和值訪問
   * 訪問OGNL上下文(OGNL context)和ActionContext
   * 支持賦值操作和表達(dá)式串聯(lián)
   * 操作集合對象

4. 測試的代碼
   
    ```
    // 訪問對象的方法
    @Test
    public void run1() throws OgnlException{
        OgnlContext context = new OgnlContext();
        // 獲取對象的方法
        Object obj = Ognl.getValue("'helloworld'.length()", context, context.getRoot());
        System.out.println(obj);
    }

    // 獲取OGNL上下文件的對象
    @Test
    public void run3() throws OgnlException{
        OgnlContext context = new OgnlContext();
        context.put("name", "美美");
        // 獲取對象的方法
        Object obj = Ognl.getValue("#name", context, context.getRoot());
        System.out.println(obj);
    }

    // 從root棧獲取值
    @Test
    public void demo3() throws OgnlException{
        OgnlContext context = new OgnlContext();
        Customer c = new Customer();
        c.setCust_name("haha");
        context.setRoot(c);
        String name = (String) Ognl.getValue("cust_name", context, context.getRoot());
        System.out.println(name);
    }
    ```

***
<br>

##在Struts2框架中使用OGNL表達(dá)式

1. Struts2引入了OGNL表達(dá)式,主要是在JSP頁面中獲取值棧中的值
2. 具體在Struts2中怎么使用呢?如下步驟
    * 需要先引入Struts2的標(biāo)簽庫
        `<%@ taglib prefix="s" uri="/struts-tags" %>`

    * 使用Struts2提供的標(biāo)簽中的標(biāo)簽
        `<s:property value="OGNL表達(dá)式"/>`

3. 在JSP頁面使用OGNL表達(dá)式
    * 訪問對象方法
        `<s:property value="'hello'.length()"/>`


###值棧的概述
1. 問題一:什么是值棧?
    * 值棧就相當(dāng)于Struts2框架的數(shù)據(jù)的中轉(zhuǎn)站,向值棧存入一些數(shù)據(jù)。從值棧中獲取到數(shù)據(jù)。
    * ValueStack 是 struts2 提供一個接口,實現(xiàn)類 OgnlValueStack ---- 值棧對象 (OGNL是從值棧中獲取數(shù)據(jù)的 )
    * Action是多例的,有一起請求,創(chuàng)建Action實例,創(chuàng)建一個ActionContext對象,代表的是Action的上下文對象,還會創(chuàng)建一個ValueStack對象。
    * 每個Action實例都有一個ValueStack對象 (一個請求 對應(yīng) 一個ValueStack對象 )
    * 在其中保存當(dāng)前Action 對象和其他相關(guān)對象
    * Struts 框架把 ValueStack 對象保存在名為 “struts.valueStack” 的請求屬性中,request中 (值棧對象 是 request一個屬性)
        * `ValueStack vs = (ValueStack)request.getAttribute("struts.valueStack");`

###值棧的內(nèi)部結(jié)構(gòu)

2. 問題二 : 值棧的內(nèi)部結(jié)構(gòu) ?
    * 值棧由兩部分組成
        > root      -- Struts把動作和相關(guān)對象壓入 ObjectStack 中--List
        > context   -- Struts把各種各樣的映射關(guān)系(一些 Map 類型的對象) 壓入 ContextMap 中

    * Struts會默認(rèn)把下面這些映射壓入ContextMap(context)中
        * 注意:request代表的是Map集合的key值,value的值其實也是一個Map集合。

        > parameters: 該 Map 中包含當(dāng)前請求的請求參數(shù)  ?name=xxx&password=123
        > request: 該 Map 中包含當(dāng)前 request 對象中的所有屬性
        > session: 該 Map 中包含當(dāng)前 session 對象中的所有屬性
        > application:該 Map 中包含當(dāng)前 application  對象中的所有屬性
        > attr: 該 Map 按如下順序來檢索某個屬性: request, session, application

    * ValueStack中 存在root屬性 (CompoundRoot) 、 context 屬性 (OgnlContext )
        > CompoundRoot 就是ArrayList
        > OgnlContext 就是 Map

    * context 對應(yīng)Map 引入 root對象 
        > context中還存在 request、 session、application、 attr、 parameters 對象引用 
        > OGNL表達(dá)式訪問值棧中的數(shù)據(jù)
            * 訪問root中數(shù)據(jù)時 不需要 #
            * 訪問 request、 session、application、 attr、 parameters 對象數(shù)據(jù) 必須寫 # 

        > 操作值棧 默認(rèn)指 操作 root 元素

![](media/14899060291645/14900765721052.jpg)

***

###值棧的創(chuàng)建和ActionContext對象的關(guān)系

3. 問題三 : 值棧對象的創(chuàng)建,ValueStack 和 ActionContext 是什么關(guān)系?
    * 值棧對象是請求時創(chuàng)建的
    * ActionContext是綁定到當(dāng)前的線程上,那么在每個攔截器或者Action中獲取到的ActionContext是同一個。
    * ActionContext中存在一個Map集合,該Map集合和ValueStack的context是同一個地址。
    * ActionContext中可以獲取到ValueStack的引用,以后再開發(fā),使用ActionContext來獲取到值棧對象
技術(shù)分析之獲取到值棧的對象

4. 問題四 : 如何獲得值棧對象
    * 獲得值棧對象 有三種方法
        * `ValueStack vs1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");`
        * `ValueStack vs2 = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);`
        * `ValueStack vs3 = ActionContext.getContext().getValueStack();`
技術(shù)分析之向值棧中保存數(shù)據(jù)

5. 問題五: 向值棧保存數(shù)據(jù) (主要針對root棧)
    * valueStack.push(Object obj);
        * push方法的底層調(diào)用root對象的push方法(把元素添加到0位置**棧頂**)

    * valueStack.set(String key, Object obj);
        * 源碼獲取map集合(map有可能是已經(jīng)存在的,有可能是新創(chuàng)建的),把map集合push到**棧頂**,再把數(shù)據(jù)存入到map集合中。

        ```
        public String execute() throws Exception {
            // 獲取到值棧對象,先獲取到ActionContext對象
            ValueStack vs = ActionContext.getContext().getValueStack();
            System.out.println(vs);
            // 棧頂壓的小風(fēng) 字符串
            vs.push("小鳳");
            // 棧頂:
            vs.set("msg", "美美");
            vs.set("info", "小蒼");
            return NONE;
        }
        ```

    在jsp中 通過 <s:debug /> 查看值棧的內(nèi)容

    ***
    <br>
    
    ###從值棧中獲取值

6. 問題六: 在JSP中獲取值棧的數(shù)據(jù)
    * 總結(jié)幾個小問題:
        > 訪問root中數(shù)據(jù) 不需要#
        > 訪問context其它對象數(shù)據(jù) 加 #
        > 如果向root中存入對象的話,優(yōu)先使用push方法。
        > 如果向root中存入集合的話,優(yōu)先要使用set方法。

    * 在OgnlContext中獲取數(shù)據(jù)
        > 在Action中向域?qū)ο笾写嫒胫?        > request:<s:property value="#request.username"/>
        > session:<s:property value="#session.username"/>
        > application:<s:property value="#application.username"/>
        > attr:<s:property value="#attr.username"/>
        > parameters:<s:property value="#parameters.cid"/>

    * 6.1 代碼如下
    
    ```
    <!--
        // vs.push("美美");
        // 獲取到棧頂?shù)闹?        <s:property value="[0].top"/>
    -->

    <!--
        // 棧頂是map集合,通過key獲取值
        vs.set("msg", "小鳳");
        <s:property value="[0].top.msg"/>
    -->

    <!--  
        vs.push(user);
        // 棧頂放user對象
        <s:property value="[0].top.username"/>
        <s:property value="[0].top.password"/>
        // [0].top 關(guān)鍵字是可以省略的  findValue()
        <s:property value="username"/>
    -->

    <!--
        vs.set("user", user);
        <s:property value="[0].top.user.username"/>
        <s:property value="[0].top.user.password"/>
        // 省略關(guān)鍵字
        <s:property value="user.username"/>
    -->

    <!--  
        // 在ValueStack1Action提供了成員的屬性
        private User user = new User("小澤","456");
        public User getUser() {
            return user;
        }
        public void setUser(User user) {
            this.user = user;
        }

        User user = new User("小蒼","123");
        vs.set("user", user);
        // 從棧頂開始查找,找user的屬性,顯示名稱    返回的小蒼
        <s:property value="user.username"/>

        // [1].top獲取ValueStack1Action [1].top.user返回user對象  [1].top.user.username獲取對象的屬性名稱
        <s:property value="[1].top.user.username"/>
    -->

    <!--  
        棧頂是list集合
        vs.push(ulist);
        <s:property value="[0].top[0].username"/>
        <s:property value="[0].top[1].username"/>
    -->

    <!--
        vs.set("ulist", ulist);
        <s:property value="ulist[0].username"/>
    -->

    <!-- 迭代的標(biāo)簽 
        屬性
            * value 要迭代的集合,需要從值棧中獲取
            * var   迭代過程中,遍歷的對象
                * var編寫上,把迭代產(chǎn)生的對象默認(rèn)壓入到context棧中,從context棧取值,加#號
                * var不編寫,默認(rèn)把迭代產(chǎn)生的對象壓入到root棧中

        for(User user:ulist){}  
        // 編寫var的屬性
        <s:iterator value="ulist" var="u">
            <s:property value="#u.username"/>
            <s:property value="#u.password"/>
        </s:iterator>

        // 沒有編寫var關(guān)鍵字
        <s:iterator value="ulist">
            <s:property value="username"/>
            <s:property value="password"/>
        </s:iterator>
    -->

    <!-- 從context棧中獲取值,加#號

    HttpServletRequest request = ServletActionContext.getRequest();
    request.setAttribute("msg", "美美");
    request.getSession().setAttribute("msg", "小風(fēng)");

    <s:property value="#request.msg"/>
    <s:property value="#session.msg"/>
    <s:property value="#parameters.id"/>
    <s:property value="#attr.msg"/>
    -->

    <!-- 在JSP頁面上,查看值棧的內(nèi)部結(jié)構(gòu) -->
    <s:debug></s:debug>
    ```

***
<br>

###EL表達(dá)式也會獲取到值棧中的數(shù)據(jù)

7. 問題七:為什么EL也能訪問值棧中的數(shù)據(jù)?

    ```
    <!--
        在JSP頁面上可以使用EL和JSTL標(biāo)簽庫來取值
        使用裝飾者模式,連接池 全站編碼
        getAttribute()增強了
    -->
    <c:forEach items="${ ulist }" var="user">
        ${ user.username } -- ${ user.password }
    </c:forEach>

    ```

    * StrutsPreparedAndExecuteFilter的doFilter代碼中 request = prepare.wrapRequest(request);    
        > 對Request對象進(jìn)行了包裝 ,StrutsRequestWrapper
        > 增強了request的 getAttribute
            
       ```
       Object attribute = super.getAttribute(s);
       if (attribute == null) {
          attribute = stack.findValue(s);
       }
       ```
       
      > 訪問request范圍的數(shù)據(jù)時,如果數(shù)據(jù)找不到,去值棧中找 
      > request對象 具備訪問值棧數(shù)據(jù)的能力 (查找root的數(shù)據(jù))



最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • action中如何接受頁面?zhèn)鬟^來的參數(shù) 第一種情況:(同名參數(shù)) 例如:通過頁面要把id=1 name=tom a...
    清楓_小天閱讀 3,292評論 1 22
  • 標(biāo)簽 如果要配置的標(biāo)簽,那么必須要先配置標(biāo)簽,代表的包的概念。 包含的屬性 name包的名稱,要求是唯一的,管理a...
    偷偷得路過閱讀 1,518評論 0 0
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,834評論 18 399
  • 今天有四節(jié)空課,本來都安排好了看書和運動,結(jié)果都被擠壓了,也不清楚這一天具體干了些什么。好像一直在和別人聊天。有點...
    顧鳴芬閱讀 292評論 0 0
  • 欲要批評他人時,就一定要有一個前提:對方對自己的親近感。沒有這個前提,直接批導(dǎo)致的唯一結(jié)果就是:將對方越...
    相逢一笑307閱讀 572評論 0 2

友情鏈接更多精彩內(nèi)容