欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java轻量级MVC框架Struts2 2.5详解(4)Struts2的拦截器

程序员文章站 2022-07-09 17:22:42
...

一、拦截器概述

拦截器: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文件.

Java轻量级MVC框架Struts2 2.5详解(4)Struts2的拦截器

常见的拦截器:

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自定义拦截器

自定义登录验证拦截器

Java轻量级MVC框架Struts2 2.5详解(4)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名称

Java轻量级MVC框架Struts2 2.5详解(4)Struts2的拦截器

2)两个Action中都不用再指定拦截器

Java轻量级MVC框架Struts2 2.5详解(4)Struts2的拦截器

3)给所有Action指定默认拦截器

Java轻量级MVC框架Struts2 2.5详解(4)Struts2的拦截器

4)拦截器文件中增加获取参数的代码

Java轻量级MVC框架Struts2 2.5详解(4)Struts2的拦截器

5)在拦截操用中,判断是否放行

 Java轻量级MVC框架Struts2 2.5详解(4)Struts2的拦截器

6)测试结果

 Java轻量级MVC框架Struts2 2.5详解(4)Struts2的拦截器

Java轻量级MVC框架Struts2 2.5详解(4)Struts2的拦截器

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