拦截器与过滤器的区别
拦截器是对调用的Action起作用,它提供了一种机制可以使开发者定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也是提供了一种可以提取action中可重用的部分的方式,很多业务逻辑都是靠拦截实现的,比如校验,验证登录权限(比如下载时跳转到登陆页面)等等。
过滤器是对整个的请求过程起作用!换句话说就是拦截器没有过滤器的范围广。过滤器是在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的 action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者 struts的action前统一设置字符集,或者去除掉一些非法字符(聊天室经常用到的,一些骂人的话,比如判断用户提交的数据是否存在非法字符等等。
Struts2拦截器是Struts2中的一个很重要的功能,本质是代理模式。本文将从概念开始,为大家讲解Struts2拦截器的实现原理以及如何定义等等内容。
一、理解Struts2拦截器
1. Struts2拦截器是在访问某个Action或Action的某个方法,字段之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现
2. 拦截器栈(Interceptor Stack)。Struts2拦截器栈就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
二、执行责任
这个执行职责有3种选择:
1.中止整个执行,直接返回一个字符串作为resultCode(则不会调用该Action)
2. 调用堆栈中下一个Interceptor的执行
3. 如果在堆栈内已经不存在任何的Interceptor,调用Action
三、实现Struts2拦截器原理
Struts2拦截器的实现原理相对简单,当请求struts2的action时,Struts2会查找配置文件,并根据其配置实例化相对应的拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器。
四、定义Struts2拦截器
Struts2规定用户自定义拦截器必须实现com.opensymphony.xwork2.interceptor.Interceptor接口。
例如:(该代码与本案例无关)
package cn.tedu.web;
import org.springframework.stereotype.Component;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
//拦截器必须实现接口Interceptor
@Component
public class DemoInterceptor implements Interceptor{
@Override
public void destroy() {}
@Override
public void init() {}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("开始");
String val = "success";
val = invocation.invoke();//调用后续控制器
System.out.println("结束");
return val;
}
}
该接口声明了3个方法
public interface Interceptor extends Serializable {
/**
* Called to let an interceptor clean up any resources it has allocated.
*/
void destroy();
/**
* Called after an interceptor is created, but before any requests are processed using
* {@link #intercept(com.opensymphony.xwork2.ActionInvocation) intercept} , giving
* the Interceptor a chance to initialize any needed resources.
*/
void init();
/**
* Allows the Interceptor to do some processing on the request before and/or after the rest of the processing of the
* request by the {@link ActionInvocation} or to short-circuit the processing and just return a String return code.
*
* @param invocation the action invocation
* @return the return code, either returned from {@link ActionInvocation#invoke()}, or from the interceptor itself.
* @throws Exception any system-level error, as defined in {@link com.opensymphony.xwork2.Action#execute()}.
*/
String intercept(ActionInvocation invocation) throws Exception;
}
不过,struts中又提供了几个抽象类来简化这一步骤。其中,init()和destroy()方法会在程序开始和结束时各执行一遍,不管使用了该拦截器与否,只要在struts.xml中声明了该Struts2拦截器就会被执行。intercept方法就是拦截的主体了,每次拦截器生效时都会执行其中的逻辑。
Struts2拦截器需要在struts.xml中声明,如下struts.xml配置文件,配置Struts2拦截器
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!-- Struts2的action由Spring来负责进行实例化 -->
<constant name="struts.objectFactory" value="spring" />
<package name="default" extends="struts-default">
<interceptors>
<interceptor name="MyInterceptor" class="interceptor.MyInterceptor">
</interceptor>
<interceptor-stack name="myInterceptorStack">
<interceptor-ref name="MyInterceptor" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<action name="loginAction" class="loginAction">
<result name="fail">/index.jsp </result>
<result name="success">/success.jsp</result>
<interceptor-ref name="myInterceptorStack"></interceptor-ref>
</action>
</package>
</struts>
-----------------------------------------------------------------------------------------------------------------------
以下为拦截器使用时候的具体案例:
项目结构如下:
导入jar依赖包:
spring配置文件:spring-web.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd">
<!-- spring的组件扫描 -->
<context:component-scan base-package="cn.tedu"/>
</beans>
web.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<display-name>struts_day04</display-name>
<filter>
<display-name>StrutsPrepareAndExecuteFilter</display-name>
<filter-name>StrutsPrepareAndExecuteFilter</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>StrutsPrepareAndExecuteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:conf/spring-*.xml</param-value>
</context-param>
</web-app>
struts.xml文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"
"http://struts.apache.org/dtds/struts-2.5.dtd">
<struts>
<!-- 拦截器案例 -->
<package namespace="/" name="wq" extends="json-default">
<!-- 拦截器 -->
<interceptors>
<!-- 配置自己的拦截器 -->
<interceptor name="myTime" class="timeConsumingInterceptor"></interceptor><!-- class为bean的ID -->
<interceptor name="myLogin" class="checkLoginInterceptor"></interceptor><!-- class为bean的ID -->
<interceptor name="myOther" class="otherInterceptor"></interceptor><!-- class为bean的ID -->
<!-- 配置拦截器栈 -->
<interceptor-stack name="myStack">
<!-- 默认自带的拦截器,当配置自己的拦截器时不再走默认的拦截器,所以需要调用自带的拦截器,并写在第一行 -->
<interceptor-ref name="defaultStack"/>
<!-- 加入自己的拦截器 -->
<interceptor-ref name="myTime"/>
<interceptor-ref name="myLogin"/>
</interceptor-stack>
</interceptors>
<!-- 定义默认的拦截器 每个Action都会自动引用,如果Action中引用了其它的拦截器 默认的拦截器将无效 -->
<default-interceptor-ref name="myStack"/>
<!-- 利用通配符访问action *对应action类的一个方法名-->
<action name="*Action" class="usersAction" method="{1}">
<!--
定义局部的拦截器:
当定义局部的拦截器,外面全局(默认)的拦截器则不会走,只会走局部的拦截器,
所以,我们在定义局部拦截器的同时,也要引用Struts2自带的默认拦截器defaultStack。
不引用defaultStack至少会遭成取不到form表单提交的值。
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="myOther"/>
-->
<!-- name属性不写默认success -->
<result>/home.jsp</result>
<result name="login">/file.jsp</result>
<result name="input">/login.jsp</result>
<!-- 允许的方法 -->
<allowed-methods>login</allowed-methods>
</action>
</package>
</struts>
login.jsp登录页面:
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>登录页面</title>
</head>
<body>
<form action="loginAction" method="post">
<div>
<label>用户名:</label>
<input type="text" name="user.name">
</div>
<div>
<label>密码:</label>
<input type="password" name="user.password">
</div>
<div>
<input type="submit" value="登录按钮">
</div>
</form>
</body>
</html>
home.jsp登录成功页面
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>登录页面</title>
</head>
<body>
登录成功进入首页。。
</body>
</html>
file.jsp登录失败页面
<%@ page contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>登录页面</title>
</head>
<body>
登陆失败页面。。
</body>
</html>
用户实体类Users:
package cn.tedu.entity;
import java.io.Serializable;
public class Users implements Serializable{
/** 属性 */
private String name;
private String password;
/** 构造方法 */
public Users() {
super();
}
public Users(String name, String password) {
super();
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "Users [name=" + name + ", password=" + password + "]";
}
}
拦截器三个如下:
CheckLoginInterceptor.java
package cn.tedu.interceptor;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import org.springframework.stereotype.Component;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
import cn.tedu.entity.Users;
/**
* 拦截器类
* 作用:检查用户是否登陆,没有登录则不能向Action发送请求。
*
* 测试请url访问:loginUsers.action
* @author wq
*
*/
@Component
public class CheckLoginInterceptor implements Interceptor{
@Override
public void destroy() {
}
@Override
public void init() {
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("执行顺序:3进入CheckLoginInterceptor");
//得到request对象
HttpServletRequest request = ServletActionContext.getRequest();
//取得登陆页面用户输入的账号密码若不为空的话让其通过
String name = request.getParameter("user.name");
String password = request.getParameter("user.password");
//取session中保存的用户登录信息
Users users = (Users) invocation.getInvocationContext().getSession().get("users");
if(name != null && password != null){
//若是登陆页面请求Action,则通过
return invocation.invoke();
}else if(users == null){
return Action.INPUT;//input常量 登录页面
}
//若已经登陆,则让其通过访问下一个拦截器,或Action。
return invocation.invoke();
}
}
TimeConsumingInterceptor.java
package cn.tedu.interceptor;
import org.springframework.stereotype.Component;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
/**
* 拦截器类
* 作用:计算用户从开始登录到结束登录消耗的毫秒数
* @author wq
*/
@Component
public class TimeConsumingInterceptor implements Interceptor{
@Override
public void destroy() {
}
@Override
public void init() {
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("执行顺序:1进入TimeConsumingInterceptor");
//开始时间
long startTime = System.currentTimeMillis();
System.out.println("执行顺序:2输出开始时间startTime:"+startTime);
//调用下一个拦截器,如果拦截器不存在,则执行Action
String result = invocation.invoke();
System.out.println("执行顺序:5输出Action返回的结果:"+result);
//结束时间
long endTime = System.currentTimeMillis();
System.out.println("执行顺序:6输出结束时间endTimen:"+endTime);
//登陆使用时间
System.out.println("登陆使用时间:"+(endTime-startTime)+"毫秒。。。");
return result;
}
}
OtherInterceptor.java
package cn.tedu.interceptor;
import org.springframework.stereotype.Component;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
/**
* 拦截器
* 作用:测试定义局部拦截器执行顺序。
* @author wq
*
*/
@Component
public class OtherInterceptor implements Interceptor{
@Override
public void destroy() {
}
@Override
public void init() {
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("局部拦截器");
return invocation.invoke();
}
}
控制器UsersAction.java
package cn.tedu.web;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import cn.tedu.entity.Users;
/**
* 控制器类
* 作用:处理用户的请求
* @author wq
*
*/
@Controller
@Scope("prototype")
public class UsersAction extends BaseAction{
/** 属性 */
private Users user;
public Users getUser() {
return user;
}
public void setUser(Users user) {
this.user = user;
}
/** 登陆验证的方法 */
public String login(){
System.out.println("执行顺序:4进入login()");
if(user != null){
if(user.getName().equals("admin") && user.getPassword().equals("admin")){
return SUCCESS;
}
}
return LOGIN;
}
}
全局拦截器:控制台输出登录成功的拦截器执行顺序。
用户名和密码均为admin
点击‘’登录按钮‘’后页面显示:
登录成功进入首页。。
控制台输出如下:
执行顺序:1进入TimeConsumingInterceptor
执行顺序:2输出开始时间startTime:1567242369265
执行顺序:3进入CheckLoginInterceptor
执行顺序:4进入login()
执行顺序:5输出Action返回的结果:success
执行顺序:6输出结束时间endTimen:1567242369287
登陆使用时间:22毫秒。。。
当用户名和密码错误的时候,页面显示:
登陆失败页面。。
控制台输出如下:
执行顺序:1进入TimeConsumingInterceptor
执行顺序:2输出开始时间startTime:1567242431836
执行顺序:3进入CheckLoginInterceptor
执行顺序:4进入login()
执行顺序:5输出Action返回的结果:login
执行顺序:6输出结束时间endTimen:1567242432658
登陆使用时间:822毫秒。。。
注意:struts.xml文件中采用了通配符来匹配请求路径,关于通配符匹配见博客
思路:
1.通过访问url:http://localhost:8000/struts_day04/login.jsp进入到登录页面,即login.jsp
2.输入用户名和密码后,进入action,由于struts.xml配置文件中配置了拦截器,按照配置顺序依次执行相应拦截器,myTime、myLogin
3.首先进入拦截器TimeConsumingInterceptor,控制台输出1、2步骤,在调用代码String result = invocation.invoke()之前,会调用下一个拦截器CheckLoginInterceptor,此时输出3步骤,在该拦截器中得到前台传来的两个参数,此时用户名和密码都不为null,进入Action处理,控制台输出步骤4,判断密码是否符合,若符合,则跳转成功页面,若不符合,则跳转失败页面,Action调用完成后再次回到TimeConsumingInterceptor,输出步骤5、6,计算Action的耗时
注意:若同时配置了全局拦截器和局部拦截器时,则不会再走全局拦截器。直接进入局部拦截器
<action name="*Action" class="usersAction" method="{1}">
<!--
定义局部的拦截器:
当定义局部的拦截器,外面全局(默认)的拦截器则不会走,只会走局部的拦截器,
所以,我们在定义局部拦截器的同时,也要引用Struts2自带的默认拦截器defaultStack。
不引用defaultStack至少会遭成取不到form表单提交的值。
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="myOther"/>
-->
<interceptor-ref name="myOther"/>
<!-- name属性不写默认success -->
<result>/home.jsp</result>
<result name="login">/file.jsp</result>
<result name="input">/login.jsp</result>
进入拦截器OtherInterceptor,输出步骤四,进入Action,然后跳转到失败页面
控制台输出:
局部拦截器
执行顺序:4进入login()