Java轻量级MVC框架Struts2 2.5详解(4)Struts2的拦截器
一、拦截器概述
拦截器:Struts2拦截器是在访问某个Action或Action的某个方法之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现。
AOP:面向切面编程.其实现原理:动态代理模式
WebWork中文文档解释:拦截器是动态拦截Action调用的对象。
它提供了一种机制使开发者可以定义在一个Action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也提供了一种可以提取Action中可重用的代码的方式。
拦截器栈(Interceptor Stack):Struts2拦截器栈就是将拦截器按一定的顺序连接成一条链。在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
拦截器能把很多功能从Action中独立出来,分散到不同的拦截器里面,减少了Action的代码。如此,拦截器和Action本身的功能都更单一了。当通用的功能代码被封装在拦截器里面(代码模块化),就可以对不同的Action,根据功能需要,来配置相应功能的拦截器了。提高了拦截器所实现的功能的重用性,也变相实现了装配式和可插拔式的体系结构,使得整个系统结构变得更灵活。
二、Struts2 拦截器的实现
1).实现interceptor接口
Struts2规定用户自定义拦截器必须实现com.opensymphony.xwork2.interceptor.Interceptor接口。
该接口声明了3个方法,其中,init和destroy方法会在程序开始和结束时各执行一遍,不管使用了该拦截器与否,只要在struts.xml中声明了该Struts2拦截器就会被执行。
intercept方法就是拦截的主体了,每次拦截器生效时都会执行其中的逻辑。
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;
}
所有拦截器都使用接口Interceptor ,Action去实现这个接口;
Init()方法:在服务器起动的时候加载一次,并且只加载一次;
Destroy()方法:当拦截器销毁时执行的方法;
Interceptor()方法:其中里边有一个参数invocation;
2).继承 AbstractInterceptor类
public abstract class AbstractInterceptor implements Interceptor {
/**
* Does nothing
*/
public void init() {
}
/**
* Does nothing
*/
public void destroy() {
}
/**
* Override to handle interception
*/
public abstract String intercept(ActionInvocation invocation) throws Exception;
}
继承AbstractInterceptor类,只需覆盖Interceptor()方法就可以。
三、Struts2拦截器与Servlet的过滤器的区别
1).过滤器:
在java web中,你传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者struts的 action进行业务逻辑,比如过滤掉非法url(不是login.do的地址请求,如果用户没有登陆都过滤掉),或者在传入servlet或者 struts的action前统一设置字符集,或者去除掉一些非法字符。
2).拦截器:
在面向切面编程的就是在你的service或者一个方法,前调用一个方法,或者在方法后调用一个方法比如动态代理就是拦截器的简单实现,在你调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在你调用方法后打印出字符串,甚至在你抛出异常的时候做业务逻辑的操作。
拦截器与过滤器的区别 :
1、拦截器是基于java的反射机制的,而过滤器是基于函数回调。
2、拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
3、拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
4、拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
5、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
6、执行顺序 :过滤前 - 拦截前 - Action处理 - 拦截后 - 过滤后。
过滤是一个横向的过程,首先把客户端提交的内容进行过滤(例如未登录用户不能访问内部页面的处理);过滤通过后,拦截器将检查用户提交数据的验证,做一些前期的数据处理,接着把处理后的数据发给对应的Action;Action处理完成返回后,拦截器还可以做其他过程(还没想到要做啥),再向上返回到过滤器的后续操作。
一个Filter 可负责拦截多个请求或响应:一个请求或响应也可被多个请求拦截。
创建一个Filter 只需两个步骤:
(1)创建Filter 处理类:
(2)在web.xml 文件中配置Filter 。
创建Filter 必须实现javax.servlet.Filter 接口,在该接口中定义了三个方法。
• void init(FilterConfig config): 用于完成Filter 的初始化。
• void destroy(): 用于Filter 销毁前,完成某些资源的回收。
• void doFilter(ServletRequest request, ServletResponse response,FilterChain chain): 实现过滤功能,该方法就是对每个请求及响应增加的额外处理。
过滤器Filter也具有生命周期:init()->doFilter()->destroy(),由部署文件中的filter元素驱动。在servlet2.4中,过滤器同样可以用于请求分派器,但须在web.xml中声明,<dispatcher>INCLUDE或FORWARD或REQUEST或ERROR</dispatcher>该元素位于filter-mapping中。
四、Struts2内置拦截器
Struts2内置了很多拦截器,Struts2框架中自定义的,可以直接拿来使用的拦截器。不同的拦截器栈(interceptor-stack)是不同拦截器的组合,用户根据不同的需求选择不同的拦截器栈.
内置拦截器在哪里,找到:struts2-core-2.5.18.jar包中的struts-default.xml文件.
常见的拦截器:
1:params拦截器
这个拦截器偷偷的把请求参数设置到相应的Action的属性去的,并自动进行类型转换。
2.modelDriven拦截器
如果Action实现ModelDriven接口,它将getModel()取得的模型对象存入OgnlValueStack中。
3.execption拦截器
顾名思义,在抛出异常的时候,这个拦截器起作用。最好把它放在第一位,让它能捕获所有的异常。
4.validation拦截器
调用验证框架读取 *-validation.xml文件,并且应用在这些文件中声明的校验。
5.token拦截器
核对当前Action请求(request)的有效标识,防止重复提交Action请求。
6.fileUpload拦截器
用来处理文件上传
7.workflow拦截器
调用Action的validate方法,一旦有错误返回,重新定位到INPUT结果视图
8.servletConfig
通过感知接口,获取感应对象
五、Struts2自定义拦截器
自定义登录验证拦截器
0.相关的Ation和jsp文件
1)LoginAction.java文件
package org.openbox.Interceptor;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport{
private static final long serialVersionUID = 1L;
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String execute() throws Exception {
//登录验证
if ("admin".equals(username) && "ps_2008".equals(password)) {
//登录成功,将数据放到session中
ActionContext.getContext().getSession().put("USER_IN_SESSION",username);
return Action.SUCCESS;
}
//失败返回登录并提示
ActionContext.getContext().put("errorMsg", "请求失败");
return "failure";
}
}
2)login.jsp文件
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<span style="color:red">${errorMsg}</span>
<form action="/login" method="post">
账号:<input name="username" /><br>
密码:<input name="password" /><br>
<input type="submit" value="login">
</form>
</body>
</html>
3)welcome.jsp文件
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录成功</title>
</head>
<body>
${USER_IN_SESSION}登录成功
</body>
</html>
1.定义一个拦截器
方式1:实现com.opensymphony.xwork2.interceptor.Interceptor接口.
方式2:继承com.opensymphony.xwork2.interceptor.AbstractInterceptor类.
拦截器类文件:LoginInterceptor.java
package org.openbox.Interceptor;
import java.util.Map;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
//拦截器
public class LoginInterceptor extends AbstractInterceptor{
private static final long serialVersionUID = 1L;
private String resultPage;
//从拦截器配置文件中,获取返回结果页面,这样代码中没有硬编码
public void setResultPage(String resultPage) {
this.resultPage = resultPage;
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
//从session中取出登录数据,通过ActionInvocation对象
ActionContext ctx = invocation.getInvocationContext();
//SESSION存在MAP中
Map<String, Object> session = ctx.getSession();
Object object = session.get("USER_IN_SESSION");
//失败,没有取到SESSION值,重返登录页面
if (object==null) {
//从拦截器参数中,获得结果视图页面
return resultPage;
}
//成功就放行操作
return invocation.invoke();
}
}
2.注册自定义拦截器
第一步:先在<package>下声明拦截器LoginInterceptor.
第二步:在相应的<action>中来引用LoginInterceptor拦截器,在那拦截就在那引用
1)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>
<!-- 1.struts2默认常量修改,不能直接修改default.propertis中的常量 -->
<constant name="struts.action.extension" value="action,do,neld,"></constant>
<constant name="struts.serve.static.browserCache" value="false"></constant>
<constant name="struts.devMode" value="true"></constant>
<constant name="struts.enable.DynamicMethodInvocation" value="true"></constant>
<!-- 2.主struts.xml文件,引用所有包中的struts-xxx.xml文件 -->
<include file="org/openbox/Interceptor/struts-interceptor.xml"></include>
</struts>
2)struts-interceptor.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 name="loginpkg" namespace="/" extends="struts-default">
<!-- 注册拦截器 -->
<interceptors>
<interceptor name="checklogin"
class="org.openbox.Interceptor.LoginInterceptor"></interceptor>
</interceptors>
<!-- 全局结果视图 -->
<global-results>
<result name="failure" type="dispatcher">/login.jsp</result>
</global-results>
<!-- 配置ACTION -->
<action name="login"
class="org.openbox.Interceptor.LoginAction" method="execute">
<result name="success" type="redirectAction">main</result>
<allowed-methods>execute</allowed-methods>
</action>
<!-- 定义一个默认的action,不用真正的写这个action -->
<action name="main">
<!-- 引用拦截器 -->
<interceptor-ref name="checklogin" />
<result>/welcome.jsp</result>
</action>
</package>
</struts>
3.配置拦截器栈和拦截器参数
1)修改后的struts-interceptor.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 name="loginpkg" namespace="/" extends="struts-default">
<!-- 注册拦截器 -->
<interceptors>
<interceptor name="checklogin" class="org.openbox.Interceptor.LoginInterceptor">
<!-- 为拦截器配置参数:指定返回结果页面 -->
<param name="resultPage">failure</param>
</interceptor>
<!-- 拦截器栈:引用自定义和系统默认拦截器 -->
<interceptor-stack name="mystack">
<interceptor-ref name="checklogin" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<!-- 默认拦截器引用:自定义的拦截器栈 -->
<default-interceptor-ref name="mystack" />
<!-- 全局结果视图 -->
<global-results>
<result name="failure" type="dispatcher">/login.jsp</result>
</global-results>
<!-- 配置ACTION -->
<action name="login" class="org.openbox.Interceptor.LoginAction" method="execute">
<!-- 第一个Action不需要自定义拦截器,所以只引用默认拦截器栈 -->
<interceptor-ref name="defaultStack" />
<result name="success" type="redirectAction">main</result>
<allowed-methods>execute</allowed-methods>
</action>
<!-- 定义一个默认的action,不用真正的写这个action -->
<action name="main">
<result>/welcome.jsp</result>
</action>
</package>
</struts>
2)修改后的拦截器类文件LoginInterceptor.java
package org.openbox.Interceptor;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
//拦截器
public class LoginInterceptor extends AbstractInterceptor{
private String resultPage;
//从拦截器配置文件中,获取返回结果页面,这样代码中没有硬编码
public void setResultPage(String resultPage) {
this.resultPage = resultPage;
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
//从session中取出登录数据,通过ActionInvocation对象
ActionContext ctx = invocation.getInvocationContext();
//SESSION存在MAP中
Map<String, Object> session = ctx.getSession();
Object object = session.get("USER_IN_SESSION");
//拦截判断:没有取到SESSION值,重返登录页面。
if (object==null) {
//从拦截器参数中,获得结果视图页面
return resultPage;
}
//成功就放行操作
return invocation.invoke();
}
}
4.利用拦截器参数排除不需要拦截的Action
其它文件和上面的相同,只需要改动struts-interceptor.xml和LoginInterceptor.java文件
重点在这里:
1)不用拦截的Action名称
2)两个Action中都不用再指定拦截器
3)给所有Action指定默认拦截器
4)拦截器文件中增加获取参数的代码
5)在拦截操用中,判断是否放行
6)测试结果
7)这两部分完整代码
<?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 name="loginpkg" namespace="/" extends="struts-default">
<!-- 注册拦截器 -->
<interceptors>
<interceptor name="checklogin" class="org.openbox.Interceptor.LoginInterceptor">
<!-- 为拦截器配置参数:指定返回结果页面 -->
<param name="resultPage">failure</param>
<param name="unCheckAction">login,logon,abc,xxx</param>
</interceptor>
<!-- 拦截器栈:引用自定义和系统默认拦截器 -->
<interceptor-stack name="mystack">
<interceptor-ref name="checklogin" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<!-- 默认拦截器引用:自定义的拦截器栈 -->
<default-interceptor-ref name="mystack" />
<!-- 全局结果视图 -->
<global-results>
<result name="failure" type="dispatcher">/login.jsp</result>
</global-results>
<!-- 配置ACTION -->
<action name="login" class="org.openbox.Interceptor.LoginAction" method="execute">
<result name="success" type="redirectAction">main</result>
<allowed-methods>execute</allowed-methods>
</action>
<!-- 定义一个默认的action,不用真正的写这个action -->
<action name="main">
<result>/welcome.jsp</result>
</action>
</package>
</struts>
package org.openbox.Interceptor;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
//拦截器
public class LoginInterceptor extends AbstractInterceptor{
private String resultPage;
//从拦截器配置文件中,获取返回结果页面,这样代码中没有硬编码
public void setResultPage(String resultPage) {
this.resultPage = resultPage;
}
private List<String> unCheckAction = null;
//从拦截器配置文件中,将不要拦截器的Action的名称,放入列表中
public void setUnCheckAction(String actionName) {
if (actionName!=null) {
this.unCheckAction=Arrays.asList(actionName.split(","));
}
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
//1.获取当前请求的Action名称
String actionName=invocation.getProxy().getActionName();
System.out.println(actionName);
System.out.println(unCheckAction);
//2.从session中取出登录数据,通过ActionInvocation对象
ActionContext ctx = invocation.getInvocationContext();
//SESSION存在MAP中
Map<String, Object> session = ctx.getSession();
Object object = session.get("USER_IN_SESSION");
//3.拦截判断:没有取到SESSION值,重返登录页面。当前的action名称,不在非拦截列表中
if (object==null && !unCheckAction.contains(actionName)) {
//从拦截器参数中,获得结果视图页面
return resultPage;
}
//成功就放行操作
return invocation.invoke();
}
}