首页 > 代码库 > 【SSH2(实践篇)】--Struts2拦截器精解

【SSH2(实践篇)】--Struts2拦截器精解

       上篇博客对Struts2的体系结构做了初步的了解,Struts2是以WebWork作为处理核心,并采用拦截器的机制来处理用户的请求,同时它还集成了Struts1丰富的标签库。另外上篇博客还对Struts2的配置使用进行了初步的介绍,下面将会集中讨论Struts2的拦截器。


一、拦截器


  1、拦截器小介


       拦截器的功能类似于web.xml文件中的Filter,能对用户的请求进行拦截,通过拦截用户的请求来实现对页面的控制。拦截器是在Struts-core-2.2.3.jar中进行配置的,原始的拦截器是在struts-default.xml中配置的,里面封存了拦截器的基本使用方法。

       Struts2拦截器功能类似于Servlet过滤器。在Action执行execute方法前,Struts2会首先执行struts.xml中引用的拦截器,如果有多个拦截器则会按照上下顺序依次执行,在执行完所有的拦截器的interceptor方法后,会执行Action的execute方法。

        Struts2的拦截器必须从com.opensymphoy.xwork2.interceptor.Interceptor中实现该接口,在被定义的拦截器中有下面三个方法需要被实现:

void destroy(); 
void init(); 
String intercept(ActionInvocation invocation) throws Exception;

       自定义的拦截器需要重写上面三个方法。另外Struts2的拦截器配置文件struts.xml它是继承了原始文件struts-default.xml文件的,这样在相应的<package>中就会自动拥有struts-default.xml中的所有配置信息了。具体代码如下:

<package name="demo" extends="struts-default" > ... </package>


  2、Demo


       想要使用拦截器必须要经过配置,struts2采用的是映射的方法,所以想用使用某一个功能就必须在配置文件中配置,拦截器也不例外。所以必须在package中添加相应的拦截器元素,同时将拦截器关联相应的class文件,这样在执行action前才会执行相应的拦截器,具体使用方法如下。


      (1)添加配置文件struts.xml,并在该文件中添加拦截器

<package name="testLogin" namespace="/" extends="struts-default">
	<!-- 拦截器 -->
	<interceptors>
		<interceptor name="myInterceptor" class="com.interceptor.MyInterceptor"></interceptor>
	</interceptors>
		
	<action name="demo" class="com.action.LoginAction">
		<result name="error" type="redirect">/error.jsp</result>
		<result name="success">/success.jsp</result>
		<result name="checkError">/checkSession.jsp</result>
		<interceptor-ref name="myInterceptor"></interceptor-ref>
		<interceptor-ref name="defaultStack"></interceptor-ref>
	</action>
</package>

        上面的package中添加了一个名为myInterceptor的拦截器,并为该拦截器注册了一个java类,该类名称为MyInterceptor,并被封存在com.interceptor包中。另外还在该package中添加了相应的action,在执行该action前会首先执行myInterceptor拦截器。


      (2)编写被注册的拦截器类MyInterceptor,该类必须实现com.opensymphoy.xwork2.interceptor.Interceptor接口,并重写相应的方法

package com.interceptor;

import java.util.Map;

import com.entity.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;

public class MyInterceptor implements Interceptor{

	private User user;
	
	
	public User getUser() {
		return user;
	}

	public void setUser(User user) {
		this.user = user;
	}

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		System.out.println("----destroy()----");
	}

	@Override
	public void init() {
		// TODO Auto-generated method stub
		System.out.println("-----Init()-------");
	}

	@Override
	public String intercept(ActionInvocation invocation) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("----intercept()------");
		Map<String, Object> session= invocation.getInvocationContext().getSession();
		if(session.get("username")!=null){
			System.out.println("登陆成功!");		
			//session.put("username",user.getUsername());
			return invocation.invoke();
		}else{
			System.out.println("登陆失败!");
			return "checkError";
		}
	}

}


       (3)经过前面两步后,拦截器已经配置完成,最后一部就是使用拦截器了,在显示页面上添加相应的标签,并为标签指定上面创建的名为demo的action,然后执行页面即可在控制台中打印出相应的拦截器内容。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form action="demo">
		用户名:<input type="text" name="username"><br>
		密  码:<input type="text" name="password"><br>
		<input type="submit" name="ok" value=http://www.mamicode.com/"提交">>
