1.訪問ServletApi三種方式
1).在Action訪問Servlet API:
在Action類中操作Servlet中相關(guān)的對象(request,repsonse,session,cookie等)
- 獲取請求參數(shù): request.getParameter(String ..)
- 設(shè)置/獲取共享數(shù)據(jù): 域?qū)ο?setAttribute(String name,Object val);
- 操作Cookie: response.addCookie(...);
2).Action訪問Servlet API,有三種方式:
1.通過Action實現(xiàn)感知接口.
操作步驟:
- 1):實現(xiàn)對應(yīng)的感知接口.
- 2):覆蓋接口中對應(yīng)的setter方法.
- 3):定義成員變量接受setter方法中的參數(shù).
- 4):使用對象.
原理:攔截器.
常用的感知接口:
ServletRequestAware ---->感知request對象.
ServletResponseAware---->感知response對象.
ServletContextAware----->感知application對象.
2.使用ServletActionContext類.
好比是一個工具類,封裝當(dāng)前請求的對象和響應(yīng)對象信息.
ServletActionContext類提供了很多靜態(tài)方法,可以直接獲取request/response等對象.
常用的方法:
static HttpServletRequest getRequest() :獲取請求對象
static HttpServletResponse getResponse() :獲取響應(yīng)對象
static ServletContext getServletContext() :獲取應(yīng)用對象

3.使用ActionContext類.
ActionContext:Action的上下文對象(環(huán)境).封裝了當(dāng)前請求/響應(yīng)的所有的數(shù)據(jù).
獲取ActionContext對象:
ActionContext ctx = ActionContext.getContext();



3).Action訪問Servlet API,有三種方式對比和選擇:
- 1.通過Action實現(xiàn)感知接口.
簡單/操作麻煩/讓Action和Servlet的API耦合在一起了. - 2.使用ServletActionContext類.
理解/使用都很簡單/讓Action和Servlet的API存在耦合. - 3.使用ActionContext類.
簡單/讓Action和Servlet的API沒有耦合,但是不能操作Cookie.
在開發(fā)中,使用方式2和方式3比較多,Struts2官方建議使用方式3.先使用方式3,搞不定再使用方式2.
2.Action獲取請求參數(shù)三種方式
J2EE的最佳實踐:按照功能職責(zé),分層開發(fā):
表現(xiàn)層:
職責(zé):
1.接受請求數(shù)據(jù)
2.把數(shù)據(jù)封裝Model對象
3.調(diào)用業(yè)務(wù)邏輯方法處理請求
4.控制界面跳轉(zhuǎn)
MVC思想:
M:數(shù)據(jù)對象.
V:JSP
C:StrutsPrepareAndExecuteFilter(前端控制器.)
問題:Action到底充當(dāng)什么角色?
Action獲取請求參數(shù)三種方式:
1).第一種:Action本身作為Model對象,通過setter方法封裝(屬性注入)
JSP(注意表單參數(shù)的名字):

Action:

注意:表單中的參數(shù)名稱要和Action中的setter方法的屬性名相同.
2).第二種:創(chuàng)建獨立Model對象,頁面通過ognl表達式封裝(屬性注入)
1):創(chuàng)建Model對象,封裝參數(shù)數(shù)據(jù)信息.

2):JSP(注意表單參數(shù)的名字):

給當(dāng)請求Action中的user屬性對應(yīng)的對象中,設(shè)置username和password值.
Action中的:user屬性.--->對應(yīng)一個對象.
user屬性對應(yīng)對象的:username/password屬性
:先從Action中獲取是否有user對象(getUser方法),
如果有,就直接在該對象中設(shè)置其他參數(shù)值.
如果沒有:創(chuàng)建新的User對象.
User user = Struts2框架通過getUser方法來獲取User對象.
if(uesr ==null){
user = new User();
}
把參數(shù)設(shè)置到user對象中去.
3):Action代碼:

