1. 程式人生 > >Struts2核心技術 (一)

Struts2核心技術 (一)

struts2

struts2發展歷史


     經過很多年發展,Struts1已經成為了高度成熟的框架,但隨著時間的發展,Struts1的侷限性和缺點不斷的暴露出來。
     現在Struts已經分化成了兩個框架

     -第一個是在Struts1的基礎上,融合了另一個web框架Webwork的Struts2.Struts2實質上是以Webwork為核心的,和Struts1有很大區別。
     -第二個是Shale,與原有Struts1關聯很少,使用了全新的設計思想。

MVC思想概述

     java web動態程式設計技術,經歷了Model和Model2時代。

     Model1時代:整個Web應用幾乎全部由jsp頁面組成,jsp頁面接收處理客戶端請求,對請求處理後直接做出響應,用少量的JavaBean來處理資料庫連線訪問等操作。Model1的侷限性非常明顯,jsp頁面同時擔任View和Controller兩種角色,將頁面表現和邏輯處理混雜在一起,程式碼重用性極低,增加了擴充套件和維護難度。

    Model2時代:已經採用了MVC的設計。在Model 2架構中,Servlet作為Controller,負責接收使用者請求,只包含控制邏輯,然後呼叫後端來進行具體的邏輯處理。最後轉發到相應的jsp頁面負責顯示。

    MVC由Model(模型),View(檢視),Controller(控制器)組成。

    javaWeb中的三層架構
         表現層:MVC,struts2框架其實是對錶現層的MVC作了優化
         業務層:service
         持久層:dao

Struts2環境的搭建

(1)下載Struts2

    目錄結構
            apps:該資料夾下包含了基於Struts2的示例應用
            docs:包含了Struts2的相關文件 
            lib:包含了Struts2的核心類庫,以及Struts2的第三方類庫
            src:包含了Struts框架的所有原始碼  

(2)建立web專案,匯入struts2所需jar包,lib中有struts2的所有jar包,但是我們不需要那麼多。

我們只把必需的新增到專案即可。將apps目錄下struts2-blank.war壓縮包中的lib目錄下的jar包新增到我們專案中即可。這是struts2必需的jar包。



Struts2架構

struts使用攔截器作為增強處理,以使用者的邏輯控制器為目標,建立一個控制器代理,控制器代理回撥業務控制器的execute方法處理使用者請求,該方法的返回值決定struts2將怎樣的檢視資源呈現給使用者

struts2大致處理流程:

(1)瀏覽器傳送請求
(2)核心控制器根據請求決定是否呼叫相應的Action
(3)struts2內建的攔截器鏈會自動對請求進行一些操作
(4)回撥Action的execute方法,執行操作。
(5)Action會將處理結果存入stack Context中,並返回字串,核心控制器根據字串跳轉到指定的檢視資源。
(6)檢視資源會讀取Stack Context中的資訊,向瀏覽器生成響應資料。

Struts2入門案例

只需跟著做即可,先不必瞭解為何這樣做。

(1)編輯web應用的web.xml配置檔案,配置struts2的核心攔截器。



<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> 

(2)從使用者請求開始,我們實現一個登陸表單

    這裡的action屬性比較特殊,不是普通的servlet。
    當表單提交給login時,struts的攔截器會起作用,呼叫使用者開發的Action處理使用者請求 

        <body>
            <form action="login" method="post">
                使用者名稱:<input type="text" name="username"><br>
                密   碼:<input type="password" name="password"><br>
                <input type="submit" value="登入">
            </form>
        </body> 

(3)實現控制器Action類處理使用者請求

我們已經指出,MVC框架的核心就是控制器,控制器處理具體使用者請求。

    這個類實現Action介面,並實現介面的execute方法。  

    該類包含的多個屬性用於封裝使用者的請求引數。 我們可能現在很難理解,我們請求的引數是怎麼賦值給這個類的。
    我們說過,在呼叫Action方法之前,struts2的內建攔截器會自動負責解析使用者請求引數,並賦值給Action相應的引數

    public class LoginAction extends Action{
                private String username;   //使用者名稱
                private String password;   //密碼
                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 String execute(){
                    if(getUsername().equals("cad")&&getPassword().equals("123456")){
                        return "success";
                    }else
                    {
                        return "error";
                    }
                }

            } 

