學過SSH框架很長一段時間了,一直沒有很系統的總結一下,這裡先簡單談談Struts2。
為什麼要用Struts2?
這裡列舉一些Servlet的缺點:
1、每寫一個servlet在web.xml中都要做相應的配置。如果有多很servlet,會導致web.xml內容過於繁多。
2、這樣的結構不利於分組開發。
3、在servlet中,doGet方法和doPost方法有HttpServletRequest和HttpServletResponse參數。這兩個參數與容器相關,如果想在servlet中作單元測試,則必須初始化這兩個參數。
4、如果一個servlet中有很多個方法,則必須采用傳遞參數的形式,分解到每一個方法中。
而而而而而而而而而而。。。。先了解一下Struts2是什麼。
Struts2是一個遵循MVC的Web層框架。
先看一下基於Web的MVC三層架構:
這是一個MVC三層架構的基本模式,三層架構中的顯示層這裡是B/S結構的Web應用。而MVC就是Model、View、Controller。
說好的Struts2是一個Web層的MVC框架呢?在Struts2中MVC是什麼呢?
Struts2利用過濾器,攔截客戶端的請求。客戶端發送請求,經過struts2的過濾器,將HttpServletRequest參數和HttpServletResponse參數封裝,利用java反射機制將請求分派給映射的Action。根據Action的執行結果,轉向其他Action或jsp頁面
Struts2 的Action實現了與Servlet API的解耦,使得在Action裡面不需要再直接去引用和使用HttpServletRequest與HttpServletResponse等接口。因而使得Action的單元測試更加簡單,而且強大的類型轉換也使得我們少做了很多重復的工作。
下面看一下Struts2的原理圖:
具體過程大致如下:
1、客戶端向Servlet容器(例如Tomcat)發送請求
2、這個請求經過一系列的過濾器(Filter)
3、接著FilterDispatcher(現已過時)被調用,FilterDispatcher詢問ActionMapper來決定這個請是否需要調用某個Action
4、如果ActionMapper決定需要調用某個Action,FilterDispatcher把請求的處理交給ActionProxy
5、ActionProxy通過Configuration Manager詢問框架的配置文件,找到需要調用的Action類
6、ActionProxy創建一個ActionInvocation的實例。
7、ActionInvocation實例使用命名模式來調用,在調用Action的過程前後,涉及到相關攔截器(Intercepter)的調用。(此處采用了AOP,一系列的攔截器即通知,Action的方法為切入點)
8、Action執行完畢,ActionInvocation負責根據struts.xml中的配置找到對應的返回結果。返回結果通常是(但不總是,也可 能是另外的一個Action鏈)一個需要被表示的JSP或者FreeMarker的模版。在表示的過程中可以使用Struts2 框架中繼承的標簽。在這個過程中需要涉及到ActionMapper
在上述過程中所有的對象(Action,Results,Interceptors,等)都是通過ObjectFactory來創建的。
FilterDispatcher是早期struts2的過濾器,2.1.3後使用StrutsPrepareAndExecuteFilter。StrutsPrepareAndExecuteFilter,prepare進行配制的導入;execute表示進行過濾,指doFilter方法,即將request請求,轉發給對應的 action去處理。
上面是Struts2的基本原理,下面看一下Struts2使用主要涉及的幾個方面:攔截器,驗證,類型轉換,屬性驅動、模型驅動,OGNL。
攔截器
Struts2自帶的攔截器有35個之多。例如:輸入驗證是由名為validation攔截器處理的,如果禁用該攔截器,輸入驗證將停止工作;文件上傳依靠名為fileUpload的攔截器。
Struts2自帶的默認攔截器足以滿足絕大多數的應用程序的需要,但也可以自定義攔截器。
自定義攔截器
1、編寫一個類,實現com.opensymphony.xwork2.interceptor.Interceptor
2、主要實現public String intercept(ActionInvocation invocation) throws Exception{}方法
3、攔截器定義好後,要在配置文件中進行注冊:
<interceptors> <interceptor name=" interceptorName" class="className"/> </interceptors>
4、配置文件中的動作,通過 <interceptor-ref name=" interceptorName "></interceptor-ref> 使用該攔截器.
注意:一旦動作中使用了自定義的攔截器,那麼默認的就不起作用了。一般應該采用如下的做法:
<interceptor-ref name="defaultStack"></interceptor-ref> <interceptor-ref name=" interceptorName"></interceptor-ref>
多個動作類都要使用的話,可以通過package來進行組合。
驗證
有時候對於從客戶端傳來的數據需要驗證,例如登錄頁面,驗證用戶名不能為空,密碼也不能為空,並且長度不能小於6位數。
驗證的方法有分為以下幾種:
1、編程方式
動作類中的所有方法進行驗證:
步驟:
a、動作類繼承ActionSupport
b、覆蓋調用public void validate()方法
c、在validate方法中,編寫不符合要求的代碼判斷,並調用父類的addFieldError(String fieldName,String errorMessage)
如果fieldError(存放錯誤信息的Map)有任何的元素,就是驗證不通過,動作方法不會執行。Struts2框架會返回到name=input的result
d、在name=input指定的頁面上使用struts2的標簽顯示錯誤信息。<s:fielderror/>
動作類中指定的方法進行驗證:
編寫步驟與上面相同,驗證方法書寫有要求:
public void validateXxx() Xxx代表的是要驗證的動作方法名,其中要把動作方法名的首字母變為大寫。
2、基於XML配置文件的方式
①動作類中的所有方法進行驗證:
在動作類的包中,建立一個名稱為:動作簡單類名-validation.xml ,比如要驗證的動作類名是UserAction UserAction-validation.xml,內容如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE validators PUBLIC 3 "-//OpenSymphony Group//XWork Validator 1.0.3//EN" 4 "http://www.opensymphony.com/xwork/xwork-validator-1.0.3.dtd"> 5 <validators> 6 <field name="username"> 7 <!-- 內置驗證器都是定義好的,在xwork-core.jar com.opensymphony.xwork2.validator.validators包中的default.xml文件中 --> 8 <field-validator type="requiredstring"><!-- 不能為null或者""字符串,默認會去掉前後的空格 --> 9 <message>用戶名不能為空</message> 10 </field-validator> 11 </field> 12 </validators>
②動作類中指定的方法進行驗證:
配置文件的名稱書寫有一定要求:動作類名-動作名(配置文件中的動作名)-validation.xml 例如UserAction-user_add-validation.xml
3、自定義基於XML的驗證器
a、編寫一個類,繼承FieldValidatorSupport類。
b、在public void validate(Object object)編寫你的驗證邏輯,不符合要求的就向fieldErrors中放消息
c、一定注冊驗證器才能使用
在WEB-INF/classes目錄下建立一個名稱為validators.xml的配置文件,內容如下:
<validators> <validator name="strongpassword" class="wz.validators.StrongPasswordValidator"/> </validators>
d、日後就可以像使用Struts2提供的16個驗證器方式去使用了。
屬性驅動和模型驅動
屬性驅動
條件:
1、頁面中name的屬性和action中的屬性必須保持一致。
2、 Action中的屬性必須有get和set方法。
3、滿足這兩個條件就實現了屬性驅動。
過程:
1、 當執行所有的攔截器的時候,當前請求的action已經放在了對象棧棧頂。
2、 放在對象棧的對象的特點是其屬性能夠直接訪問。
3、 也就是說當執行ParameterInterceptor攔截器的時候,action的所有的屬性在棧頂。
4、 所以只需要給棧頂的action的屬性賦值就可以了。
5、 而ParameterInterceptor攔截器正好完成了此功能。
模型驅動
假設在完成網站的某項功能時,在後台需要得到20多個屬性。如果用action中的屬性獲取值,就要在action中會寫20個屬性以及其set和get方法。這樣會導致action中的代碼結構不是很好。
模型驅動很好的解決了這個問題。使用javaBean對象來封裝請求參數,實現ModelDriven接口並定義模型成員域即可。
例如:
1 public class ModelDriverAction extends ActionSupport implements ModelDriven<User>{ 2 private User model = new User(); 3 public User getModel() { 4 return this.model; 5 } 6 7 public String execute(){ 8 return "modeldriver"; 9 } 10 }
當浏覽器提交對當前Action的請求時,先經過攔截器。其中有一個攔截器為ModelDrivenInterceptor,從這個源代碼可以看出,這個攔截器的作用就是獲取實現了ModelDriver接口的action的模型驅動。在這裡為user。然後把模型驅動利用push方法壓入到對象棧棧頂。這樣就能直接通過屬性進行回顯和賦值了。
到底是用屬性驅動和是模型驅動呢?
(1)最好統一整個系統中的Action使用的驅動模型,即要麼都是用屬性驅動,要麼都是用模型驅動。
(2)如果DB中的持久層的對象與表單中的屬性都是一一對應的話,那麼就使用模型驅動,代碼要整潔很多。
(3)如果表單的屬性不是一一對應的話,那麼就應該使用屬性驅動,否則,你的系統就必須提供兩個Bean,一個對應表單提交的數據,另一個用與持久層。
類型轉換
從屬性驅動的角度考慮,中如果屬性中要求接受的不是String類型,而是其他類型呢?struts2將做自動的轉化。
客戶端表單的每一項輸入之可能是一個String或一個String數組。在服務器端,必須先把這些String值轉換為特定的數據類型,才能進行相應的處理把請求參數映射到動作屬性的工作由Parameters攔截器負責,它是defaultStack攔截器棧的一員。所有的請求參數都是String類型,但並非所有的動作屬性都是String類型,所以每一種非String類型的動作屬性需要對相關的請求參數進行類型轉換。有些Struts2可以自動轉化,而有些需要我們手動編寫轉換的代碼。
具體方式:
1、編寫一個類,繼承com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter
2、覆蓋掉其中的public Object convertValue(Map<String, Object> context, Object value,Class toType)
context:OGNL表達式的上下文
value:實際的值。用戶輸入的都是字符串,但他是一個String數組。
toType:目標類型
3、注冊類型轉換器
3.1局部類型轉換器:只對當前的Action有效
具體做法:在動作類相同的包中,建立一個名稱是“動作類名-conversion.properties”的配置文件,文件中增加以下內容:要驗證的字段=驗證器的類全名。例如:birthday=wz.convertor.DateConvertor
3.2全局類型轉換器:對所有的Action都有效
具體做法:在WEB-INF/classes目錄下,建立一個名稱為"xwork-conversion.properties"的配置文件,文件中增加以下內容:目標類型全名=驗證器的類全名。例如:java.util.Date=cn.itcast.convertor.DateConvertor
注意:如果轉換失敗,Struts2框架會尋找name=input的結果頁面
OGNL
OGNL表達式是(Object-Graph Navigation Language)是對象圖形化導航語言。OGNL是一個開源的項目,struts2中默認使用OGNL表達式語言來顯示數據。與serlvet中的el表達式的作用是一樣的。
提起OGNL就不得不提ValueStack了。ValueStack是一個接口,在struts2中使用OGNL表達式實際上是使用實現了ValueStack接口的類OgnlValueStack,這個類是OgnlValueStack的基礎。ValueStack貫穿整個action的生命周期。每一個action實例都擁有一個ValueStack對象。其中保存了當前action對象和其他相關對象。Struts2把ValueStack對象保存中名為struts.valueStack的request域中。
當struts接受一個請求時,會迅速創建ActionContext,ValueStack,action。然後把action存放進ValueStack,所以action的實例變量可以被OGNL訪問
ActionContext.getContext()從ThreadLocal中得到本線程的ActionContext對象
actionContext對象可以獲取context、application、session、valueStack等對象。後三者其實是從context中取出的。
ActionContext的成員域context是OgnlContext對象,即ValueStack中的context對象
context對象中存放request、session、application、parameters、attr等map以及ValueStack等對象
context map與valueStack的關系:
1、context中有一個鍵值對,key=com.opensymphony.xwork2.util.ValueStack.ValueStack,value=valueStack,即valueStack
2、valueStack中成員域包括CompoundRoot root和OgnlCotext context;。沒錯,就是上面的context。
3、而ActionContext中的成員域context,就是上面的context。
下面是ActionContext中context對象的內容,注意看地址。
暫時就這麼多吧。以上