方式1:同時提供getter/setter.
方式2:在生命對象的時候,new出來,再提供getter方法即可.

3).第三種:使用ModelDriven接口,對請求數(shù)據(jù)進行封裝(模型驅(qū)動)
1):JSP(注意表單參數(shù)的名字):

username和password對應(yīng):
Model中的屬性:
2):Action類:

4).Action獲取請求參數(shù)三種方式對比:
第一種:Action本身作為Model對象,通過setter方法封裝(屬性注入)
1:如果參數(shù)比較多,此時Action中將提供大量字段和setter方法,Action很臃腫.
2:我們還得手動把數(shù)據(jù)封裝到對象中去.
第二種:創(chuàng)建獨立Model對象,頁面通過ognl表達式封裝(屬性注入)
1:表單參數(shù)的name屬性值,不是很清晰.
第三種:使用ModelDriven接口,對請求數(shù)據(jù)進行封裝(模型驅(qū)動)
1:不能為多個對象封裝數(shù)據(jù).
一般的,我們在開發(fā)中使用第二種較多.
但是,修改密碼的操作:
在User對象中,只有密碼這個屬性.
有時候,我們是方式1+方式2或者方式1+方式3.
3.Interceptor(攔截器)的美
攔截器:Interceptor
攔截器:
Struts2攔截器是在訪問某個Action的某個方法之前或之后實施攔截,并且Struts2攔截器是可插拔的,攔截器是AOP的一種實現(xiàn).
AOP:
面向切面編程.其實現(xiàn)原理:動態(tài)代理模式--->留給Spring
WebWork中文文檔解釋:
攔截器是動態(tài)攔截Action調(diào)用的對象。它提供了一種機制可以使開發(fā)者可以定義在一個Action執(zhí)行的前后執(zhí)行的代碼,也可以在一個action執(zhí)行前阻止其執(zhí)行。同時也提供了一種可以提取Action中可重用的代碼的方式。
攔截器棧(Interceptor Stack):
Struts2攔截器棧就是將攔截器按一定的順序連接成一條鏈。在訪問被攔截的方法或字段時,Struts2攔截器鏈中的攔截器就會按其之前定義的順序被調(diào)用。
攔截器的"美":
DRY原則:Dont't Repeat Yourself.
攔截器在設(shè)計和程序結(jié)構(gòu)上的優(yōu)點:
攔截器能把很多功能從Action中獨立出來,分散到不同的攔截器里面,減少了Action的代碼。如此,攔截器和Action本身的功能都更單一了。當(dāng)通用的功能代碼被封裝在攔截器里面(代碼模塊化),就可以對不同的Action,根據(jù)功能需要,來配置相應(yīng)功能的攔截器了。提高了攔截器所實現(xiàn)的功能的重用性,也變相實現(xiàn)了裝配式和可插拔式的體系結(jié)構(gòu),使得整個系統(tǒng)結(jié)構(gòu)變得更靈活。
1.簡化Action的實現(xiàn)
2.功能更單一
3.通用代碼模塊化
4.提高重用性
4.Struts2執(zhí)行流程
1).Struts2的執(zhí)行步驟:
- ①.客戶端發(fā)送請求;
- ②.該請求經(jīng)過一系列的過濾器(Filter):其中可選過濾器ActionContextCleanUp,幫助Struts2和其他框架集成。例如:SiteMesh Plugin。
- ③.接著FilterDispatcher被調(diào)用,F(xiàn)ilterDispatcher詢問ActionMapper,來決定該請求是否需要調(diào)用某個Action。
- ④.若ActionMapper決定需要調(diào)用某個Action,F(xiàn)ilterDispatcher把請求的處理交給ActionProxy。
- ⑤.ActionProxy通過Configuration Manager詢問框架的配置文件,找到需要調(diào)用的Action類。
- ⑥.ActionProxy創(chuàng)建一個ActionInvocation的實例。
- ⑦.ActionInvocation實例調(diào)用Action的前后,涉及到相關(guān)攔截器(Intercepter)的調(diào)用。
- ⑧.一旦Action執(zhí)行完畢,ActionInvocation負(fù)責(zé)根據(jù)struts.xml中的配置找到對應(yīng)的返回結(jié)果。返回結(jié)果是一個JSP或其他頁面(也可以是其他的Action鏈)。 JSP頁面展現(xiàn)可使用Struts2框架中的標(biāo)簽(該過程會涉及ActionMapper)。
在上述過程中所有的對象(Action、Interceptors、Results等)都由xwork容器中的ObjectFactory創(chuàng)建。