打印输出内容:

        分析输出结果,程序编译阶段首先会去读取配置文件struts.xml,在该配置文件action中顺序查找是否添加了拦截器,如果添加了拦截器则根据拦截器名称在<interceptors>中查找是否定义了该拦截器或者拦截器栈,如果发现定义的是拦截器则根据拦截器查找对应的注册的class,最后在包内查找注册的class并执行相应的init()方法;程序运行阶段的大致流程和编译阶段类似,用户在前台提交请求后,会按照注册的action在struts.xml中查找与之相对应的,如果查找到将会查找拦截器,没有查找到的话会相应的抛错,最后执行拦截器注册类的intercept方法。


  3、拦截器栈


       拦截器同样有栈的概念,它是将使用的拦截器定义到共有的状态下来实现统一管理,这样在package中就可以做到拦截器的共享了,大大便利了拦截器的使用。在一个package中往往会用到重复的interceptor,如果每次都在Action中添加interceptor-ref的话就会很麻烦,那么拦截器栈就是为了解决这个问题而产生的,具体配置如下:

<package name="testLogin" namespace="/" extends="struts-default">
		<!-- 拦截器 -->
		<interceptors>
			<interceptor name="myInterceptor" class="com.interceptor.MyInterceptor"></interceptor>
			<!-- 定义公共的拦截器链,在action标签中只需要引用拦截器链 -->
			<interceptor-stack name="defaultstack1">
				<interceptor-ref name="myInterceptor"></interceptor-ref>
				<interceptor-ref name="defaultStack"></interceptor-ref>
			</interceptor-stack>
		</interceptors>
		
		<action name="demo" class="com.action.LoginAction">
			<result name="error" type="redirect">/error.jsp</result>
			<result name="success">/success.jsp</result>
			<result name="checkError">/checkSession.jsp</result>
			<interceptor-ref name="defaultstack1"></interceptor-ref>
		</action>
</package>

        实例中使用了interceptor-stack来定义了一个名称为defaultstack1的拦截器栈,在该栈中添加了要执行的拦截器,把拦截器做了封装,在Action中直接调用该拦截器栈即可,实现了拦截器栈的共享。


  4、默认拦截器栈


        另外可以定义默认的拦截器栈,即:如果某个Action中没有定义拦截器,那么它会默认执行该公共的拦截器。它和interceptors标签属于同一等级的,使用default-interceptor-ref定义。

<package name="testLogin" namespace="/" extends="struts-default">
	<!-- 拦截器 -->
	<interceptors>
		<interceptor name="myInterceptor" class="com.interceptor.MyInterceptor"></interceptor>
		<!-- 定义公共的拦截器链,在action标签中只需要引用拦截器链 -->
		<interceptor-stack name="defaultinter">
			<interceptor-ref name="myInterceptor"></interceptor-ref>
			<interceptor-ref name="defaultStack"></interceptor-ref>
		</interceptor-stack>
	</interceptors>
	
	<!-- 定义默认的拦截器栈,会自动注册到action中 -->
	<default-interceptor-ref name="defaultinter"></default-interceptor-ref>
	
	<action name="demo" class="com.action.LoginAction">
		<result name="error" type="redirect">/error.jsp</result>
		<result name="success">/success.jsp</result>
		<result name="checkError">/checkSession.jsp</result>
	</action>
</package>

      定义的默认的拦截器栈只是在Action没有指定拦截器的情况下才执行自定义默认的拦截器栈的,如果在Action中重定义了拦截器,那么它会覆盖自定义默认的拦截器栈的。

  5、不执行任何拦截器


      还有一种情况是一个package中定义了默认的拦截器,但是在编写的某个Action中并不需要执行任何拦截器,那么此时可以在相应的Action中添加一个名称为defaultStack的拦截器即可,它是系统默认的拦截器,不会有任何操作。

