文章目錄
一、基礎
參考:https://mp.weixin.qq.com/s/witX9zNQvH7_qOjk8De3IA
1.1 含義
含義:Interceptor。攔截器是Struts的概念,它與過濾器是類似的。可以近似於看作是過濾器。都是AOP思想的實現。
1.2 作用
1、Struts爲我們實現了很多的功能(如數據自動封裝,文件上傳功能)。
2、Struts爲我們提供的這些功能都是通過攔截器完成的。
如:
<!-- 數據自動封裝通過ParametersInterceptor攔截器。-->
<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
<!-- 文件上傳通過FileUploadInterceptor攔截器。-->
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
3、攔截器的設計就是基於組件設計的應用!
1.3 struts-default.xml
作用:定義了Struts的所有攔截器(約32個)。在啓動服務器的時候會自動裝載這個文件,因此我們纔可以在Action中使用到Struts爲我們提供的功能【數據自動封裝、文件上傳】。
攔截器棧:存放多個攔截器的引用。爲了方便對攔截器的引用,Struts2提供了攔截器棧的定義。
若要引用多個攔截器,則攔截器都放在棧裏,在外邊引用攔截器棧即可!
注意:Struts2默認執行的是默認攔截器棧,一旦用戶有指定執行哪些攔截器,那麼默認的攔截器棧就不會被執行!
<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>
二、自定義攔截器
2.1 編寫攔截器類
方式一:實現Interceptor接口。
方式二:繼承AbstractInterceptor類。
public class MyInterceptor implements Interceptor {
@Override
public void destroy() {
}
@Override
public void init() {
}
// intercept()與過濾器中的doFilter()方法相似。
@Override
public String intercept(ActionInvocation actionInvocation) throws Exception {
// 調用invoke()方法,代表着放行執行下一個攔截器,如果沒有攔截器了,那麼就執行Action的業務代碼
actionInvocation.invoke();
return null;
}
}
2.2 在struts.xml中配置
注意:配置了自定義攔截器,則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>
2.3 攔截器的執行順序
攔截器的執行順序:
1、服務器開啓時,會執行攔截器的init()方法。
2、訪問Action時,Action實例被創建。
3、創建完Action實例時,會調用攔截器的interceptor()方法。
4、最後,執行Action的execute()方法
數據自動封裝的執行順序:
1、服務器啓動時,加載配置文件的信息。
2、初始化默認的攔截器棧。
3、當用戶訪問Action時,創建Action的實例。拿到Action具體的信息【成員變量、setter和getter】。
4、執行攔截器具體的內容,根據Action具體的信息,把web端的數據封裝到Action上
5、最後在execute()就可以得到封裝後的數據了!
三、攔截器應用案例1
功能:是否已經登錄。
需求:
1、當用戶登陸成功,跳轉到顯示用戶的JSP頁面中。
2、當用戶登陸失敗,重新返回登陸界面。
3、若用戶直接訪問顯示用戶的JSP頁面,那麼返回到登陸界面
需求分析:使用過濾器。獲取用戶的請求URL,判斷URL是不是爲list.jsp。若是,則返回登陸界面。
3.1 搭建配置環境
3.1.1 導入jar包
導入c3p0開發包:c3p0-0.9.2-pre1.jar
導入mysql開發包:mysql-connector-java-5.17-bin.jar
dbUtils開發包:commons-dbutils-1.2.jar
8個struts2需要用到的開發包:
——commons-fileupload-1.2.2.jar
——commons-io-2.0.1.jar
——commons-lang3-3.1.jar
——freemarker-2.3.19.jar
——javassist-3.11.0.GA.jar
——ognl-3.0.5.jar
——struts2-core-2.3.4.1.jar
——xwork-core-2.3.4.1.jar
導入JSTL開發包:
——jstl.jar
——standard.jar
3.1.2 編寫工具類
導入c3p0.xml文件
寫數據庫連接池工具類
Utils2DB.java
public class Utils2DB {
private static ComboPooledDataSource comboPooledDataSource = null ;
static{
// 會自動尋找配置文件,節點爲mysql的數據庫。
comboPooledDataSource = new ComboPooledDataSource();
}
public static Datasource getDatasource(){
return comboPooledDataSource;
}
public static Connection getConnection(){
//...
return conn;
}
}
創建數據庫表,導入數據。
3.2 entity層
public class User {
private String id ;
private String username;
private String cellphone;
private String email;
private String password;
}
3.3 DAO層
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;
}
}
3.4 Service層
public class Service {
UserDao userDao = new UserDao();
public User login(User user) {
return userDao.login(user);
}
public List<User> getAll() {
return userDao.getAll();
}
}
3.5 JSP頁面
<%@ 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>
3.6 Action層
public class UserAction {
/****************1.封裝數據********************/
private User user;
// setter/getter方法。
/***************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";
}
}
註冊action。
struts.xml
<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>
3.7 攔截器層
目標:只有用戶調用的是login方法時或者已經登陸的情況下,才能跳轉到對應的顯示頁面。
第一步:判斷是否請求的login.jsp頁面。若是,則放行。若否,則執行第二步。
第二步:判斷是否已經登錄。若是,則放行。若否,則返回input視圖。
MyInterceptor.java
public class MyInterceptor 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();
}
}
}
struts.xml
<?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="my" extends="struts-default">
<interceptors>
<!--配置自定義的攔截器-->
<interceptor name="Interceptor1" class="interceptor.MyInterceptor"/>
<!--配置攔截器棧,把默認的攔截器棧都加載自定義的攔截器棧中-->
<interceptor-stack name="myStack">
<interceptor-ref name="Interceptor1"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<!--讓Struts執行攔截器-->
<!--【執行攔截器:第一種寫法: 當前包下所有的acntion都執行myStack棧】-->
<default-interceptor-ref name="myStack"/>
<!--第二種寫法: 只是在這一個Action中執行myStack棧
<interceptor-ref name="defaultStackt"/>
<interceptor-ref name="loginCheck"/>
-->
<!-- 第三種寫法:執行用戶棧(與第二種寫法一樣, 只在當前aciton中執行自定義棧) -->
<!-- <interceptor-ref name="myStack"/>-->
<action name="user_*" class="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>
四、攔截器應用案例2
目的:防止表單重複提交。
4.1 Servlet方案
1、在Servlet上生成獨一無二的token,保存在Session域中,並交給JSP頁面。
2、JSP頁面在提交表單數據的時候,把token放在隱藏域中…一起帶過去給Servlet。
3、Servlet判斷用戶有沒有帶token值過來,判斷token的值是否和Session的相匹配。
4、如果用戶是第一次提交的話,那麼就允許用戶的請求,接着就把保存在Session中的token值去除。
5、等用戶想要再次提交的時候,Servlet發現Session中並沒有token了,所以不搭理用戶的請求。
4.2 Struts方案
Struts簡化4.1中的書寫代碼步驟,不需要另外寫一個組件來生成token值,struts2標籤就有這麼一個功能。
通過攔截器來實現。
<interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
4.3 Struts方案代碼編寫
4.3.1 DAO層
UserDao.java
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.SQLException;
import java.util.List;
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("登陸失敗了!");
}
}
}
4.3.2 service層
UserService.java
import my.utils.WebUtils;
public class Service {
UserDao userDao = new UserDao();
public void add(User user) {
user.setId(WebUtils.makeId());
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);
}
}
4.3.3 視圖層
在需要提交的表單上使用token標籤。
login.jsp
<%@ 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">
<s:token></s:token>
<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>
list.jsp
<%@ 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>
updatePage.jsp
<form action="${pageContext.request.contextPath}/user_update">
<s:token></s:token>
<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>
4.3.4 action層
UserAction.java
//這裏一定要實例化
User user = new User();
// setter/getter方法
@Override
public User getModel() {
return user;
}
/*******調用service********/
Service service = new Service();
public String register() throws Exception {
service.add(user);
//註冊成功,就跳轉到list()方法,list方法就跳轉到查看所有用戶頁面了!
return list();
}
public String updatePage() throws Exception {
//得到用戶帶過來的id,根據id查找對象
User user222 = service.findUser(user.getId());
ActionContext.getContext().getValueStack().push(user222);
return "updatePage";
}
4.3.5 配置攔截器
token攔截器默認是不會啓動的,需要我們手動配置。
<?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="my" extends="struts-default">
<action name="user_*" class="my.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>