2).Struts2中內(nèi)置的攔截器:
在struts-core-2.3.x.jar--->struts-default.xml中
常見的攔截器:
- 1:params攔截器
這個攔截器偷偷的把請求參數(shù)設(shè)置到相應(yīng)的Action的屬性去的,并自動進行類型轉(zhuǎn)換。 - 2.modelDriven攔截器
如果Action實現(xiàn)ModelDriven接口,它將getModel()取得的模型對象存入OgnlValueStack中。 - 3.execption攔截器
顧名思義,在拋出異常的時候,這個攔截器起作用。最好把它放在第一位,讓它能捕獲所有的異常。 - 4.validation攔截器
調(diào)用驗證框架讀取 *-validation.xml文件,并且應(yīng)用在這些文件中聲明的校驗。 - 5.token攔截器
核對當(dāng)前Action請求(request)的有效標(biāo)識,防止重復(fù)提交Action請求。 - 6.fileUpload攔截器
用來處理文件上傳 - 7.workflow攔截器
調(diào)用Action的validate方法,一旦有錯誤返回,重新定位到INPUT結(jié)果視圖 - 8.servletConfig
通過感知接口,獲取感應(yīng)對象

5.自定義攔截器
1).需求:
做一個登陸攔截器(LoginCheckInterceptor),攔截Action訪問.
如果強行訪問某一個需要登陸之后才能訪問的Action,直接跳轉(zhuǎn)到登錄頁面.

2).操作步驟:
步驟1.定義攔截器類
方式1:實現(xiàn)com.opensymphony.xwork2.interceptor.Interceptor接口.
方式2:繼承com.opensymphony.xwork2.interceptor.AbstractInterceptor類.

步驟2.在struts.xml中注冊攔截器
第一步:先在<package>中聲明攔截器LoginInterceptor.
第二步:在<action>中來引用LoginInterceptor攔截器.

上圖的攔截器引用配置:
因為是在<action name="main">中引用了登錄攔截器,所以只有當(dāng)前Action才會做登錄攔截功能.其他Action元素,沒有這個功能.
如果其他多個action元素都做登錄檢查功能,那么所有action元素都得配置:
<interceptor-ref name="loginCheck" />
如此一來,重復(fù)配置.
3).可以通過全局配置攔截器:

如果不攔截某一個Action:
1.方式一:代碼中放行:

2.方式二:默認(rèn)攔截器

