如果要編寫自定義攔截器,那么可以只用實(shí)現(xiàn)com.opensymphony.xwork2.interceptor.Interceptor這個(gè)接口即可。
在這個(gè)類中,需要實(shí)現(xiàn)3個(gè)方法:destroy(),init(),intercept(),核心方法是intercept(),該方法完成了主要邏輯。
在該方法中有個(gè)參數(shù)invocation,類型為ActionInvocation接口,這個(gè)接口中最重要的是invoke方法。
intercept 方法可以如此實(shí)現(xiàn):
- String result = invocation.invoke();
-
- return result;
例如,可以這樣來(lái)實(shí)現(xiàn)自定義攔截器
- package cn.tshining.interceptor;
-
- import com.opensymphony.xwork2.ActionInvocation;
- import com.opensymphony.xwork2.interceptor.Interceptor;
-
- public class MyInterceptor implements Interceptor {
-
- public void destroy() {
- System.out.println("destory");
- }
-
- public void init() {
- System.out.println("init");
- }
-
- public String intercept(ActionInvocation invocation) throws Exception {
- String result = invocation.invoke();
- return result;
- }
-
- }
配置攔截器
定義好攔截器后,需要在struts.xml中配置使用攔截器,配置使用攔截器有2步:
1.聲明攔截器。
在 struts.xml中package元素下增加元素
- <interceptors>
- <interceptor name="myinterceptor" class="cn.tshining.interceptor.MyInterceptor"></interceptor>
- </interceptors>
2.使用攔截器
首先需要了解的是,struts2已經(jīng)定義了一個(gè)默認(rèn)的攔截器棧,攔截器棧就是若干個(gè)攔截器或攔截器棧組成的一系列攔截器的集合。
在 struts2-core-2.1.8.jar下struts-default.xml中定義了這個(gè)默認(rèn)的攔截器棧。
在 struts中所有的action都使用了默認(rèn)的攔截器棧,若想使用自定義的攔截器棧,只用在action中配置即可,但是此時(shí)必須顯式的寫出使用默認(rèn)攔截器棧的配置信息。
- <action name="register2" class="cn.tshining.action.RegisterAction">
- <result name="success">/success.jsp</result>
- <result name="input">/register2.jsp</result>
- <interceptor-ref name="myinterceptor"></interceptor-ref>
- <interceptor-ref name="defaultStack"></interceptor-ref>
- </action>
同時(shí)可以在控制臺(tái)上看到輸出的"init",這是由于攔截器在服務(wù)器啟動(dòng)時(shí)會(huì)自動(dòng)加載,完成初始化。攔截器中定義的intercept方法就是在提交請(qǐng)求時(shí)執(zhí)行的。
用戶輸入的數(shù)據(jù)會(huì)自動(dòng)賦值到action中,實(shí)際上是底層攔截器起的作用。
在 struts-default.xml中可以看到名為params的攔截器,這個(gè)攔截器的作用就是將用戶輸入的數(shù)據(jù)注入到Action中的字段中。
使用攔截器參數(shù)
在定義攔截器,或者在使用攔截器時(shí)可以給攔截器傳遞參數(shù)。
首先在 MyInterceptor類中聲明一下字段
public int myparam;
修改 intercept方法:
- public String intercept(ActionInvocation invocation) throws Exception {
- String result = invocation.invoke();
- System.out.println(myparam);
- return result;
- }
修改struts.xml中攔截器的定義:
- <interceptors>
- <interceptor name="myinterceptor" class="cn.tshining.interceptor.MyInterceptor">
- <param name="myparam">1</param>
- </interceptor>
- </interceptors>
然后運(yùn)行該程序,在register2.jsp中輸入正確數(shù)據(jù)提交后就會(huì)在控制臺(tái)中輸出1.
也可以在攔截器調(diào)用時(shí)再傳入?yún)?shù):
- <action name="register2" class="cn.tshining.action.RegisterAction">
- <result name="success">/success.jsp</result>
- <result name="input">/register2.jsp</result>
- <interceptor-ref name="myinterceptor">
- <param name="myparam">2</param>
- </interceptor-ref>
- <interceptor-ref name="defaultStack"></interceptor-ref>
- </action>
此時(shí)重啟服務(wù)器,在register2.jsp中輸入正確的數(shù)據(jù)之后,可以看到控制臺(tái)中輸出2.
注意此時(shí)并沒(méi)有刪除之前攔截器定義時(shí)的參數(shù),由此可見(jiàn),在攔截器調(diào)用時(shí)的參數(shù)比攔截器定義時(shí)優(yōu)先級(jí)高。
上面的攔截器調(diào)用中使用到了默認(rèn)攔截器棧,當(dāng)然自己也可以自定義默認(rèn)攔截器棧。
- <interceptors>
- <interceptor name="myinterceptor" class="cn.tshining.interceptor.MyInterceptor">
- <param name="myparam">1</param>
- </interceptor>
- <interceptor-stack name="myDefaultStack">
- <interceptor-ref name="myinterceptor"></interceptor-ref>
- <interceptor-ref name="defaultStack"></interceptor-ref>
- </interceptor-stack>
- </interceptors>
- <default-interceptor-ref name="myDefaultStack"></default-interceptor-ref>
這樣,這個(gè)自定義的默認(rèn)攔截器棧中包含了自定義的攔截器和struts2定義的默認(rèn)攔截器棧。
在攔截器棧的定義中,既可以引用攔截器,也可以引用攔截器棧。
在 action中沒(méi)定義攔截器時(shí),會(huì)使用默認(rèn)攔截器。
當(dāng)然 struts2提供了一個(gè)簡(jiǎn)便的創(chuàng)建自定義攔截器的方法。
只需要繼承AbstractInterceptor類,該類空實(shí)現(xiàn)了Interceptor接口的init和destroy方法,因此只用實(shí)現(xiàn) intercept方法即可。
在這里我們override AbstractInterceptor的init方法。
- package cn.tshining.interceptor;
-
- import com.opensymphony.xwork2.ActionInvocation;
- import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
-
- public class MyInterceptor2 extends AbstractInterceptor {
-
- @Override
- public String intercept(ActionInvocation invocation) throws Exception {
- String result = invocation.invoke();
- return result;
- }
-
- @Override
- public void init() {
- System.out.println("init2");
- }
- }
在<interceptor-ref name="myinterceptor"></interceptor-ref>下面增加以下內(nèi)容:
- <interceptor-ref name="myinterceptor2"></interceptor-ref>
這樣就將myinterceptor2增加到了默認(rèn)攔截器棧中,查看控制臺(tái)輸出會(huì)看到:
init
init2
銷毀時(shí)會(huì)看到:
destory2
destory
這是由于先配置的攔截器先執(zhí)行,結(jié)束時(shí)后配置的攔截器先執(zhí)行
在這里攔截器的執(zhí)行順序?yàn)椋?
myintercept——myintercept2——action——myintercept2——myintercept
需要說(shuō)明的是一定要在action中引用攔截器才會(huì)使用攔截器,攔截器攔截的是Action中的execute或自定義的邏輯處理方法。
當(dāng)然也可以讓攔截器攔截指定的方法,那么我們需要繼承MethodFilterinterceptor類
- package cn.tshining.interceptor;
-
- import com.opensymphony.xwork2.ActionInvocation;
- import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
-
- public class MyInterceptor3 extends MethodFilterInterceptor {
-
- @Override
- protected String doIntercept(ActionInvocation invocation) throws Exception {
- String result = invocation.invoke();
- System.out.println("doing");
- return result;
- }
- }
在struts.xml中聲明攔截器
- <interceptor name="myinterceptor3" class="cn.tshining.interceptor.MyInterceptor3"></interceptor>
然后在 Action中引用此攔截器。
首先查看MethodFilterinterceptor,它接受兩個(gè)參數(shù),
excludeMethods:排除的方法,多個(gè)方法間用逗號(hào)隔開
includeMethods:包含的方法,多個(gè)方法間用逗號(hào)隔開
在 struts.xml中如下配置
- <interceptor-ref name="myinterceptor3">
- <param name="excludeMethods">test,execute</param>
- <param name="includeMethods">test</param>
- </interceptor-ref>
那么該攔截其將會(huì)攔截test方法,因?yàn)閕nclude比exclude優(yōu)先級(jí)高
在 com.opensymphony.xwork2.interceptor包里面還有一個(gè)重要的接口PreResultListener,查看文檔。
* PreResultListeners may be registered with an ActionInvocation to get a callback after the Action has been executed but before the Result is executed.
注釋:
PreResultListeners 可以注冊(cè)到ActionInvocation,當(dāng)Action執(zhí)行完畢但是在返回結(jié)果之前調(diào)用。
也就是在action的execute執(zhí)行結(jié)束之后,在渲染頁(yè)面前調(diào)用。
首先實(shí)現(xiàn)自己的PreResultListeners類
- package cn.tshining.interceptor;
-
- import com.opensymphony.xwork2.ActionInvocation;
- import com.opensymphony.xwork2.interceptor.PreResultListener;
-
- public class MyListener implements PreResultListener {
-
- public void beforeResult(ActionInvocation invocation, String resultCode) {
- System.out.println(resultCode);
- }
- }
修改自己的攔截器MyInterceptor3中的 doIntercept方法。
- protected String doIntercept(ActionInvocation invocation) throws Exception {
- invocation.addPreResultListener(new MyListener());
- String result = invocation.invoke();
- System.out.println("doing");
- return result;
- }
注意PreResultListeners必須向 ActionInvocation實(shí)例去注冊(cè),而且注冊(cè)要在調(diào)用ActionInvocation實(shí)例的invoke方法之前。
當(dāng)在 register2.jsp中輸入正確的數(shù)據(jù)時(shí),在控制臺(tái)上可以看到輸出的success信息。
該執(zhí)行過(guò)程為:
攔截器 ——validate——execute——beforeResult——結(jié)束
攔截器的應(yīng)用
用攔截器進(jìn)行權(quán)限校驗(yàn)
首先分析一下這個(gè)示例的工作流程:
首先用戶從login2.jsp登陸,只有username輸入hello,password輸入world,然后跳轉(zhuǎn)到register2.jsp進(jìn)行注冊(cè)操作。
如果用戶直接訪問(wèn)register2.jsp,提交后將直接跳轉(zhuǎn)回login2.jsp。
1.首先我們修改struts2.xml,讓用戶在login2.jsp中輸入正確頁(yè)面之后,將頁(yè)面轉(zhuǎn)到register2.jsp。
- <action name="login" class="cn.tshining.action.LoginAction">
- <result name="success">/register2.jsp</result>
- <result name="input">/login2.jsp</result>
- <result name="failer">/login2.jsp</result>
- </action>
2.修改LoginAction類中execute方法
- public String execute() throws Exception {
-
- if ("hello".equals(this.getUsername())
- && "world".equals(this.getPassword())) {
- Map<String,Object> map = ActionContext.getContext().getSession();
- map.put("user", "login");
- return "success";
- } else {
- this.addFieldError("username", "username or password error");
- return "failer";
- }
-
- }
在該方法中,若用戶輸入正確信息后,會(huì)獲得session對(duì)象,并把 user和login存到session中。
struts2 的Action中獲得session對(duì)象的方法是
Map map = ActionContext.getContext().getSession();
struts 將session從servlet容器中抽離出來(lái)了,形成了一個(gè)map,更利于單元測(cè)試
3.新建一個(gè)攔截器。
- package cn.tshining.interceptor;
-
- import java.util.Map;
-
- import com.opensymphony.xwork2.Action;
- import com.opensymphony.xwork2.ActionInvocation;
- import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
-
- public class AuthInterceptor extends AbstractInterceptor {
-
- @Override
- public String intercept(ActionInvocation invocation) throws Exception {
- Map<String,Object> map = invocation.getInvocationContext().getSession();
- if(map.get("user") == null){
- return Action.LOGIN;
- }else{
- return invocation.invoke();
- }
- }
- }
在這個(gè)攔截器中,獲得session對(duì)象的方法是
- Map<String,Object> map = invocation.getInvocationContext().getSession();
因此,當(dāng)session中不存在user的值為login時(shí),攔截器將返回Action.LOGIN,在struts.xml中返回結(jié)果為login將跳轉(zhuǎn)到 login2.jsp。
這樣便可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的用戶校驗(yàn)了。
4.配置action
- <action name="register2" class="cn.tshining.action.RegisterAction">
- <result name="success">/success.jsp</result>
- <result name="input">/register2.jsp</result>
- <result name="login">/login2.jsp</result>
- <interceptor-ref name="logininterceptor"></interceptor-ref>
- <interceptor-ref name="defaultStack"></interceptor-ref>
- </action>
這樣就可以正常工作了。
當(dāng)然也可以不在Action中定義login的結(jié)果跳轉(zhuǎn),而直接在全局結(jié)果中配置。將下面代碼放到Action配置前即可正常工作。
- <global-results>
- <result name="login">/login2.jsp</result>
- </global-results>
這樣就定義了一個(gè)全局結(jié)果頁(yè)面。
直接進(jìn)入到register2.jsp,點(diǎn)提交后,會(huì)發(fā)現(xiàn)地址欄跳到了http://localhost:8888/struts2 /register.action
結(jié)果重定向
前面在結(jié)果轉(zhuǎn)發(fā)時(shí)都是用到了請(qǐng)求轉(zhuǎn)發(fā),實(shí)際上struts2定義了很多結(jié)果類型,我們可以使用重定向試試。
打開 struts2-core-2.1.8.jar,查看里面的struts-default.xml,其中定義了很多結(jié)果類型。
其中的 redirect代表的就是重定向。
然后將上面Action配置中作如下修改:
- <result name="login" type="redirect">/login2.jsp</result>
然后重啟服務(wù)器,直接進(jìn)入到register2.jsp,點(diǎn)提交后,會(huì)發(fā)現(xiàn)地址欄跳到了http://localhost:8888/struts2 /login2.jsp
這就是重定向。在struts2中默認(rèn)是dispatcher。
下次將學(xué)習(xí)struts2的文件上傳與下載。