攔截器Interceptor.....攔截器是Struts的概念,它與過濾器是相似的...能夠近似於看做是過濾器html
前面在介紹Struts的時候已經講解過了,Struts爲咱們實現了不少的功能,好比數據自動封裝阿..文件上傳功能阿....Struts爲咱們提供的這些功能都是經過攔截器完成的......java
<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
這個攔截器。<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
這個攔截器攔截器的設計就是基於組件設計的應用!mysql
在開始講解Struts的時候已經說明過了struts-default.xml這個文件,它定義了Struts的全部攔截器。由於咱們在啓動服務器的時候會自動裝載這個文件,所以咱們才能夠在Action中使用到Struts爲咱們提供的功能【數據自動封裝...文件上傳】web
在struts-default.xml中定義的攔截器就有32個之多,Struts2爲了方便咱們對攔截器的引用,提供了攔截器棧的定義。sql
<interceptor-stack name="defaultStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="i18n"/> <interceptor-ref name="prepare"/> <interceptor-ref name="chain"/> <interceptor-ref name="scopedModelDriven"/> <interceptor-ref name="modelDriven"/> <interceptor-ref name="fileUpload"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="multiselect"/> <interceptor-ref name="staticParams"/> <interceptor-ref name="actionMappingParams"/> <interceptor-ref name="params"> <param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param> </interceptor-ref> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="debugging"/> </interceptor-stack>
也就是說:當咱們要引用多個攔截器的時候,只要把攔截器都放在棧裏頭,在外邊引用攔截器便可!數據庫
值得注意的是:Struts2默認執行的是默認攔截器棧,一旦用戶有指定執行哪些攔截器,那麼默認的攔截器棧就不會被執行!apache
Struts2容許咱們自定義攔截器,這就使咱們可以更加靈活地操做Struts2這個框架了!小程序
Struts2提供了Interceptor這個攔截器接口,只要咱們實現這個接口,那麼這就算是自定義開發攔截器了。服務器
固然啦,大部分時候,咱們定義攔截器都是繼承AbstractInterceptor這個類....爲了學習攔截器的內容,下面就實現Interceptor這個接口了。微信
public class MyInterceptor implements Interceptor { @Override public void destroy() { } @Override public void init() { } @Override public String intercept(ActionInvocation actionInvocation) throws Exception { return null; } }
init()和destory()都是和攔截器執行順序有關的方法,咱們如今先不理會....首先來說解intercept這個方法
/** * @param actionInvocation 攔截器的執行狀態 */ @Override public String intercept(ActionInvocation actionInvocation) throws Exception { //調用invoke()方法,表明着放行執行下一個攔截器,若是沒有攔截器了,那麼就執行Action的業務代碼 actionInvocation.invoke(); return null; }
這很容易就能讓咱們想起在學習過濾器中的doFilter()方法,實際上是差很少的!
像Struts默認的攔截器同樣,咱們自定義的攔截器是須要咱們在struts中配置的。
因爲咱們配置了自定義攔截器,那麼struts默認的攔截器棧是不會執行的。若是咱們想要使用默認攔截器棧的功能,就必須把它配置在咱們自定義的棧中!
<package name="xxx" extends="struts-default" > <interceptors> <!--配置用戶自定義的攔截器--> <interceptor name="MyInterceptor" class="TestAction"/> <!--自定義攔截器棧,咱們配置了自定義的攔截器,默認的攔截器棧就不會被執行,所以,想要使用默認的攔截器功能,就要配置進來--> <interceptor-stack name="mystack"> <!--引用默認的攔截器棧,必定要放在第一行--> <interceptor-ref name="defalutStack"/> <!--引用自定義的攔截器--> <interceptor-ref name="MyInterceptor"/> </interceptor-stack> </interceptors> <!--上面配置了攔截器棧,可是沒有被執行...下面配置執行攔截器--> <default-interceptor-ref name="mystack"/> <action name="TestAction" class="TestAction" method="execute"> <result name="success">/index.jsp</result> </action> </package>
咱們來觀察攔截器和Action類的執行順序...只要在對應的方法上向控制檯輸出就好了!
public class MyInterceptor implements Interceptor { @Override public void destroy() { System.out.println("我是攔截器的銷燬方法"); } @Override public void init() { System.out.println("我是攔截器的初始化方法"); } /** * @param actionInvocation 攔截器的執行狀態 */ @Override public String intercept(ActionInvocation actionInvocation) throws Exception { System.out.println("我是攔截器的攔截方法"); //調用invoke()方法,表明着放行執行下一個攔截器,若是沒有攔截器了,那麼就執行Action的業務代碼 //可當作是過濾器的doFilter()方法 actionInvocation.invoke(); return null; } }
public class TestAction extends ActionSupport { public TestAction() { System.out.println("我是Action類,我被初始化了!"); } @Override public String execute() throws Exception { System.out.println("我是Action類的執行方法"); return null; } }
從效果圖咱們能夠看出,他們的執行順序是這樣的:
其實很好理解,以前咱們使用Struts爲咱們提供數據自動封裝功能的時候,是這樣子的:
需求:當用戶登錄成功,跳轉到顯示用戶的JSP頁面中。當用戶登錄失敗,從新返回登錄界面。若是用戶直接訪問顯示用戶的JSP頁面,那麼返回到登錄界面
實現這個需求,咱們可使用過濾器的。只要獲取用戶的請求URL,再判斷URL是否是爲list.jsp,若是是,咱們返回到登錄的界面就行了。
如今,爲了對攔截器的理解,咱們使用攔截器去完成這個功能!
package zhongfucheng.entity; /** * Created by ozc on 2017/5/3. */ public class User { private String id ; private String username; private String cellphone; private String email; private String password; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getCellphone() { return cellphone; } public void setCellphone(String cellphone) { this.cellphone = cellphone; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
package zhongfucheng.dao; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import zhongfucheng.entity.User; import zhongfucheng.utils.Utils2DB; import java.sql.SQLException; import java.util.List; /** * Created by ozc on 2017/5/3. */ public class UserDao { public User login(User user) { try { String sql = "SELECT * FROM user WHERE username = ? AND password = ?"; QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource()); return (User) queryRunner.query(sql, new BeanHandler(User.class), new Object[]{user.getUsername(), user.getPassword()}); } catch (SQLException e) { new RuntimeException("登錄失敗了!"); } return null; } public List<User> getAll() { try { String sql = "SELECT * FROM user"; QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource()); return (List<User>) queryRunner.query(sql, new BeanListHandler(User.class)); } catch (SQLException e) { new RuntimeException("登錄失敗了!"); } return null; } }
public class Service { UserDao userDao = new UserDao(); public User login(User user) { return userDao.login(user); } public List<User> getAll() { return userDao.getAll(); } }
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>登錄頁面</title> </head> <body> <form action="${pageContext.request.contextPath}/user_login" method="post"> <input type="text" name="username"><br> <input type="password" name="password"><br> <input type="submit" value="登錄"><br> </form> </body> </html>
package zhongfucheng.action; import com.opensymphony.xwork2.ActionContext; import zhongfucheng.entity.User; import zhongfucheng.service.Service; import java.util.List; import java.util.Map; /** * Created by ozc on 2017/5/3. */ public class UserAction { /****************1.封裝數據********************/ private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } /***************2.調用Service*******************/ Service service = new Service(); //登錄 public String login() { User user = service.login(this.user); if (user == null) { return "input"; } else { //將user的信息存到Session域對象中 Map<String, Object> session = ActionContext.getContext().getSession(); session.put("user", user); //登錄成功 return "login"; } } //查看user信息 public String list() { //拿到全部用戶的信息 List<User> users = service.getAll(); //存到request域對象中 Map<String, Object> request = ActionContext.getContext().getContextMap(); request.put("users", users); return "list"; } }
<package name="xxx" extends="struts-default" > <action name="user_*" class="zhongfucheng.action.UserAction" method="{1}" > <!--若是登錄成功,重定向到Action中,執行list業務方法--> <result name="login" type="redirectAction">user_list</result> <!--若是是list,那麼跳轉到list.jsp頁面--> <result name="list" >/WEB-INF/list.jsp</result> </action> </package>
到目前爲止,咱們登錄或者不登錄均可以獲得用戶的具體信息....這是不合理的
咱們想要的效果是:只有用戶正在調用login方法,或者該用戶已經登錄了,才能夠查看具體的用戶信息。
所以,咱們們要攔截它們,只有用戶調用的是login方法時或者已經登錄的狀況下,才能跳轉到對應的顯示頁面
package zhongfucheng; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.ActionProxy; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; /** * Created by ozc on 2017/5/3. */ public class Interceptor extends AbstractInterceptor{ @Override public String intercept(ActionInvocation actionInvocation) throws Exception { //獲得正在執行的代理對象 ActionProxy proxy = actionInvocation.getProxy(); //經過代理對象獲得正在執行的方法 String method = proxy.getMethod(); //若是方法的名字不是login,那麼就讓他們返回到login頁面上 if (!method.equals("login")) { //查看用戶是否登錄了 Object user = ActionContext.getContext().getSession().get("user"); //若是沒有登錄,回到login頁面 if (user == null) { return "input"; } else { //登錄了,那麼就讓它訪問具體的用戶信息頁面 return actionInvocation.invoke(); } } else { //若是是訪問login方法,那麼就讓它執行 return actionInvocation.invoke(); } } }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="xxx" extends="struts-default"> <interceptors> <!--配置自定義的攔截器--> <interceptor name="Interceptor1" class="zhongfucheng.Interceptor"/> <!--配置攔截器棧,把默認的攔截器棧都加載自定義的攔截器棧中--> <interceptor-stack name="myStack"> <interceptor-ref name="Interceptor1"/> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors> <!--讓Struts執行攔截器--> <!--【執行攔截器:第一種寫法: 當前包下全部的acntion都執行myStack棧】--> <default-interceptor-ref name="myStack"></default-interceptor-ref> <!--第二種寫法: 只是在這一個Action中執行myStack棧 <interceptor-ref name="defaultStackt"></interceptor-ref> <interceptor-ref name="loginCheck"></interceptor-ref> --> <!-- 第三種寫法:執行用戶棧(與第二種寫法同樣, 只在當前aciton中執行自定義棧) --> <!-- <interceptor-ref name="myStack"></interceptor-ref>--> <action name="user_*" class="zhongfucheng.action.UserAction" method="{1}"> <!--若是登錄成功,重定向到Action中,執行list業務方法--> <result name="login" type="redirectAction">user_list</result> <!--若是是list,那麼跳轉到list.jsp頁面--> <result name="list">/WEB-INF/list.jsp</result> <!--若是是直接訪問Action或者沒有用戶登錄,返回login頁面--> <result name="input">/login.jsp</result> </action> </package> </struts>
只有當用戶登錄了才能查看用戶具體信息,直接訪問Action會跳轉回
Struts2自帶了計時攔截器,也就是用來統計每一個Action執行的時間
若是頁面執行得太慢了,Struts2還提供了執行等待攔截器,也就是說,當頁面加載得過久了,就跳轉到對應的提示頁面...當服務器執行完畢了,也跳轉到相對應的頁面
當咱們學習Session的時候已經經過Session來編寫了一個防止表單重複提交的小程序了,咱們來回顧一下咱們當時是怎麼作的:
咱們之前寫表達重複提交就花了這麼幾個步驟...若是有興趣的同窗能夠看一下之前的實現思路:http://blog.csdn.net/hon_3y/article/details/54799494#t11
Struts2是簡化咱們的開發的,表單重複提交也是一件很是經常使用的功能...Struts2也爲咱們實現了...固然啦,也是經過攔截器來實現
<interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
它的實現原理和咱們之前寫的思路幾乎一致...它不須要另外寫一個組件來生成token值,struts2標籤就有這麼一個功能...所以是十分方便的
爲了熟悉一下Struts2,咱們也使用Struts2來編寫一下上圖的程序...
package zhongfucheng.dao; import org.apache.commons.dbutils.QueryRunner; import org.apache.commons.dbutils.handlers.BeanHandler; import org.apache.commons.dbutils.handlers.BeanListHandler; import zhongfucheng.entity.User; import zhongfucheng.utils.Utils2DB; import java.sql.SQLException; import java.util.List; /** * Created by ozc on 2017/5/3. */ public class UserDao { public void add(User user) { try { String sql = "INSERT INTO user(id,username,cellphone,password,address) VALUES (?,?,?,?,?)"; QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource()); queryRunner.update(sql, new Object[]{user.getId(), user.getUsername(), user.getCellphone(), user.getPassword(),user.getAddress()}); } catch (SQLException e) { new RuntimeException("登錄失敗了!"); } } public User findUser(String id) { try { String sql = "SELECT * FROM user WHERE id=?"; QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource()); return (User) queryRunner.query(sql, new BeanHandler(User.class), new Object[]{id}); } catch (SQLException e) { new RuntimeException("登錄失敗了!"); } return null; } public List<User> getAll() { try { String sql = "SELECT * FROM user"; QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource()); return (List<User>) queryRunner.query(sql, new BeanListHandler(User.class)); } catch (SQLException e) { new RuntimeException("登錄失敗了!"); } return null; } public void updateUser(User user) { try { String sql = "UPDATE user SET username=?,password=?,cellphone=? WHERE id=?"; QueryRunner queryRunner = new QueryRunner(Utils2DB.getDataSource()); queryRunner.update(sql, new Object[]{user.getUsername(), user.getPassword(), user.getCellphone(), user.getId()}); } catch (SQLException e) { new RuntimeException("登錄失敗了!"); } } }
package zhongfucheng.service; import zhongfucheng.dao.UserDao; import zhongfucheng.entity.User; import zhongfucheng.utils.WebUtils; import java.util.List; /** * Created by ozc on 2017/5/3. */ public class Service { UserDao userDao = new UserDao(); public void add(User user) { //手動設置id,由於在數據庫表我沒使用自動增加id user.setId(WebUtils.makeId()); //這是之前的表,規定要address,只能手動設置了 user.setAddress("廣州"); userDao.add(user); } public User findUser(String id) { return userDao.findUser(id); } public List<User> getAll() { return userDao.getAll(); } public void updateUser(User user) { userDao.updateUser(user); } }
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@taglib prefix="s" uri="/struts-tags" %> <html> <head> </head> <body> <form action="${pageContext.request.contextPath}/user_register" method="post"> <table border="1"> <tr> <td>用戶名:<input type="text" name="username"></td> </tr> <tr> <td> 密碼:<input type="password" name="password"></td> </tr> <tr> <td>電話:<input type="text" name="cellphone"></td> </tr> <tr> <td><input type="submit" value="提交"></td> </tr> </table> </form> </body> </html>
//這裏必定要實例化 User user = new User(); public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public User getModel() { return user; } /*******調用service********/ Service service = new Service(); public String register() throws Exception { service.add(user); //註冊成功,就跳轉到list()方法,list方法就跳轉到查看全部用戶頁面了! return list(); }
<%-- Created by IntelliJ IDEA. User: ozc Date: 2017/5/2 Time: 18:24 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>列出下載頁面</title> </head> <body> <table border="1" align="center"> <tr> <td>用戶id</td> <td>用戶姓名</td> <td>用戶密碼</td> <td>用戶電話</td> <td>操做</td> </tr> <s:if test="#request.users!=null"> <c:forEach items="${users}" var="user"> <tr> <td>${user.id}</td> <td>${user.username}</td> <td>${user.password}</td> <td>${user.cellphone}</td> <td><a href="${pageContext.request.contextPath}/user_updatePage?id=${user.id}">修改</a></td> </tr> </c:forEach> </s:if> </table> </body> </html>
public String updatePage() throws Exception { //獲得用戶帶過來的id,根據id查找對象 User user222 = service.findUser(user.getId()); ActionContext.getContext().getValueStack().push(user222); return "updatePage"; }
<form action="${pageContext.request.contextPath}/user_update"> <table border="1"> <tr> <td>用戶名<s:textfield name="username"/></td> </tr> <tr> <td>密碼 <s:textfield name="password" /></td> </tr> <tr> <td>電話<s:textfield name="cellphone"/></td> </tr> <s:hidden name="id"/> <tr> <td><input type="submit" value="修改"></td> </tr> </table> </form>
上面咱們已經完成了大部分的功能了,但當咱們若是提交以後,再刷新頁面,那麼表單的數據就會重複提交...咱們使用Struts2咱們提供的防止表單重複提交的功能把!
<table border="1"> <s:token></s:token> <tr> <td>用戶名:<input type="text" name="username"></td> </tr> <tr> <td> 密碼:<input type="password" name="password"></td> </tr> <tr> <td>電話:<input type="text" name="cellphone"></td> </tr> <tr> <td><input type="submit" value="提交"></td> </tr> </table>
token攔截器默認是不會啓動的,也就是說:須要咱們手動配置...
當咱們配置攔截器的時候,Struts2默認的攔截器是不會執行的,因此要把Struts2默認的攔截器也寫上
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.ui.theme" value="simple"/> <package name="xxx" extends="struts-default"> <action name="user_*" class="zhongfucheng.action.UserAction" method="{1}"> <interceptor-ref name="defaultStack"/> <interceptor-ref name="token"> <!-- 要攔截的方法! --> <param name="includeMethods">register</param> </interceptor-ref> <!--若是是list,那麼就跳轉到list的JSP頁面--> <result name="list"> /list.jsp</result> <!--請求跳轉到修改頁面--> <result name="updatePage">/update.jsp</result> <!--若是校驗成功,跳轉到login.jsp頁面回顯--> <result name="success">/login.jsp</result> <result name="redirectList" type="redirect">/user_list</result> </action> </package> <include file="config.xml"/> </struts>
若是文章有錯的地方歡迎指正,你們互相交流。 習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y