攔截器可謂struts2的核心了,最基本的bean的注入就是通過默認的攔截器實現的,一般在struts2.xml的配置中,package內直接或間接繼承了struts-default.xml,這樣struts2默認的攔截器就會作用.下面詳細的說明一下:
Interceptor攔截器類似於過濾器,是可以在action執行前后執行的代碼。是我們做web開發時經常用的技術。比如:權限控制、日志等。我們也可以將多個Interceptor連在一起組成Interceptor棧。
Struts2攔截器,每個攔截器類只有一個對象實例,即采用單例模式,所以引用這個攔截器的Action都共享這一攔截器類的實例,因此,在攔截器中如果使用類變量,要注意同步問題。
實現原理 : Struts2攔截器的實現原理相對簡單,當請求struts2的action時,Struts 2會查找配置文件,並根據其配置實例化相對應攔截器對象,然后串成一個列表,最后一個一個地調用列表中的攔截器。
攔截器和過濾器的區別:
1. 攔截器和過濾器的概念非常類似。
2. 過濾器隸屬於web容器,可以過濾一切請求(包括action、servlet、jsp、html等等)。
3. 而攔截器隸屬於struts2框架,只能攔截action(無法攔截對jsp的請求)。
4. 過濾器內部采用函數回調來實現。攔截器采用動態代理來實現!
攔截器在struts2中的應用:
對於Struts2框架而言,正是大量的內置攔截器完成了大部分操作。比如:
– 像params攔截器將http請求中參數解析出來賦值給Action中對應的屬性。
– Servlet-config攔截器負責把請求中HttpServletRequest實例和HttpServletResponse實例傳遞給Action
– …
struts-default.xml中有一個默認的引用,在默認情況下(也就是<action>中未引用攔截器時)會自動引用一些攔截器。
常用攔截器:token
作用:防止表單重復提交攔截器
使用方法:配置struts2.xml+重復提交處理+jsp頁面表單內插入<s:token></s:token>標簽;其中struts.xml的配置是核心
(1)先來看一下最基本的struts.xml的配置:
1 <package name="test" namespace="/" extends="struts-default"> 2 <action name="testValidate" class="com.bjsxt.struts.test.TestValidateAction"> 3 <interceptor-ref name="token"></interceptor-ref> 4 <result name="success">/ok.jsp</result> 5 <result name="input">/testFormLabel.jsp</result> 6 <result name="invalid.token">/tokenInvalid.jsp</result> 7 </action> 8 </package>
說明:
1.代碼第三行代表引入一個struts2自帶的攔截器token,此操作會使默認的攔截器失效,即無法使用其他功能,自定義攔截器再詳細說明;
2.代碼第六行為基本固定的格式,result的name屬性值是固定的,后面的/tokenInvalid.jsp可以根據需求自己定義
3.此token攔截器會對該action下的所有請求作用,最關鍵的一點!即:所有該action的請求都會經過token的攔截處理,自定義攔截器時再說明;
(2)form表單中需加入<s:token></s:token>標簽:
1 <s:form action="testValidate" > 2 <s:textfield name="uname" ></s:textfield> 3 <s:token></s:token> 4 <s:submit></s:submit> 5 </s:form>
說明:不一定是s標簽的表單,普通表單中插入<s:token></s:token>也可以實現攔截的效果.
(3)處理措施,我的是跳到一個錯誤頁面:testFormLabel.jsp
1 <%@ page language="java" import="java.util.*" pageEncoding="gbk"%> 2 <h1>表單不能重復提交!!</h1>
--------------------------------------------------------------------------------------------------------------------------------------------------------
自定義攔截器:
作為“框架(framework)”,可擴展性是不可或缺的,因為世上沒有放之四海而皆准的東西。雖然,Struts 2為我們提供如此豐富的攔截器實現,但是這並不意味我們失去創建自定義攔截器的能力,恰恰相反,在Struts 2自定義攔截器是相當容易的一件事。
大家在開始着手創建自定義攔截器前,切記以下原則:
攔截器必須是無狀態的,不要使用在API提供的ActionInvocation之外的任何東西。要求攔截器是無狀態的原因是Struts 2不能保證為每一個請求或者action創建一個實例,所 以如果攔截器帶有狀態,會引發並發問題。
條件:
1. 直接或間接實現接口com.opensymphony.xwork2.interceptor.Interceptor或者繼承類com.opensymphony.xwork2.interceptor.AbstractInterceptor
2.通過<interceptor>元素來定義攔截器
3.通過<interceptor-ref>元素來使用攔截器
4.自定攔截器的java類並重寫public String intercept(ActionInvocation ai) throws Exception
注意:如果為Action指定了一個攔截器,則系統默認的攔截器棧將會失去作用。為了繼續使用默認攔截器,所以上面配置文件中手動引入了默認攔截器
步驟:
1.新建一個class,用於實現自定義攔截器功能,這兒就實現最為普遍的登陸驗證,即防止未經登陸的情況下,訪問action;先說一下原理:
首先,用戶登陸后,經過action驗證用戶名和密碼是否正確,如果正確則在session作用域內存放user對象,否則跳到登陸頁面並提示用戶名或密碼不正確,而且session作用域中user的值是沒有設置的.然后,由於在該action下使用了自定義的攔截器(struts.xml配置),訪問該action的所有請求都會被攔截,驗證session中user屬性的值,如果值不存在,則跳到登陸頁面並提示無權操作,否則執行invoke方法,即請求會被action正常響應,並返回相應的結果.
代碼體現如下:
自定義攔截器類:
1 package com.bjsxt.struts2.exercise.interceptor; 2 3 import java.util.Map; 4 5 import com.bjsxt.struts2.exercise.vo.Users; 6 import com.opensymphony.xwork2.Action; 7 import com.opensymphony.xwork2.ActionContext; 8 import com.opensymphony.xwork2.ActionInvocation; 9 import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor; 10 11 public class LoginInterceptor extends MethodFilterInterceptor{ 12 @Override 13 protected String doIntercept(ActionInvocation invocation) throws Exception { 14 //獲取session對象(經過struts2包裝過) 15 Map session = ActionContext.getContext().getSession(); 16 //獲取session作用域內是否有值 17 Users user = (Users) session.get("user"); 18 if(user!=null){//合法訪問 19 return invocation.invoke(); 20 }else{//user為空說明未經過登陸,保存錯誤提示信息,跳到登陸頁面 21 ActionContext.getContext().put("noright", "請先登陸再進行操作!"); 22 return Action.LOGIN; 23 } 24 } 25 }
user類:
1 package com.bjsxt.struts2.exercise.vo; 2 3 public class Users { 4 private Integer id; 5 private String name; 6 private String password; 7 public Integer getId() { 8 return id; 9 } 10 public void setId(Integer id) { 11 this.id = id; 12 } 13 public String getName() { 14 return name; 15 } 16 public void setName(String name) { 17 this.name = name; 18 } 19 public String getPassword() { 20 return password; 21 } 22 public void setPassword(String password) { 23 this.password = password; 24 } 25 }
2.配置struts.xml,使自定義攔截器作用:
1 <package name="move" namespace="/move" extends="struts-default"> 2 <interceptors> 3 <interceptor name="loginInteceptor" class="com.bjsxt.struts2.exercise.interceptor.LoginInterceptor"></interceptor> 4 </interceptors> 5 <global-results> 6 <result name="login" >/jsp/movebooking/login.jsp</result> 7 </global-results> 8 <action name="moveBookingAction" class="com.bjsxt.struts2.exercise.action.MoveBookingAction" > 9 <interceptor-ref name="loginInteceptor"> 10 <param name="excludeMethods">addMoveInfo</param> 11 </interceptor-ref> 12 <interceptor-ref name="token"> 13 <param name="includeMethods">addMoveInfo</param> 14 </interceptor-ref> 15 <interceptor-ref name="defaultStack"></interceptor-ref> 16 <result name="queryList" >/jsp/movebooking/movelist.jsp</result> 17 <result name="queryInfo" >/jsp/movebooking/moveinfo.jsp</result> 18 <result name="update" >/jsp/movebooking/updateinfo.jsp</result> 19 <result name="afterupdate" type="redirectAction" >moveBookingAction!queryMoveList</result> 20 <result name="success">/jsp/movebooking/success.jsp</result> 21 <result name="invalid.token">/jsp/movebooking/tokenInvalid.jsp</result> 22 </action> 23 24 <action name="userAction" class="com.bjsxt.struts2.exercise.action.UserAction"> 25 <result name="success" type="redirectAction" >moveBookingAction!queryMoveList</result> 26 <result name="failed" type="redirect">/jsp/movebooking/login.jsp 27 </result> 28 </action> 29 </package>
說明:
(1)使用自定義攔截器會使默認攔截器失效,所以需要手動引入:代碼第15行;當然上述代碼引入攔截器的方式也可以采用攔截器棧的方式,就不貼了;
(2)注意攔截器的順序,一般情況下,默認攔截器都是放在最后面的,權限驗證與token相比較,我覺得先驗證是否登陸更加必要;
(3)interceptor-ref內可以定義param元素,即實現對特定方法的攔截;但是注意,使用方法攔截器必須直接或間接實現AbstractInerceptor接口或者繼承
MethodFilterInterceptor類,並重寫doIntercept方法.param標簽內有兩個屬性:
excludeMethods:排除的方法
includeMethods:包含的方法
在本配置文件中,由於token攔截器的作用范圍為moveBookingAction下所有請求,而我在測試的時候就發現,該action下的所有請求都會被攔截到重復提交的錯誤頁面,原理尚不明白,有高手敬請指導(只有需要被作用的請求對應的jsp頁面的form表單內加入了token標簽),所以被逼無奈對token攔截的方法只限定在addMoveInfo方法中!
3.使用的action類和幾個jsp頁面:
UserAction:
1 package com.bjsxt.struts2.exercise.action; 2 3 import com.bjsxt.struts2.exercise.service.UserService; 4 import com.bjsxt.struts2.exercise.vo.Users; 5 import com.opensymphony.xwork2.Action; 6 import com.opensymphony.xwork2.ActionContext; 7 import com.opensymphony.xwork2.ActionSupport; 8 9 public class UserAction extends ActionSupport{ 10 private UserService userService =new UserService(); 11 private Users user; 12 public String queryUser() throws Exception{ 13 user = userService.queryUser(user); 14 // 將user存到session中,實現攔截器攔截未經登陸直接請求的url的功能 15 ActionContext.getContext().getSession().put("user", user); 16 if(user!=null){ 17 return Action.SUCCESS; 18 }else{ 19 ActionContext.getContext().getSession().put("wrong", "用戶名或密碼錯誤!"); 20 return "failed"; 21 } 22 } 23 24 public Users getUser() { 25 return user; 26 } 27 28 public void setUser(Users user) { 29 this.user = user; 30 } 31 32 }
MoveBookingAction:
1 package com.bjsxt.struts2.exercise.action; 2 3 import com.bjsxt.struts2.exercise.service.MoveBookingService; 4 import com.bjsxt.struts2.exercise.util.MyPageUtil; 5 import com.bjsxt.struts2.exercise.vo.MoveBookingVo; 6 import com.opensymphony.xwork2.ActionSupport; 7 8 public class MoveBookingAction extends ActionSupport{ 9 private MoveBookingService moveBookingService = new MoveBookingService(); 10 private MyPageUtil pu; 11 private Integer pageNum; 12 private MoveBookingVo moveBookingVo; 13 private String[] phone; 14 /** 15 * 搬家列表 16 * @return 17 * @throws Exception 18 */ 19 public String queryMoveList() throws Exception{ 20 pu = moveBookingService.queryMoveList(pageNum); 21 return "queryList"; 22 } 23 24 /** 25 * 搬家信息詳情 26 * @return 27 * @throws Exception 28 */ 29 public String queryMoveInfo() throws Exception{ 30 moveBookingVo = moveBookingService.queryMoveInfo(moveBookingVo); 31 return "queryInfo"; 32 } 33 34 /** 35 * 搬家詳情,跳到update頁面 36 * @return 37 * @throws Exception 38 */ 39 public String queryForUpdate() throws Exception{ 40 moveBookingVo = moveBookingService.queryMoveInfo(moveBookingVo); 41 return "update"; 42 } 43 44 /** 45 * 修改搬家信息 46 * @return 47 * @throws Exception 48 */ 49 public String updateMoveInfo() throws Exception{ 50 moveBookingService.updateMoveInfo(moveBookingVo); 51 return "afterupdate"; 52 } 53 /** 54 * 搬家預約 55 * @return 56 * @throws Exception 57 */ 58 public String addMoveInfo() throws Exception{ 59 moveBookingVo.setStatus("0"); 60 moveBookingVo.setPhone(phone[0]+phone[1]); 61 moveBookingService.addMoveInfo(moveBookingVo); 62 return "success"; 63 } 64 65 public MyPageUtil getPu() { 66 return pu; 67 } 68 public void setPu(MyPageUtil pu) { 69 this.pu = pu; 70 } 71 public Integer getPageNum() { 72 return pageNum; 73 } 74 public void setPageNum(Integer pageNum) { 75 this.pageNum = pageNum; 76 } 77 public MoveBookingVo getMoveBookingVo() { 78 return moveBookingVo; 79 } 80 public void setMoveBookingVo(MoveBookingVo moveBookingVo) { 81 this.moveBookingVo = moveBookingVo; 82 } 83 84 public String[] getPhone() { 85 return phone; 86 } 87 88 public void setPhone(String[] phone) { 89 this.phone = phone; 90 } 91 }
login.jsp:
1 <body> 2 <font size="5"><b>管理員登陸</b></font><br> 3 <form id="f1" action="move/userAction!queryUser"> 4 <input type="hidden" id="flag" name="flag" value="${flag }"/> 5 用戶名: 6 <input id="uname" name="user.name" onclick="clearMsg()" /> 7 密碼: 8 <input id="pwd" type="password" name="user.password" onclick="clearMsg()" /> 9 <input type="button" style="background-color: lightgray;width: 100px;" value="登陸" onclick="submit_form()"/> 10 </form> 11 <div id="div" style="color: red">${wrong }${noright }</div> 12 </body>
tokenInvalid.jsp:
1 <body> 2 <h1>請勿重新提交</h1> 3 </body>