6.OGNL和ValueStack(值棧)
1).什么是OGNL
OGNL是Object Graphic Navigation Language(對象圖導(dǎo)航語言)的縮寫,它是一個開源項目。
Struts2框架使用OGNL作為默認(rèn)的表達式語言。
EL(表達式語言),OGNL就是EL的升級版.
作用:Action和視圖(JSP)之間數(shù)據(jù)交互的橋梁.
講解OGNL之前,先得學(xué)習(xí)ValueStack.
2).什么是ValueStack
值棧是對應(yīng)每一個請求對象的輕量級的內(nèi)存數(shù)據(jù)中心。
每一次請求的時候,都會創(chuàng)建一個新的ValueStack對象,該ValueStack對象封裝了這一次請求相關(guān)的數(shù)據(jù)信息.
- 1).ValueStack實際是一個接口,在Struts2中利用OGNL時,實際上使用的是實現(xiàn)了該接口的OgnlValueStack類,這個類是Struts2利用OGNL的基礎(chǔ)。
- 2).ValueStack貫穿整個Action的生命周期(一次請求):每個Action類的實例都擁有一個ValueStack對象。 ValueStack相當(dāng)于數(shù)據(jù)的中轉(zhuǎn)站,在其中保存該次請求和當(dāng)前Action對象和其他相關(guān)對象信息。
每一次的請求: 一個新的Action對象,新的ActionContext對象,新的ValueStack對象.
- 3).Struts2框架把ValueStack對象保存在名為“struts.valueStack”的request屬性中。
3).如何獲取ValueStack.
- 方式1: 因為ValueStack在請求中,屬性名為:struts.valueStack.
ValueStack vs = request.getAttribute("struts.valueStack");
ValueStack vs = ServletActionContext.getRequest().getAttribute("struts.valueStack");
ValueStack vs = ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
- 方式2: 通過ActionContext對象獲取
ValueStack vs = ActionContext.getContext().getValueStack();
以上兩種方式獲取的是同一個ValueStack對象.

4).ValueStack內(nèi)部結(jié)構(gòu)
ValueStack對象中有兩個很重要的屬性,這兩個屬性就是ValueStack的主要內(nèi)部結(jié)構(gòu):
| 屬性 | 類型 | 數(shù)據(jù)儲存類型 | 引用 | 獲取數(shù)據(jù) |
|---|---|---|---|---|
| root | CompoundRoot extends ArrayList : 棧的數(shù)據(jù)結(jié)構(gòu)(后進先出) | 主要存儲Action對象相關(guān)的數(shù)據(jù)信息. | - | 從root中獲取數(shù)據(jù): 直接使用屬性名獲取. ---><s:property value="屬性名"/> |
| context | Map 上下文 | 主要存儲映射關(guān)系數(shù)據(jù).(key-value). | 存在用root對象的引用(只要拿到context對象就可以獲取到root對象);context中還存在request、session、application、attr、parameters對象的引用。 | 從context中獲取數(shù)據(jù): #key ---><s:property value="#key"/> |



5).把數(shù)據(jù)放入ValueStack.
1.把數(shù)據(jù)放入root中:(棧,ArrayList.每次都要壓在棧頂)
- 方式1:ValueStack對象.getRoot().add(0, Obejct val);//把數(shù)據(jù)壓入棧頂
- 方式2:ValueStack對象.getRoot().push(Object val):等價于valueStack對象.getRoot().add(0, Obejct val);
- 方式3:ValueStack對象.set(String propertyName,Object value);
- 方式4.在Action中提供一個可訪問的屬性(getter方法).
此時:Action在棧頂,往Action中存儲屬性和屬性值.
2).把數(shù)據(jù)放入context中:
- 方式1:ValueStack對象.getContext().put(String key,Object value); 太長了.
- 方式2:ActionContext對象.put(String key,Object value);
一般的:把集合中的數(shù)據(jù)放入context中.
6).如何從JSP中取出ValueStack中的數(shù)據(jù):
- 此時必須使用Struts的標(biāo)簽.--->先引入Struts2標(biāo)簽
<%@ taglib uri="/struts-tags" prefix="s"%>
Struts2的調(diào)試標(biāo)簽:<s:debug/> :主要用來觀察數(shù)據(jù)在哪一個空間(root,context).
訪問方式:<s:property value="OGNL表達式"/>.
1).獲取root中數(shù)據(jù):
若:放入root中的數(shù)據(jù),沒有屬性名:<s:property value="[棧中的存儲位置].top"/>:棧頂是0.
2).把context中數(shù)據(jù):
<s:property value="#key"/>

EL可以訪問ValueStack中的數(shù)據(jù):
不建議這么做,為什么可以呢:
Struts2重新包裝而來請求對象.${msg}---><%=pageContext.findAttribute("msg") %>
StrutsRequestWrapper:先從ValueStack中取出數(shù)據(jù),再放入request中.