一、struts2工作原理(網友總結,千遍一律)
1 客戶端初始化一個指向Servlet容器(例如Tomcat)的請求
2 這個請求經過一系列的過濾器(Filter)(這些過濾器中有一個叫做ActionContextCleanUp的可選過濾器,這個過濾器對於Struts2和其他框架的集成很有幫助,例如:SiteMesh Plugin)
3 接著FilterDispatcher被調用,FilterDispatcher詢問ActionMapper來決定這個請是否需要調用某個Action
4 如果ActionMapper決定需要調用某個Action,FilterDispatcher把請求的處理交給ActionProxy
5 ActionProxy通過Configuration Manager詢問框架的配置文件,找到需要調用的Action類
6 ActionProxy創建一個ActionInvocation的實例。
7 ActionInvocation實例使用命名模式來調用,在調用Action的過程前後,涉及到相關攔截器(Intercepter)的調用。
8 一旦Action執行完畢,ActionInvocation負責根據struts.xml中的配置找到對應的返回結果。返回結果通常是(但不總是,也可 能是另外的一個Action鏈)一個需要被表示的JSP或者FreeMarker的模版。在表示的過程中可以使用Struts2 框架中繼承的標簽。在這個過程中需要涉及到ActionMapper
在上述過程中所有的對象(Action,Results,Interceptors,等)都是通過ObjectFactory來創建的。
二、struts2基本配置
2.1 struts.xml配置文件詳解
<struts> <!-- 開發模式下使用,打印更多詳細錯誤信息 --> <constant name="struts.devMode" value="true" /> <!-- 國際化 --> <constant name="struts.i18n.encoding" value="UTF-8"/> <!-- 允許ognl訪問靜態方法 --> <constant name="struts.ognl.allowStaticMethodAccess" value="true" /> <!-- 該屬性指定需要struts2處理的請求後綴,默認值是action,即,所有匹配*.action的請求 都由struts2處理,如果用戶需要指定多個請求後綴,則多個後綴之間以英文逗號","隔開 --> <constant name="struts.action.extension" value="action"/> <!-- 設置浏覽器是否緩存靜態內容,默認值為true(生產環境下使用),開發階段最好關閉 --> <constant name="struts.serve.static.browserCache" value="false"/> <!-- 當struts的配置文件修改後,系統是否自動重新加載該文件,默認值false:不重新加載 --> <constant name="struts.configuration.xml.reload" value="true"/> <!-- 默認的視圖主題 --> <constant name="struts.ui.theme" value="simple"/> <!-- 與spring集成時,指定由spring負責管理action對象的創建 --> <constant name="struts.objectFactory" value="spring"/> <!-- 該屬性設置struts2是否支持動態方法調用,默認值true:支持 --> <constant name="struts.enable.DynamicMethodInvocation" value="false"/> <!-- 上傳文件的大小限制 --> <constant name="struts.multipart.maxSize" value="10701096"/> <!-- 引入文件 --> <include file="cn/qjc/action/login/login.xml"></include> <include file="cn/qjc/action/demo/demo01.xml"></include> <include file="cn/qjc/interceptor/interceptor.xml"></include> </struts>
struts2配置文件加載順序
a、default.properties:struts2-core**.jar org.apache.struts包中(程序員只能看)
b、struts-default.xml:struts2-core**.jar中(程序員只能看)
c、struts-plugin.xml:在插件的jar包中(程序員只能看)
d、struts.xml:在應用的構建路徑頂端。自己定義的Struts配置文件(推薦)
e、struts.properties:在應用的構建路徑頂端。程序員可以編寫(不推薦)
f、web.xml:配置過濾器時,指定參數。程序員可以編寫(不推薦)
特別注意:順序是固定的。後面的配置會覆蓋前面的同名配置信息。
加載struts.xml過程
說明:
1、 在啟動的時候加載了三個配置文件 struts-default.xml、struts-plugin.xml、struts.xml
2、 如果這三個文件有相同的項,後面覆蓋前面的。
3、 struts.xml文件必須放在src下才能找到。
2.2 package元素
意義:分模塊開發
屬性:
name:必須的。配置文件中要唯一。就是一個名字。
extends:指定父包。會把父包中的配置內容繼承下來。一般需要直接或間接的繼承一個叫做“struts-default”的包(在struts-default.xml配置文件中)。
如果不繼承該包,那麼Struts2中的核心功能將無法使用。
abstract:是否是抽象包。沒有任何action子元素的package可以聲明為抽象包。
namespace:指定名稱空間。一般以”/”開頭。該包中的動作訪問路徑:namesapce+動作名稱。如果namespace=””,這是默認名稱空間,和不寫該屬性是一樣的。
2.3 action配置
作用:定義一個動作。
屬性:
name:必須的。動作名稱。用戶用於發起請求。在包中要唯一。
class:指定動作類的全名。框架會通過反射機制實例化。默認是:com.opensymphony.xwork2.ActionSupport。
method:指定動作類中的動作方法。框架會執行該方法。默認是execute()。
<!-- 配置全局視圖:訪問動作時沒有局部視圖,則找全局視圖 --> <package name="default" extends="struts-default" abstract="true"> <global-results> <result name="success">/WEB-INF/login/success.jsp</result> </global-results> </package> <package name="login" namespace="/user" extends="default"> <action name="login" class="cn.qjc.action.login.Login" method="login"> <!-- type默認dispatcher 表示請求轉發 --> <result name="success" type="dispatcher">/WEB-INF/login/success.jsp</result> <result name="error">/WEB-INF/login/error.jsp</result> </action> </package>
三、動作類(Action類)
3.1 編寫動作類的三種方式:
a、POJO(Plain Old Java Object)普通的JavaBean。
/** * 編寫動作類方式一:普通javaBean * @author qjc */ public class Demo1Action { public String seyHello(){ System.out.println("動作類執行了"); return "success"; } }
b、實現com.opensymphony.xwork2.Action接口
/** * 編寫動作類方式二:實現Aaction接口 * @author qjc */ public class Demo2Action implements Action{ @Override public String execute() throws Exception { System.out.println("動作類執行了"); return SUCCESS; } }
c、繼承com.opensymphony.xwork2.ActionSupport(推薦)
意義:提供了一些基本的功能。比如驗證和國際化消息提示等。
/** * 編寫動作類方式三:繼承ActionSupport類 * @author qjc */ public class Demo3Action extends ActionSupport{ }
3.2 ActionSupport用法
在struts框架中,准備了一個ActionSupport類,源碼分析:
代碼段一:
public class ActionSupport implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable {
代碼段二:
/** * A default implementation that does nothing an returns "success". * <p/> ActionSupport是一個默認的Action實現,但是只返回了一個字符串success * Subclasses should override this method to provide their business logic. * <p/>子類需要重新覆蓋整個方法,在這個方法中寫相應的邏輯 * See also {@link com.opensymphony.xwork2.Action#execute()}. * * @return returns {@link #SUCCESS} * @throws Exception can be thrown by subclasses. */ public String execute() throws Exception { return SUCCESS; }
代碼段三:
public static final String SUCCESS = "success";
說明:
1、代碼段一說明了ActionSupport也實現了Action接口(以前寫的類實現了Action接口)
2、代碼段二說明如果程序員寫自己的action繼承了ActionSupport,需要重新覆蓋execute方法即可。
3、這個方法默認的返回的是success;
所以在配置文件中也可以這樣寫:
<action name="actionSupprotAction"> <result name="success">/baseconfig/successActionSupport.jsp</result> </action>
可以看到action標簽中沒有class屬性,在struts-default.xml中,
<default-class-ref class="com.opensymphony.xwork2.ActionSupport" />
說明:
如果在action標簽中沒有寫class屬性。那麼根據上述的配置文件,struts2會啟用ActionSupport這個類。所以action標簽中的class屬性可以不寫。
四、結果類型
1、 每個action方法都返回一個String類型的值,struts一次請求返回什麼值是由這個值確定的。
2、 在配置文件中,每一個action元素的配置都必須有result元素,每一個result對應一個action的返回值。
3、 Result有兩個屬性:
name:結果的名字,和action中的返回值一樣,默認值為success;
type:響應結果類型,默認值為dispatcher.
在struts-default.xml文件中,如下面所示
<result-types> <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/> <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/> <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/> <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/> <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/> <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/> <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/> <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/> <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/> <result-type name="plainText"
說明:
1、 從上述可以看出總共10種類型
2、 默認類型為ServletDispatcherResult即轉發。
3、 結果類型可以是這10種結果類型的任意一種。
4.1 dispatcher類型:
dispatcher類型是最常用的結果類型,也是struts框架默認的結果類型。
在配置文件中,可以有兩種寫法:
第一種寫法: <result name="success">/resulttype/successDispatcher.jsp</result> 第二種寫法: <result name="success"> <param name="location">/resulttype/successDispatcher.jsp</param> </result>
下面的圖說明了location的來歷:
4.2 redirect類型:
這種結果類型與dispatcher類型相對,dispatcher結果類型是將請求forwsrd(轉發)到指定的JSP資源;而redirect結果類型,則意味著將請求redirect(重定向)到指定的視圖資源。
redirect與dispatcher差別主要是轉發和重定向的差別:重定向會丟失所有的請求參數、請求屬性,當然也丟失Action處理結果。
注意:
使用redirect類型的結果時,不能重定向到/WEB-INF/路徑下任何資源,因為重定向相當於重新發送請求,而Web應用的WEB-INF/路徑是受保護的。
4.3 redirectAction類型:
這種結果類型與redirect類型非常相似,一樣是重新生成一個全新請求,但與redirect區別在於:redirectAction使用ActionMapperFactory提供的ActionMapper來重定向求情。
即:把結果類型重新定向到action。
可以接受兩種參數
a) actionName: action的名字
b) namespace:命名空間
第一種方式
<result name="success" type="redirectAction">resulttype/redirectactionAction.action</result>
第二種方式
<result name="success" type="redirectAction"> <!-- actionName: 請求的action的路徑 namespace: 如果不寫,默認就是請求的action的路徑,如果寫,路徑將被重新賦值 --> <param name="actionName"> resulttype/redirectactionAction.action </param> </result>
4.4 其他類型
freemarker:用於轉發到另外一個freemarker模板。(頁面靜態化)
velocity:用於轉發到另外一個velocity模板。
httpheader:用於輸出http協議的消息頭。
xslt:XML有關的樣式
redirect:用於重定向到另外一個JSP頁面。
redirectAction:用於重定向到另外一個動作。
stream:用於文件下載(日後再講。文件上傳和下載)
plainText:以純文本的形式展現頁面。輸出源碼。
4.5 自定義類型
自定義結果類型步驟
以隨機驗證碼圖片為例
a、編寫一個類,直接或間接實現com.opensymphony.xwork2.Result接口。一般繼承org.apache.struts2.dispatcher.StrutsResultSupport類
/** * 使用自定義結果集加載驗證碼 * @author qjc */ public class Demo02 extends StrutsResultSupport{ private int width=120; private int height=80; private int codeCount=4; private int lineCount=100; @Override protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { //輸出結果即可 ValidateCode vc = new ValidateCode(width, height, codeCount, lineCount); BufferedImage image = vc.getBuffImg(); //輸出即可 HttpServletResponse response = ServletActionContext.getResponse(); ImageIO.write(image, "jpeg", response.getOutputStream()); } //此處省略getter and setter ...
}
b、聲明結果類型,然後才能使用
<!--
自定義結果集
--> <package name="p3" extends="struts-default"> <result-types> <!-- 結果類型定義 --> <result-type name="captcha" class="cn.qjc.action.demo.Demo02"></result-type> </result-types> </package>
c、使用
<action name="captcha"> <!--使用自定義結果類型:captcha --> <result name="success" type="captcha"> <param name="width">200</param> </result> </action>
頁面編寫
<body> <form action="${pageContext.request.contextPath }/user/login.action"> 用戶名:<input name="username"><br> 密 碼:<input type="password" name="password"><br> 驗證碼:<input name="code" size="4"> <img src="${pageContext.request.contextPath}/captcha.action"><br> <input type="submit" value="登錄"> </form> </body>
五、Action原型模式
在servlet中,一個servlet只有一個對象,也就是說servlet是單例模式。如果把一個集合寫在servlet屬性中,則要考慮線程安全的問題。
但是在struts2的框架中,並不存在這種情況,action是多例的。也就是說struts2中的action,只要訪問一次就要實例化一個對象。這樣就不存在線程安全的問題。這也是struts2框架的一個好處。
可以寫一個類,如下:
package cn.qjc.struts2.action.moreinstance; import com.opensymphony.xwork2.ActionSupport; @SuppressWarnings("serial") public class MoreInstanceAction extends ActionSupport{ public MoreInstanceAction(){ System.out.println("create new action"); } public String execute(){ System.out.println("more instance action"); return SUCCESS; } }
配置文件為:
<struts> <package name="moreinstance" namespace="/moreinstance"> <action name="moreinstanceAction" class="cn.qjc.struts2.action.moreinstance.MoreInstanceAction"> </action> </package> </struts>
請求兩次http://localhost:8080/struts2/moreinstance/moreinstanceAction.action路徑,如果構造函數中的”create new action”輸出兩次,說明創建了兩次對象。