(4)配置Action

我們使用者傳送login請求,那麼如果根據login請求去呼叫相應的Action實現類呢?這就需要我們去配置Action。

配置struts.xml ,struts.xml應該放在src的classes路徑下   

        <?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="demo" extends="struts-default"> //包  name隨便取,extends照寫就ok 

                <action name="login" class="com.cad.struts2.LoginAction"> //action name為login,即為負責處理login的請求,action預設呼叫自身的execute方法處理請求
                    <result name="success">/welcome.jsp</result>  //根據返回的字串轉發到相應頁面
                    <result name="error">/error.jsp</result> 

                </action>
            </package>
        </struts>    

Struts2兩個重要組成部分

struts2核心就是核心控制器和業務控制器。

核心控制器StrutsPrepareAndExecuteFilter

StrutsPrepareAndExecuteFilter作為一個filter執行在web應用中,負責攔截所有使用者請求,該filter 會過濾使用者請求,然 

後將請求都交給struts2框架處理。攔截器會預設攔截副檔名為.action的請求,什麼字尾名都不寫也可以。例如  

hello.action或者hello都會進行攔截。xxx.jsp就不會進行攔截,直接放行。 

(1)啟動Tomcat,tomcat載入web.xml檔案。同時載入和初始化核心過濾器,檢視struts2原始碼,發現核心過濾器的初始化方法中會載入struts.xml等struts2配置檔案。

(2)當用戶請求到達時,核心過濾器攔截使用者請求,將請求交給struts2框架來處理,即去匹配struts.xml的內容。
struts2框架獲取使用者請求後,根據請求的名字來決定呼叫哪個業務邏輯元件,例項化相應的類。例如,對於login請求,呼叫名為login的Action處理。

(3)struts2的所有Action都被定義在struts.xml檔案中,Action有name和class屬性,name決定了該Action處理哪個使用者請求,class決定了該Action的實現類。

(4)Struts2使用者處理使用者請求的Action例項,並不是使用者實現的業務控制器,而是Action代理,他會回撥使用者的處理方法,因為使用者實現的業務控制器沒有與Servlet API有任何關係,所以根本沒辦法進行獲取引數等請求處理,而struts2定義了一系列攔截器,會對請求引數解析,傳入到Action中,回撥execute方法。

我們每次請求都會例項化相應的類,所以不會出現執行緒不安全的情況。而Servlet為單例,會出現執行緒不安全。

業務控制器:

業務控制器就是使用者實現的Action類,Action類中通常包含一個execute方法,該方法返回一個字串,字串與struts.xml中的result的name相對應,跳轉到不同頁面。  

struts2內部執行流程

這裡寫圖片描述

(1)客戶端發出HTTP請求

(2)然後請求被核心過濾器StrutsPrepareAndExecuteFilter攔截

(3)核心過濾器將請求轉發到Action對映器,Action對映器負責識別當前的請求是否需要交由Struts2處理。

(4)Action對映器返回需要struts2處理的資訊,StrutsPrepareAndExecuteFilter會建立一個Action代理

(5)Action代理並不知道執行哪一個Action,它會向配置管理器詢問呼叫哪一個Action,配置管理器會從struts.xml讀取我們配置的Action資訊。

(6)Action代理建立相關請求的Action物件,呼叫相關的方法之前,struts2的一系列攔截器會幫我們做一些操作,例如獲取請求引數等。

(7)然後呼叫execute方法根據返回的字串去匹配相應的頁面,

(8)頁面可以獲取一些頁面模板,然後生成最終頁面,再倒序的執行攔截器的後續操作

(9)最後生成HTTP響應內容

Struts2配置檔案