<package name="testLogin" namespace="/" extends="struts-default">
	<!-- 拦截器 -->
	<interceptors>
		<interceptor name="myInterceptor" class="com.interceptor.MyInterceptor"></interceptor>
		<!-- 定义公共的拦截器链,在action标签中只需要引用拦截器链 -->
		<interceptor-stack name="defaultinter">
			<interceptor-ref name="myInterceptor"></interceptor-ref>
			<interceptor-ref name="defaultStack"></interceptor-ref>
		</interceptor-stack>
	</interceptors>
	
	<!-- 定义默认的拦截器栈,会自动注册到action中 -->
	<default-interceptor-ref name="defaultinter"></default-interceptor-ref>
	
	<action name="demo" class="com.action.LoginAction">
		<result name="error" type="redirect">/error.jsp</result>
		<result name="success">/success.jsp</result>
		<result name="checkError">/checkSession.jsp</result>
		
		<!-- 添加defaultStack保证不执行拦截器 -->
		<interceptor-ref name="defaultStack"></interceptor-ref>
	</action>
</package>


  6、拦截方法


      6.1 用法

        上面的拦截器只是实现了对Action的拦截,其实拦截器的功能很强大,它也可以拦截相应Action方法。和拦截Action不同的是想要拦截方法就必须继承类MethodFilterInterceptor,该类封存在xwork-core.jar中,又一次证明了WebWork是Struts2的核心。另外还需要在配置文件中添加相应的属性来确定拦截的方法和不拦截的方法,具体配置方法如下:

<package name="testLogin" namespace="/" extends="struts-default">
	<!-- 拦截器 -->
	<interceptors>
		<interceptor name="myInterceptor" class="com.interceptor.MyInterceptor"></interceptor>
		<!-- 定义公共的拦截器链,在action标签中只需要引用拦截器链 -->
		<interceptor-stack name="defaultinter">
			<interceptor-ref name="myInterceptor"></interceptor-ref>
			<interceptor-ref name="defaultStack"></interceptor-ref>
		</interceptor-stack>
	</interceptors>
	
	<action name="demo" class="com.action.LoginAction">
		<result name="error" type="redirect">/error.jsp</result>
		<result name="success">/success.jsp</result>
		<result name="checkError">/checkSession.jsp</result>
		
		<!-- 在defaultStack中配置拦截方法,参数includeMethods中添加被拦截的方法名称,excludeMethods中添加不需要拦截的名称 -->
		<interceptor-ref name="defaultStack">
			<param name="includeMethods">添加要拦截的方法名称</param><!-- 拦截方法 -->
			<param name="excludeMethods">添加不需要拦截的方法名称</param><!-- 不拦截方法 -->
		</interceptor-ref>
	</action>
</package>

      继承MethodFilterInterceptor类的相应拦截方法的类中的代码:

package com.interceptor;

import java.util.Map;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

public class inter extends MethodFilterInterceptor {

	@Override
	public String doIntercept(ActionInvocation invocation) throws Exception {
		System.out.println("--intercept()--");
		//获取相应的Session
		Map<String,Object> session=invocation.getInvocationContext().getSession();
		
		Map request=(Map)ActionContext.getContext().get("request");
		
		String username=(String)request.get("user.username");
		if(session.get("username") != null){
			String result=invocation.invoke();
			
			System.out.println("--end()--");
			return result;
		}
	}

}

    6.2 Demo

       来看一个拦截方法的实例,并对结果进行分析。下面的实例演示拦截方法的输出结果,在实例中分别创建了一个loginAction类,添加Action要执行的方法;Inter类,拦截器中重写MethodFilterInterceptor方法,在控制台中输出是否对某个方法进行拦截;login.jsp文件,添加三个按钮,分别演示三个方法的执行。

      (1)struts.xml内方法拦截器的定义,在package中定义了一个名称为inter的拦截器,在拦截器中指定了参数,includeMethods用来拦截Method1,excludeMethods中的Method2表示不拦截Methods2方法,具体配置如下代码:

<!DOCTYPE struts PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
          "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
    <constant name="struts.action.extension" value=http://www.mamicode.com/",">>
       (2)loginAction类,配置login.jsp中的action,分别在该类中添加Method1-Method3三个方法,其中Method1被拦截,Method2和Method3不被拦截,最后我们查看输出结果。

package com.action;

import com.opensymphony.xwork2.ActionSupport;

public class loginAction extends ActionSupport {
	@Override
	public String execute() throws Exception {
		if(this.username.equals("admin") && this.password.equals("admin")){
			return "success";
		}else if(this.username.equals("cancel") && this.password.equals("cancel")){
			return "cancel";
		}else{
			return "error";
		}
	}
	
	public void Method1(){
		System.out.println("执行方法:Method1");
	}
	
	public void Method2(){
		System.out.println("执行方法:Method2");
	}
	
	public void Method3(){
		System.out.println("执行方法:Method3");
	}
	
	private String username;
	private String password;
	
	public String getUsername(){
		return this.username;
	}
	
	public void setUsername(String username){		
		this.username=username;
	}
	
	public String getPassword(){
		return this.password;
	}
	
	public void setPassword(String password){
		this.password=password;
	}
	
	
}

        (3)inter类,继承MethodFilterInterceptor类,用来实现拦截方法。重写doIntercept方法,在该方法中添加拦截的相应信息。
package com.interceptor;

import java.util.Date;
import java.util.Map;

import com.action.loginAction;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

public class inter extends MethodFilterInterceptor {
	
	@Override
	protected String doIntercept(ActionInvocation invocation) throws Exception {
		// TODO Auto-generated method stub  
		 System.out.println("拦截器在Action执行前拦截"+new Date());  
		 String result=invocation.invoke();  //执行Action方法
		 System.out.println("拦截器在Action执行后拦截"+new Date());  
 		return result;  

 	}

}

     (4)login.jsp,在jsp页面上添加三个按钮,分别演示三个方法,判断拦截器对方法的拦截情况。三个按钮在点击后回发的action是在javascript中动态的进行添加的,这样做达到了一个form中执行不同的action的方法,当然还有其它的方法,将会在下篇文章中讨论。
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
	
	//方法1,定义被拦截的方法的实例
	function method1(){
		var form=document.forms[0];
		form.action="loginaction!Method1";
		form.submit();
	}
	
	//方法2,为按钮2添加不拦截的方法
	function method2(){
		var form=document.forms[0];
		form.action="loginaction!Method2";
		form.submit();
	}
	
	//方法3,为按钮3添加不拦截的方法
	function method3(){
		var form=document.forms[0];
		form.action="loginaction!Method3";
		form.submit();
	}
</script>
</head>
<body>
	<form>
		用户名:<input type="text" name="username"><br>
		密     码:<input type="text" name="password"><br>
		<input type="submit" name="ok" value=http://www.mamicode.com/"按钮1" onclick="method1()">>
        运行完成后的页面视图:
             
       (5)分析运行结果,分别单击按钮1、2、3,在控制台中输出结果,按钮1是绑定的method1,该方法在struts.xml中进行了拦截如果结果正确的话会显示被拦截的结果,而相应的按钮2和3只输出运行结果,因为它们没有被拦截。那看下面的结果图:

        结果图正好正是了我们的分析结果,按钮1被拦截了,执行了inter类中的doIntercept方法,二相应的按钮2和3没有被拦截。也就是说,Method1被放到了方法拦截器的白名单内,执行要拦截该方法;Method2被放到了拦截器黑名单内,不需要拦截该方法;Method3不做任何处理。

结语


        对于拦截器的内容就总结到这里,拦截器提供了很强大的功能,使得开发人员能够在运行时控制输出结果,增加了编程的灵活性。另外对于任何理论性的东西都不要试图去记忆,一定要理性的去分析,多多实践,动手做几个实例,分析结果更深刻的理解。