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();
	}
}

猜你喜欢

转载自blog.csdn.net/openbox2008/article/details/87714035