Struts2核心配置檔案是struts.xml,該檔案主要負責管理業務控制器Action。 

    struts2有很多配置檔案,按照以下順序載入配置  
    (在伺服器開啟時,載入web.xml檔案,然後初始化核心過濾器,核心過濾器的init初始化方法中,
    提供了載入配置檔案的方法。所以伺服器一啟動,這些配置檔案就已經載入到記憶體中了。 )

        default.properties:該檔案儲存在struts2-core-2.3.32.jar中的org.apache.struts2包裡面  
                            裡面儲存一些常量。 

        struts-default.xml :該檔案儲存在struts2-core-2.3.32.jar中 
                            定義了一些struts2的基礎Bean和struts2內建支援的結果型別,還有struts2內建的攔截器(攔截器有很多,分為一系列的攔截器塊,我們預設使用defaultStack攔截器塊) 

        struts-plugin.xml :該檔案儲存在struts-xxx-2.3.32.jar等struts2外掛jar包中 
                            我們整合spring等框架時,都需要這種外掛的jar包。 

        struts.xml:是web應用預設的自己的struts2配置檔案 

        struts.properties :是struts2的預設配置檔案 

        web.xml:web應用的配置檔案 

        如果多個檔案配置了同一個struts2常量,則後一個檔案中配置的常量值會覆蓋前面檔案中配置的常量值。  

配置檔案中常用的常量

            struts.i18n.encoding:該常量指定struts2應用預設使用的字符集。 

            struts.objectFactory.spring.autoWire:和spring框架整合有關。 

            struts.multipart.parser:指定檔案上傳用的元件。預設為jakarta(即common-fileupload上傳元件) 

            struts.multipart.saveDir:指定上傳檔案的臨時儲存路徑  

            struts.multipart.maxSize:指定上傳中整個請求所允許上傳的最大位元組數。 

            struts.action.extension:指定struts2需要處理的請求字尾。預設值為.action或者什麼都不寫 

            struts.serve.static.browserCache:設定瀏覽器是否快取靜態內容,當應用處於開發階段時,我們希望不快取,可設定為false  

            struts.enable.DynamicMethodInvocation:設定struts2是否支援動態方法呼叫,預設值為true 

            struts.devMode:設定struts2是否使用開發模式。如果設定為true,為開發模式,修改struts.xml配置檔案不需要重啟伺服器,會顯示更多更友好的錯誤提示。 

            struts.ui.theme:指定檢視主題。 

            struts.url.includeParams:指定struts2生成url時是否包含請求引數。有none,get和all三個值。分別對應不包含,僅包含get型別請求引數和包含全部請求引數。 

struts.xml檔案中配置和修改常量

在struts.xml檔案中配置,使用<constant .../>配置常量。
            name:常量名
            value:常量值
            <constant name="" value=""/>  
    例子:我們修改請求字尾為.abc 

                在struts.xml檔案中配置 
                    <package name="demo" extends="struts-default">
                        <action name="hello" class="com.cad.struts2.Hello" method="sayHello">
                            <result name="success">/welcome.jsp</result>
                            <result name="error">/error.jsp</result>
                        </action>
                    </package>  
                    //修改請求字尾為abc
                    <constant name="struts.action.extension" value="abc"></constant> 


                編寫jsp頁面 
                <body>
                    //使用預設請求字尾
                    <a href="${pageContext.request.contextPath }/hello.action">字尾為.action</a>
                    <a href="${pageContext.request.contextPath }/hello">沒有後綴</a>
                    <a href="${pageContext.request.contextPath }/hello.abc">字尾為.abc</a>
                </body>

                結果為前兩個都出現404 
                最後一個超連結訪問成功 

在web.xml檔案中配置常量

                //修改請求字尾為do
                <init-param>
                    <param-name>struts.action.extension</param-name>
                    <param-value>do</param-value>
                </init-param>  

                jsp頁面 
                    <body>
                        <a href="${pageContext.request.contextPath }/hello.action">字尾為.action</a>
                        <a href="${pageContext.request.contextPath }/hello">沒有後綴</a>
                        <a href="${pageContext.request.contextPath }/hello.abc">字尾為.abc</a>
                        <a href="${pageContext.request.contextPath }/hello.do">字尾為.do</a>
                    </body> 

                前三個都請求失敗,最後.do結尾的請求成功。這也驗證了我們說的配置檔案的載入順序。 

深入struts2配置檔案

Bean配置

