一、拦截器基本概念
Intercetor, 即为拦截器。
1. Struts2的每一个功能都是由拦截器实现。
用户想使用哪一个功能,可以自由组装。
2. 为了方便对拦截器的引用,struts提供了拦截器栈的定义,
里面可以引用多个拦截器。
文件夹(文件, 文件2) 拦截器栈(拦截器,拦截器2)
3. struts指定了默认执行的拦截器栈(该栈中定义了18个拦截器)
如果用户没有指定拦截器,那么会自动执行该拦截器栈。
如果指定了拦截器,则执行指定的拦截器。而默认的拦截器栈就不会执行。
二、自定义一个拦截器 – 开发步骤
2.1 自定义一个拦截器
方式一 实现Interceptor接口
1.自定义拦截器类,必须实现Interceptor接口
public class MyIncepteror implements Interceptor{
@Override
public void destroy() {
}
@Override
public void init() {
System.out.println("1.初始化拦截器");
}
@Override
public String intercept(ActionInvocation in) throws Exception {
System.out.println("2.拦截器 -- 前置逻辑");
//执行方法,返回结果标记
String input=in.invoke();
System.out.println("2.拦截器 -- 后置逻辑");
return null;
}
}
方式一 继承AbstractInterceptor
public class UserInterceptor extends AbstractInterceptor{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public String intercept(ActionInvocation ac) throws Exception {
String method = ac.getProxy().getMethod();
//放行
if(method.equals("login")) {
return ac.invoke();
}
//验证
Object admin = ActionContext.getContext().getSession().get("userInfo");
//验证-失败
if(admin==null) {
return "loginFail";
}
//验证-放行
return ac.invoke();
}
}
2.2 在Struts.xml 配置拦截器
1.在struts.xml中配置拦截器
//1. 定义拦截器以及拦截器栈
<interceptors>
1.1 拦截器定义
<interceptor name="mm" class="" />
1.2 拦截器栈的定义
<interceptor-stack name="myStack">
<interceptor-ref name="mm"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
2.引用拦截器
+++ 在package节点下引入拦截器
<!-- 自定义拦截器 -->
<interceptors>
<interceptor name="myInterceptor" class="org.jsoft.interceptor.MyIncepteror"></interceptor>
<interceptor-stack name="myStack">
<interceptor-ref name="myInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 在package节点下引入默认执行的拦截器栈 -->
<default-interceptor-ref name="myStack"></default-interceptor-ref>
+++ 在action节点下引入拦截器
<package name="xxxx1" extends="struts-default" namespace="/" >
<!-- 自定义拦截器 -->
<interceptors>
<interceptor name="myInterceptor" class="org.jsoft.interceptor.MyIncepteror"></interceptor>
<interceptor-stack name="myStack">
<interceptor-ref name="myInterceptor"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<action name="emp_*" class="org.jsoft.action.EmpAction" method="{1}">
<!-- 加入默认拦截器、token拦截器 -->
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="token">
<!-- 哪些方法被令牌拦截器拦截 -->
<param name="includeMethods">addEmp</param>
</interceptor-ref>
</action>
</package>
2.2.1 在package节点下引入拦截器 – 拦截当前包下的所有action
将拦截器的引用在package节点下配置。
当前package下所有定义的action都会被拦截。
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="xxxx" extends="struts-default">
<!-- 1.自定义拦截器配置 -->
<interceptors>
<!-- 自定义拦截器 -->
<interceptor name="testInterceptor" class="org.jsoft.interceptor.HelloInterceptor"></interceptor>
<!-- 自定义拦截器栈 -->
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="testInterceptor"/>
</interceptor-stack>
</interceptors>
<!-- 2.指定执行的拦截器 -->
<default-interceptor-ref name="myStack"/>
<action name="loginOut" class="org.jsoft.action.LoginAction" method="loginOut">
<result name="loginOut" type="redirect">/login.jsp</result>
</action>
</package>
</struts>
2.2.2 在action节点下引入拦截器 – 拦截器拦截当前action
将拦截器的引用配置在action节点下配置。
只有当前action被拦截。
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="xxxx" extends="struts-default">
<!-- 1.自定义拦截器配置 -->
<interceptors>
<!-- 自定义拦截器 -->
<interceptor name="testInterceptor" class="org.jsoft.interceptor.HelloInterceptor"></interceptor>
<!-- 自定义拦截器栈 -->
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="testInterceptor"/>
</interceptor-stack>
</interceptors>
<action name="loginOut" class="org.jsoft.action.LoginAction" method="loginOut">
<!---2.自定义拦截器,只拦截当前action -->
<interceptor-ref name="myStack"></interceptor-ref>
<interceptor-ref name="testInterceptor"></interceptor-ref>
<result name="loginOut" type="redirect">/login.jsp</result>
</action>
</package>
</struts>
2.3 引入拦截器注意事项
1.在struts的package节点下使用该标签来引入拦截器。 (只能用一个标签)
<default-interceptor-ref name="myStack"/>
2.在struts的action节点下使用该标签来引入拦截器。(可以使用多个标签来引入多个拦截器)
<interceptor-ref name="testStak" />
<interceptor-ref name="defaultStack" />
3.在struts中指定了执行的拦截器,
那么默认的拦截器栈(18个拦截器)就不会执行了。
所以我们仍需要手动指定执行默认的拦截器栈。
+++ 方式一:
//1. 定义拦截器以及拦截器栈
<interceptors>
1.1 拦截器定义
<interceptor name="" class="" />
1.2 拦截器栈的定义
<interceptor-stack name="defaultStack">
引用了上面拦截器(1.1)
</interceptor-stack>
</interceptors>
//2.执行拦截器
<default-interceptor-ref name="myStack"/>
+++ 方式二:
<interceptor-ref name="testStak"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
三、自定义一个拦截器案例
HelloInterceptor.java
public class HelloInterceptor implements Interceptor{
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void init() {
// TODO Auto-generated method stub
}
@Override
public String intercept(ActionInvocation chain) throws Exception {
System.out.println("拦截器前置逻辑");
String falg = chain.invoke();
System.out.println("拦截器后置逻辑");
return falg;
}
}
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="xxxx" extends="struts-default">
<interceptors>
<!-- 自定义拦截器 -->
<interceptor name="testInterceptor" class="org.jsoft.interceptor.HelloInterceptor"></interceptor>
<!-- 自定义拦截器栈 -->
<interceptor-stack name="myStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="memberInterceptor"/>
<interceptor-ref name="testInterceptor"/>
</interceptor-stack>
</interceptors>
<!-- 指定执行的拦截器 -->
<default-interceptor-ref name="myStack"/>
<global-results>
<result name="login" type="redirect">/login.jsp</result>
</global-results>
<action name="login" class="org.jsoft.action.LoginAction" method="login">
<result name="success" type="redirect">/list</result>
<result name="error" type="redirect">/login.jsp</result>
</action>
</package>
</struts>
四、拦截器生命周期
public class MyIncepteror implements Interceptor{
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void init() {
System.out.println("1.初始化拦截器");
}
@Override
public String intercept(ActionInvocation in) throws Exception {
System.out.println("2.拦截器 -- 前置逻辑");
//执行方法,返回结果标记
String input=in.invoke();
System.out.println("2.拦截器 -- 后置逻辑");
return null;
}
}
4.1 拦截器的生命周期
Interceptor接口:
构造方法
init()
intercept(ActionInvocation chain)
destory()
+++ tomcat服务器启动
1.tomcat 服务器启动时,会自动加载web.xml配置文件
2.自动创建StrusPrepareAndExcuteFilter过滤器实例。
3.调用init(FilterConfig config)方法。传入过滤器参数。
4.该方法会自动初始化struts。
a.创建所有拦截器实例。
b.调用拦截器的init()方法,初始化拦截器。
+++ 用户请求action
1.用户发送请求首先会被过滤器进行拦截。
2.用户请求进入struts执行链。
--> a.创建action实例。
--> b.执行拦截器栈(拦截器的interceptor方法)
--> 执行拦截器前置逻辑(拦截器1前置-->拦截器2前置 ...)
--> 执行action方法。
--> 执行拦截器后置逻辑(拦截器1后置-->拦截器2后置 ...)
用户的每一次请求都会执行用户请求1- 2流程。
4.2 拦截器注意细节
1、如果用户请求aciton,会被拦截器进行拦截。
如果用户请求jsp和静态文件,则不会被拦截器进行拦截。
2、服务器内部action跳转action,则会被拦截器进行拦截。
服务器内部action跳转jsp和静态文件,则不会被拦截器进行拦截。
3、用户发送请求,首先创建action实例,然后才会被拦截器进行拦截。
--> 创建action实例
--> 执行拦截器前置逻辑
--> action业务方法
--> 执行拦截器后置逻辑
--> 跳转
五、过滤器与拦截器的区别?
都是对于用户的请求起到拦截的作用,
只不过Filter是Java EE规范的概念,
而拦截器Interceptors是Struts2中提出的概念!
Struts2中过滤器负责调用拦截器!
过滤器会过滤所有资源。即使用户访问jsp,也会被过滤。
拦截器只会过滤action资源。
六、Stuts2常用的内置拦截器的作用
6.1 Parameters 拦截器
Parameters拦截器的作用:
1.获取值栈的栈顶对象。
2.封装请求数据到栈顶对象中(自动类型转换)。
Parameters拦截器详细过程:
1.用户访问Action,Struts会自动创建Action,ValueStack,ActionContext对象。
同时将Action放在ValueStack对象的list栈的栈顶。
2.
Parameters拦截器会自动获取值栈对象,
并将请求数据封装到值栈对象中。类似于BeanUtils组件。
由于此时Action即值栈对象,所以会把数据封装到Action对象的属性中。
(属性必须带有get、set方法。属性名和请求参数名必须同名。)
6.1.1 封装注意事项
1.Parameters拦截器会将请求数据封装到值栈对象中,并非栈顶对象。
2.Parameters拦截器会将请求数据封装到值栈对象中,从上到下依次封装。
3.Parameters拦截器会将请求数据封装到值栈对象中,从上到下依次封装。
如果请求属性已经匹配过了,就不会再次匹配其他值栈对象的属性。
即如果name属性在第一个值栈对象中已经匹配了。如果第二个值栈对象也有name属性,
就不会再次匹配。
3.Parameters拦截器只会将请求数据封装到值栈对象中,但是对象中的对象不会封装。
6.1.2 案例
>>>>>> 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>
<!-- 0.请求数据的编码 。作用于HttpServletRequest的setCharacterEncoding方法 和freemarker 、velocity的输出-->
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
<package name="xxxx" extends="struts-default">
<!-- 全局视图 -->
<global-results>
<result name="success">/index.jsp</result>
<result name="err1">/error/err1.jsp</result>
</global-results>
<!-- 全局异常:会到全局视图中查过标记对应的页面 -->
<global-exception-mappings>
<exception-mapping result="null" exception="java.lang.NullPointerException"></exception-mapping>
<exception-mapping result="err1" exception="java.lang.Exception"></exception-mapping>
</global-exception-mappings>
<!-- action实例交给spring创建 -->
<action name="emp_*" class="empAction" method="{1}">
<result name="index">/index.jsp</result>
</action>
</package>
</struts>
>>>>>> EmpEntity.java
public class EmpEntity {
private String empId;
private String name;
private int age;
private DeptEntity dept;
}
>>>>>> EmpAction.java
public class EmpAction extends ActionSupport implements ModelDriven<EmpEntity> {
private EmpEntity emp=new EmpEntity();
private String deptId;
public String getDeptId() {
return deptId;
}
public void setDeptId(String deptId) {
this.deptId = deptId;
}
@Override
public EmpEntity getModel() {
return emp;
}
/**
* 跳转到新增员工界面
* @return
*/
public String add() {
System.out.println(deptId);
System.out.println(emp);
return "index";
}
}
>>>>>> index.java
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div style="margin:30px auto;width:300px;border:1px solid black;">
<form action="${pageContext.request.contextPath}/emp_add" method="get">
<table cellspacing="0" cellpadding="0" style="margin:0 auto;">
<caption><h1>新增员工</h1></caption>
<tr>
<td style="padding-top:8px;">姓名</td>
<td> <input type="text" name="name"></td>
</tr>
<tr >
<td style="padding-top:8px;">年龄</td>
<td> <input type="text" name="age"></td>
</tr>
<tr >
<td style="padding-top:8px;">部门</td>
<td>
<select name="deptId">
<option value="1">科技部</option>
<option value="2">科技部2</option>
</select>
</td>
</tr>
<tr align="center">
<td colspan="2" style="padding-top:8px;"> <input type="submit" value="新增"> <input type="submit" value="重置"> </td>
</tr>
</table>
</form>
</div>
</body>
</html>
>>>>>> 测试
>>>>>> 分析
由上图可以看见,deptId赋值了,emp实体的属性也赋值。
1.用户请求,服务器就会创建AcionContext、ValueStack、Aciton对象。
同时将Action对象放在栈顶中。
2.由于EmpAction使用了模型驱动,所以EmpEtityb又被压入栈顶。
3.此时值栈对象为(从上到下):
EmpEntity
id
name
age
EmpActon
deptId
6.Parameters拦截器会将请求数据从上到下自动封装到值栈对象中。
所以EmpEntity的属性有值。deptId也有值。
6.2 ModelDriven 拦截器
+++ ModelDriven拦截器的作用:
实现数据驱动。
+++ ModelDriven拦截器的具体细节:
1.用户访问Action,
首先会把Action、值栈对象以及ActionContext对象创建出来。
2.然后把action对象放在值栈对象的list集合的栈顶。
3.ModelDriven拦截器调用action的getModel()方法,获取实例对象,
然后将该对象放在值栈对象的list集合的栈顶。
4.然后Parameters拦截器会把请求数据自动
封装到值栈对象的list集合的栈顶对象。
6.3 FileUpload 拦截器
fileUpload拦截器主要用于文件上传。
struts封装了fileUpload组件,实现了文件上传功能。
6.4 Token 拦截器
1.在defaultStack默认拦截器栈中没有该拦截器,所以如果需要使用该拦截器,
必须要手动引入。
2.Token拦截器的作用:
解决表单数据重复提交。
3.Token拦截器执行流程:
+++ 使用:
在请求表单中加入<s:token />
在struts.xml中引入token拦截器
+++ 执行流程:
1.用户访问jsp,首先<s:token />会生成一个token值存储在客户端与Token拦截器中。
2.表单提交,token拦截器拦截用户执行的方法
==》用户访问的方法没有被拦截,则放行。
==》用户访问的方法被拦截,则会比较客户端与服务器端的token值,
如果token值一致,则删除服务器端token值,然后进入方法。
如果token值不一致,则视为重复提交,返回 invalide.token 结果视图