struts2是一個高度可擴充套件的框架。框架的大部分核心元件,並不是硬編碼寫在程式碼中,而是以自己的IOC容器管理框架的核心元件。 

struts2以可配置的方式來管理核心元件,從而允許開發者很方便的擴充套件該框架的核心元件,當開發者需要擴充套件,替換核心元件時,只需要提供自己元件的實現類,將其部署在struts2的IoC容器中即可。 

struts-default.xml檔案中配置了大量的struts2框架的內建Bean。

    我們在struts.xml中定義Bean時,通常有兩個作用
            -建立該Bean的例項,將該例項作為struts2框架的核心元件使用。

            -Bean包含的靜態方法需要注入一個值。可以很方便地允許不建立某個類的例項,卻可以接受框架常量。

            這個部分只需要瞭解即可,百分之九十九的struts2應用都不用我們去定義核心元件和去配置Bean。 

            使用<bean />元素在struts.xml定義Bean
            屬性:
                class:必填屬性。指定了Bean例項的實現類
                type:可選屬性,制定了Bean例項實現的struts2規範,該規範通過某個介面實現。
                name:可選屬性。Bean例項的名字
                scope:可選屬性。指定Bean例項的作用域
                static:可選屬性。指定Bean是否使用靜態方法注入。
                optional:可選屬性。指定該Bean是否是一個可選Bean。

                例如 
                使用自定義的ObjectFactory,實現了ObjectFactory介面。實現類是MyObjectFactory
                <bean type="com.opensymphony.xwork2.ObjectFactory" name="myfactory" class="com.my.MyObjectFactory"/>  

瞭解即可,不需要深入。

package包配置

    struts2框架的核心元件就是Action,攔截器等。struts2使用包來管理Action,攔截器等。 

            屬性
            name:配置包時,必須指定name屬性,是包的唯一標識。  

            extends:屬性值必須是另一個包的name屬性。指定extends屬性表示繼承其他包。子包可以整合一個或多個父包中的攔截器,攔截器棧,action等配置。 

            例如我們前面專案中定義的 <package name="demo" extends="struts-default"> ,就繼承struts-default.xml中的struts-default包。 

            abstract:可選屬性。指定該包是否是抽象包,抽象包不能包含Action定義。

            namespace:該屬性是一個可選屬性,定義該包的名稱空間。一個web應用中可能出現同名Action。同一個名稱空間中不能有同名Action 

            某個包指定名稱空間後,該包下的所有action的url地址必須是 名稱空間+action 

            例如我們加一個名稱空間,則訪問這個動作的時候必須加上名稱空間。例如 http://localhost:8080/Struts2Demo/user/hello.action 

            <package name="demo" extends="struts-default" namespace="/user">
                <action name="hello" class="com.cad.struts2.Hello" method="sayHello">
                    <result name="success">/welcome.jsp</result>
                    <result name="error">/error.jsp</result>
                </action>
            </package>  

            如果包沒有指定名稱空間,則預設的名稱空間為""  
            根名稱空間為"/" 

包的執行順序

        (1)搜尋配置檔案所有package的namespace 

        (2)先去匹配名稱空間/user/my,如果匹配到,就查詢Action,查詢到就執行,沒查詢到會到預設名稱空間查詢Action,查詢到執行,沒找到報錯。 

        (3)如果沒匹配到該名稱空間,就接著匹配名稱空間/user,如果匹配到,就查詢Action,查詢到就執行,沒查詢到會到預設名稱空間查詢Action,查詢到執行,沒找到報錯。 

        (4)沒匹配到就去根名稱空間(”/“)查詢action,沒查詢到會到預設名稱空間查詢Action,查詢到執行,沒找到報錯。 

        (5)沒匹配到任何名稱空間直接報錯。 

Struts2的Action

開發者需要提供大量的Action,並在struts.xml中配置Action.Action類裡包含了對使用者請求的處理邏輯,因為我們也稱Action為業務控制器。

編寫Action處理類

第一種建立處理類方法

    struts2採用了低侵入式的設計,struts2不要求Action類繼承任何的struts基類,也不需要實現任何介面。Action類只是一個普通的POJO(Plain Ordinary Java Object簡單的java物件) 

第二種:建立一個類實現Action介面,該介面定義了五個字串常量。還包含一個String execute()方法

            public interface Action{
                五個字串常量
                public static final String  ERROR="errror";
                public static final String  INPUT="input";
                public static final String  LOGIN="login";
                public static final String  NONE="none";
                public static final String  SUCCESS="success"; 
                //處理使用者請求的execute方法
                    public String execute()throws Exception;
            }    

第三種:繼承Action介面的實現類ActionSupport,該類提供了很多的預設方法,包括獲取國際化資訊,資料校驗的方法等。大大簡化了Action的開發。我們開發中選擇第三種

配置Action

        在struts.xml檔案中配置。struts2使用包來組織action。所以action定義放在包定義的下面 。

            <action.../>
            屬性
                name:action的名字
                class:指定該action的實現類,class屬性並不是必須的,如果我們不指定class屬性,系統預設使用ActionSupport類 


        配置Action的預設處理類
            如果我們不指定<action>中的class屬性,預設使用ActionSupport類。
            我們可以使用<default-class-ref class=""></default-class-ref>來指定預設的動作處理類。  

Action的方法呼叫

我們繼承的ActionSupport,當我們執行Action的時候,預設執行他的execute方法,現在我們來執行自己的方法。< action >中有一個method屬性,可以指定使用者呼叫哪個方法。

例子:
            我們寫一個Action類,類裡有四個方法。
                    public class Hello extends ActionSupport{
                        public String addUser(){
                            System.out.println("新增使用者");
                            return SUCCESS;
                        }
                        public String updateUser(){
                            System.out.println("修改使用者");
                            return SUCCESS;
                        }

                        public String selectUser(){
                            System.out.println("查詢使用者");
                            return SUCCESS;
                        }

                        public String deleteUser(){
                            System.out.println("刪除使用者");
                            return SUCCESS;
                        }
                    } 
我們在struts.xml中配置我們的action 

        <package name="demo" extends="struts-default">

                <action name="addUser" class="com.cad.struts2.Hello" method="addUser">
                            <result name="success">/welcome.jsp</result>
                            <result name="error">/error.jsp</result>
                </action> 

                <action name="updateUser" class="com.cad.struts2.Hello" method="updateUser">
                            <result name="success">/welcome.jsp</result>
                            <result name="error">/error.jsp</result>
                </action> 

                <action name="selectUser" class="com.cad.struts2.Hello" method="selectUser">
                            <result name="success">/welcome.jsp</result>
                            <result name="error">/error.jsp</result>
                </action> 

                <action name="deleteUser" class="com.cad.struts2.Hello" method="deleteUser">
                            <result name="success">/welcome.jsp</result>
                            <result name="error">/error.jsp</result>
                </action>

        </package> 
在jsp頁面中請求action
                    <body>
                        <a href="${pageContext.request.contextPath }/addUser">新增使用者</a>
                        <a href="${pageContext.request.contextPath }/updateUser">修改使用者</a>
                        <a href="${pageContext.request.contextPath }/selectUser">檢視使用者</a>
                        <a href="${pageContext.request.contextPath }/deleteUser">刪除使用者</a>
                    </body> 

我們發現這種方式寫的很多程式碼類似,相當冗餘,為了解決這個問題,struts2提供了萬用字元的配置方式幫我們解決這個問題。

使用萬用字元

    我們在struts.xml檔案中配置 
                    <package name="demo" extends="struts-default">

                        <action name="*" class="com.cad.struts2.Hello" method="{1}">
                            <result name="success">/{1}.jsp</result>
                            <result name="error">/error.jsp</result>
                        </action>

                    </package>  

action的name中可以使用萬用字元 * , * 可以匹配所有的action, * 的值為傳入的action名字,例如傳入了addUser.action,那麼 * 的值就為addUser。method屬性中可以使用表示式來獲取 * 的值,{第幾個*}

例如 ” * _ * “,我們傳遞add_User,那麼{1}的值就是add,{2}的值就是User。

動態方法呼叫

使用動態呼叫前要先將動態呼叫的常量更改成true,動態呼叫預設是false,因為不安全。 

        <constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>

        我們使用動態方法呼叫我們需要的方法。 

        格式 :動作名稱!方法名稱
我們配置struts.xml檔案 ,不寫method值,也不用萬用字元
    <package name="demo" extends="struts-default">

        <action name="user" class="com.cad.struts2.Hello" >
            <result name="success">/welcome.jsp</result>
            <result name="error">/error.jsp</result>
        </action>
    </package>  
我們更改jsp頁面按照動態方法呼叫的格式,就可以呼叫相關的方法。 
            <body>
                    <a href="${pageContext.request.contextPath }/user!addUser">新增使用者</a>
                    <a href="${pageContext.request.contextPath }/user!updateUser">修改使用者</a>
                    <a href="${pageContext.request.contextPath }/user!selectUser">檢視使用者</a>
                    <a href="${pageContext.request.contextPath }/user!deleteUser">刪除使用者</a>
            </body> 

管理處理結果

當Action處理完使用者請求時,處理結果應該通過檢視資源實現,但將哪個檢視呈現給瀏覽者呢。由<result.../>來決定

Action處理完使用者請求後,返回一個普通字串。整個普通字串就是一個邏輯檢視名。 

通過配置檔案將邏輯檢視和物理檢視聯絡起來。一旦系統收到Action返回的邏輯檢視名,就把對應的物理檢視呈現給瀏覽者。

struts2支援多種檢視技術。當一個Action處理使用者請求後,僅僅返回一個字串,這個字串只是邏輯檢視名

邏輯檢視名可以和很多檢視資源關聯。例如 JSP,FreeMarker等 

結果型別。

比如我們邏輯檢視名是success,對應success.jsp,那麼我們是請求轉發到該頁面還是重定向呢。這就需要我們指定結果型別。struts2提供了一系列的內建結果型別,在struts-default.xml中能看到。

<result../>屬性 
            name:邏輯檢視名稱,應該與Action返回的字串相同,如果不填寫,預設為success
            type:結果檢視型別,不寫的時候預設值為dispatcher(請求轉發)
            name是去哪裡,type是怎麼去。 

struts內建支援的常用結果型別

    -chain:Action鏈式處理。當一個Action處理完成之後,系統並不想轉發到檢視資源,而是希望下一個Action進行處理,此時就需要這個型別。  

    -dispatcher:請求轉發 

    -redirect:重定向 

    -redirectAction:重定向到其他Action 

    -stream:向瀏覽器返回一個InputStream的結果型別(一般用於檔案下載) 

Chain例子

struts.xml檔案配置 
        當傳送請求demo1時,返回的結果轉發到demo2的Action處理  

        <package name="demo" extends="struts-default">

            <action name="demo1" class="com.cad.struts2.Hello" >
                <result type="chain">demo2</result>
            </action>

            <action name="demo2" >
                <result name="success" >/welcome.jsp</result>
                <result name="error">/error.jsp</result>
            </action> 

        </package>  

不同包之間的請求轉發

<package name="demo" extends="struts-default">

    <action name="demo1" class="com.cad.struts2.Hello" >
        <result type="chain"> 
            //因為結果型別都有對應的實現類,我們到請求轉發的實現類中發現,有actionName和namespace兩個引數,並提供了get和set方法
            //使用的是注入的思想,在請求轉發之前,先呼叫setNamespace和setActionName賦值
            <param name="actionName">demo2</param>
            <param name="namespace">/user</param>
        </result>
    </action>

</package> 
<package name="demo1" extends="struts-default" namespace="/user">
    <action name="demo2" >
        <result name="success" >/welcome.jsp</result>
        <result name="error">/error.jsp</result>
    </action>
</package> 

重定向也是一樣。  

自定義結果型別

需要實現一個結果型別類,繼承StrutsResultSupport類  
                我們這裡面使用我們的驗證碼小工具,輸出一個驗證碼 
                至於這個驗證碼小工具,以前的文章中有詳細的說明。 

            public class VcodeResult extends StrutsResultSupport {

                @Override
                protected void doExecute(String arg0, ActionInvocation arg1) throws Exception {
                    VerifiCode v=new VerifiCode();
                    HttpServletResponse response=ServletActionContext.getResponse();
                    BufferedImage  b=v.getImage();
                    v.output(b, response.getOutputStream());
                }

            }
    然後再建立一個Action類 ,什麼都不用寫
                    public class VcodeAction extends ActionSupport {

                        }   

        在struts.xml中進行配置 
                <package name="vcode" extends="struts-default" > 
                    //配置我們自定義的結果型別
                    <result-types>
                        <result-type  name="vcode" class="com.cad.struts2.VcodeResult"></result-type>
                    </result-types> 
                    //我們還是在我們的原頁面,所以不需要指定其他頁面,type即為我們的自定義結果型別
                    <action name="vcode" class="com.cad.struts2.VcodeAction">
                        <result name="success" type="vcode"></result>
                    </action>
                </package>  
        我們可以在自定義的結果型別類中新增getset方法,來方便我們的一些引數自定義。
            例如我們添加了weight,height的getset方法。 

                    <action name="vcode" class="com.cad.struts2.VcodeAction">
                        <result name="success" type="vcode">
                            <param name="weight">100</param>
                            <param name="height">100</param>
                        </result>
                    </action>    

    我們就可以自定義驗證碼的長寬等。這也又體現了我們的注入思想。
    我們前面請求轉發前設定nameSpace和actionName和我們做的其實是相同的操作。

全域性結果檢視和區域性結果檢視

我們在包中定義了自己的結果型別,只有在自己的包或者子包中才能使用,在別的包中還是無法使用這個結果型別,為了所有的Action都能使用,我們需要將其變為全域性。

                我們只需要定義一個包,繼承struts2的預設配置檔案
                <package name="myresult" extends="struts-default">
                    <result-types>
                        <result-type  name="vcode" class="com.cad.struts2.VcodeResult"></result-type>
                    </result-types>
                    <global-results>
                        <result>
                            <param name="weight">500</param>
                            <param name="height">1000</param>
                        </result>
                    </global-results>
                </package> 

        然後如果我們需要這個結果型別,只需要我們的包繼承這個包即可。
        在<global-results>中配置全域性引數,所有的action使用這個型別生成的驗證碼尺寸都一樣。

Action訪問Servlet API

第一種方式

            Struts2提供了一個ServletActionContext物件可以訪問ServletAPI。
            例如
                HttpServletRequest request=ServletActionContext.getRequest();
                HttpServletResponse response=ServletActionContext.getResponse();
                ServletContext context=ServletActionContext.getServletContext();
                HttpSession session=request.getSession(); 
第二種方式,實現ServletContextAware,ServletRequestAware,ServletResponseAware三個介面 

                public class VcodeAction extends ActionSupport implements ServletContextAware,ServletRequestAware,ServletResponseAware { 
                    //定義三個引數
                    private HttpServletRequest request;
                    private HttpServletResponse response;
                    private ServletContext context;

                    public String execute() throws Exception {

                        return null;
                    } 
                    //實現介面中設定引數的方法
                    @Override
                    public void setServletResponse(HttpServletResponse response) {
                        this.response=response;

                    }
                    @Override
                    public void setServletRequest(HttpServletRequest request) {
                        this.request=request;

                    }
                    @Override
                    public void setServletContext(ServletContext context) {
                        this.context=context;

                    }
                }


                執行流程是什麼,誰呼叫了set方法?
                struts的內建攔截器有一個ServletConfig的攔截器。
                它會先得到我們的動作類的引用,
                然後通過instanceof方法判斷我們動作類是否屬於ServletContextAware,ServletRequestAware,ServletResponseAware型別
                因為我們實現了這個介面,當然屬於這個型別
                然後獲取request,response等
                然後呼叫我們動作類實現的介面方法 setServletResponse,setServletRequest,setServletContext等為我們的request,response賦值。

分檔案編寫配置檔案

struts2允許將一個配置檔案分解成多個配置檔案,從而進行模組化的設計,也提高了配置檔案的可讀性。

            例如我們有一個商城系統,分為使用者模組,訂單模組等等很多模組 
            <struts>
                <include file="struts-user.xml"/>
                <include file="struts-order.xml"/>
                .......
            </struts>