第十五部分_Struts2.1拦截器深度剖析、异常处理

恢复拦截器:interceptor。

在我们声明拦截器(这时候默认的拦截器就不起作用了)的同时,我们一定要加上struts2提供的默认拦截器(否则访问页面的返回信息可能出乎你的意料,比如提交的表单信息出现一堆乱七八糟的信息),且我们自己声明的拦截器一定要在默认的之前。

使用拦截器的步骤:

  1. 定义相应的拦截器类,实现Interceptor或是继承AbstractInterceptor(该抽象类空实现实现了Interceptor接口的init和destroy方法)
  2. 在struts.xml中声明拦截器,在相关Action中配置,配置的最后记得一点要添加默认的拦截器

下面我们使用拦截器帮助我们读取xml中的参数(类比与之前用过滤器实现黑名单禁止留言的例子,这里不需要用getParameter方法就可以方便的读取参数值)。

下面举个例子:

新建一个com.test.interceptor包,在该包下面新建一个类MyInterceptor:

package com.test.interceptor;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.interceptor.PreResultListener;

public class MyInterceptor implements Interceptor
{
	private String hello; // 接收拦截器在struts.xml配置中的hello属性中的值

	public String getHello()
	{
		return hello;
	}

	public void setHello(String hello)
	{
		this.hello = hello;
	}

	public void destroy()
	{
		System.out.println("destroy invoked");
	}

	public void init()
	{
		System.out.println("init invoked");
		System.out.println("hello :" + hello);
	}

	public String intercept(ActionInvocation invocation) throws Exception
	{
		System.out.println("intercept invoked");

		String result = invocation.invoke();

		System.out.println(result);

		return result;
	}
}

为了更加清楚地了解拦截器的运转流程,我们多写几个拦截器,MyInterceptor2:

package com.test.interceptor;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class MyInterceptor2 extends AbstractInterceptor
{

	@Override
	public String intercept(ActionInvocation invocation) throws Exception
	{
		System.out.println("myInterceptor2 invoked");

		String result = invocation.invoke();

		System.out.println("result2: " + result);

		return result;
	}

}

再来一个MyInterceptor3(前面是基于类的拦截器,这个是基于方法的):

package com.test.interceptor;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;

public class MyInterceptor3 extends MethodFilterInterceptor
{

	@Override
	protected String doIntercept(ActionInvocation invocation) throws Exception
	{
		System.out.println("myInterceptor3 invoked");

		String result = invocation.invoke();

		System.out.println("result3 : " + result);

		return result;
	}

}

然后在struts.xml中原来的基础之上进行配置:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

	<struts>

		<package name="struts2" extends="struts-default">

		<interceptors>

			<interceptor name="myInterceptor" class="com.test.interceptor.MyInterceptor">
					<param name="hello">world</param>
			</interceptor>

			<interceptor name="myInterceptor2" class="com.test.interceptor.MyInterceptor2">
			</interceptor>

			<interceptor name="myInterceptor3" class="com.test.interceptor.MyInterceptor3">
			</interceptor>

	</interceptors>

	            <action name="register" class="com.test.action.RegisterAction">
				<result name="success">/success.jsp</result>
				<result name="input">/register.jsp</result>

				<interceptor-ref name="myInterceptor"></interceptor-ref>
				<interceptor-ref name="myInterceptor2"></interceptor-ref>
 				<interceptor-ref name="myInterceptor3"></interceptor-ref>
							</action>
		</package>

	</struts>

服务器启动过程中会输出的与我们相关的信息(截取一部分):

信息: Parsing configuration file [struts-plugin.xml]
2015-7-23 9:21:28 com.opensymphony.xwork2.util.logging.commons.CommonsLogger info
信息: Parsing configuration file [struts.xml]
init invoked
hello :world
2015-7-23 9:21:30 org.apache.catalina.startup.HostConfig deployDescriptor
信息: Deploying configuration descriptor host-manager.xml
2015-7-23 9:21:30 org.apache.catalina.startup.HostConfig deployDescriptor
信息: Deploying configuration descriptor manager.xml

访问http://localhost:8080/struts2/register.action,成功转移到结果页面时,控制台输出如下:

intercept invoked
myInterceptor2 invoked
myInterceptor3 invoked
validate~~~~~~~~~~~~~~~~
execute invoked
result3: success
result2: success
success

这里validate~~~~~~~~~~~~~~得到输出是因为我们在RegisterAction的validate方法中打印了改行语句,从输出中我们可以窥得拦截器的执行时机和执行流程,它和过滤器的运行机理基本一致。

正如我们在之前MyInterceptor3中写的,它是一个基于方法的拦截器,如何让其生效呢?那就是在配置的时候给它添加额外的参数:

<interceptor-ref name="myInterceptor3">
					<param name="excludeMethods">execute</param>
				</interceptor-ref>

这样,访问页面,输出变成这个样子:

intercept invoked
myInterceptor2 invoked
validate~~~~~~~~~~~~~~~~
execute invoked
result2: success
success

通过这种方法,我们禁掉了第三个拦截器对execute方法的拦截。

另外,会不会觉得当拦截器很多的时候,一个Action配置多个拦截器有些麻烦?我们想到了这个问题,Struts2的设计者也早就想到了,解决方案可以在struts-default.xml中得到一些启示:

方法就是使用拦截器栈:

<interceptors>

			<interceptor name="myInterceptor" class="com.test.interceptor.MyInterceptor">
					<param name="hello">world</param>
			</interceptor>

			<interceptor name="myInterceptor2" class="com.test.interceptor.MyInterceptor2">
			</interceptor>

			<interceptor name="myInterceptor3" class="com.test.interceptor.MyInterceptor3">
			</interceptor>

			<interceptor-stack name="myStack">
				<interceptor-ref name="myInterceptor"></interceptor-ref>
				<interceptor-ref name="myInterceptor2"></interceptor-ref>
				<interceptor-ref name="defaultStack"></interceptor-ref>
			</interceptor-stack>

		</interceptors>

这样在action中我们只要引入myStack,myInterceptor和myInterceptor2还有defaultStack就都起作用了。

此外,如果在struts.xml中我们这样做:

<interceptors>

			<interceptor name="myInterceptor" class="com.test.interceptor.MyInterceptor">
					<param name="hello">world</param>
			</interceptor>

			<interceptor name="myInterceptor2" class="com.test.interceptor.MyInterceptor2">
			</interceptor>

			<interceptor name="myInterceptor3" class="com.test.interceptor.MyInterceptor3">
			</interceptor>

			<interceptor-stack name="myStack">
				<interceptor-ref name="myInterceptor"></interceptor-ref>
				<interceptor-ref name="myInterceptor2"></interceptor-ref>
				<interceptor-ref name="defaultStack"></interceptor-ref>
			</interceptor-stack>

		</interceptors>

		<default-interceptor-ref name="myStack"></default-interceptor-ref>

也就是给它添加一个默认的拦截器,这样所有的action到会引用到这个默认的拦截器。

一个应用场景:

拦截器最重要的使用场合是进行权限验证: 定义一个拦截器去拦截用户的请求,如果用户没有登录的话,我们就让用户登录,不让他往后走了,如果用户登录了,直接让用户进入到相应的主界面。

struts为我们提供了一个:全局结果(global-results),所有action所共享的。

struts2默认使用请求转发,我们可以改变它的行为,使用重定向的方式。type="redirect",我们如何知道有这么一个类型呢,回到struts-default.xml中(下面截取与我们相关的一部分):

<package name="struts-default" abstract="true">
        <result-types>
            <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
            <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
            <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
            <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
            <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
            <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
            <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
            <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
            <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
            <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
        </result-types>

可以看到默认的是请求转发dispatcher。下面是我们的程序AuthInterceptor:

package com.test.interceptor;

import java.util.Map;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class AuthInterceptor extends AbstractInterceptor
{

	@Override
	public String intercept(ActionInvocation invocation) throws Exception
	{
		// 通常我们检查用户是否登录了,是通过session来完成的
		// 注意到这个session不是HttpSession,它是被struts2封装好的Map,很好的实现了隐藏,此外,便于用单元测试框架进行测试,而不必借助于容器
		Map map = invocation.getInvocationContext().getSession();

		if(null == map.get("user")) // 用户没有登录
		{
			return Action.LOGIN; // 返回到登录界面
		}
		else
		{
			return invocation.invoke();
		}
	}

}

接下来是structs.xml中相关的配置:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

	<struts>

		<package name="struts2" extends="struts-default">

		<interceptors>

			<interceptor name="myInterceptor" class="com.test.interceptor.MyInterceptor">
					<param name="hello">world</param>
			</interceptor>

			<interceptor name="myInterceptor2" class="com.test.interceptor.MyInterceptor2">
			</interceptor>

			<interceptor name="myInterceptor3" class="com.test.interceptor.MyInterceptor3">
			</interceptor>

			<interceptor name="authInterceptor" class="com.test.interceptor.AuthInterceptor">
			</interceptor>

			<interceptor-stack name="myStack">
				<interceptor-ref name="authInterceptor"></interceptor-ref>
				<interceptor-ref name="myInterceptor"></interceptor-ref>
				<interceptor-ref name="myInterceptor2"></interceptor-ref>
				<interceptor-ref name="defaultStack"></interceptor-ref>
			</interceptor-stack>

		</interceptors>

		<global-results>
			<result name="login" type="redirect">/login.jsp</result>
		</global-results>

			<action name="register" class="com.test.action.RegisterAction">
				<result name="success">/success.jsp</result>
				<result name="input">/register.jsp</result>
				<!--
				<interceptor-ref name="myInterceptor"></interceptor-ref>
				<interceptor-ref name="myInterceptor2"></interceptor-ref>
				 -->

				<interceptor-ref name="myStack"></interceptor-ref>

			</action>
		</package>

	</struts>

访问http://localhost:8080/struts2/register.jsp,请求转发的方式登录地址栏显示http://localhost:8080/struts2/register.action,重定向显示http://localhost:8080/struts2/login.jsp;

扩展——防止表单重复提交: 一般有两种方式

  • 使用重定向。
  • 使用token(令牌)。

补充:异常处理机制

我们还以login.jsp为例:

<body>
  <form action="login.action">
    username: <input type="text" name="username" size="20"/><br>
    password: <input type="password" name="password" size="20"/><br/>

    <input type="submit" value="submit">
   </form>
  </body>

首先在com.test.exception包下面建立两个类UsernameException:

package com.test.exception;

public class UsernameException extends Exception
{
	private String message;

	public String getMessage()
	{
		return message;
	}

	public void setMessage(String message)
	{
		this.message = message;
	}

	public UsernameException(String message)
	{
		super(message);
		this.message = message;
	}
}

还有一个PasswordException:

package com.test.exception;

public class PasswordException extends Exception
{
	private String message;

	public String getMessage()
	{
		return message;
	}

	public void setMessage(String message)
	{
		this.message = message;
	}

	public PasswordException(String message)
	{
		super(message);
		this.message = message;
	}
}

我们在LoginAction中,假定用户名不为"hello"就抛异常,密码不为"world"也抛异常:

package com.test.action;

import com.opensymphony.xwork2.ActionSupport;
import com.test.exception.PasswordException;
import com.test.exception.UsernameException;

public class LoginAction extends ActionSupport
{
	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;
	}

	public String execute() throws Exception
	{
		if(!"hello".equals(username))
		{
			throw new UsernameException("username invalid");
		}
		else if(!"world".equals(password))
		{
			throw new PasswordException("password invalid");
		}
		else
		{
			return SUCCESS;
		}
	}

}

做到这里,访问login页面,输入非"hello"用户名,一大堆异常信息就抛给用户了,这显然是不友好的,因此我们这样处理,使得用户名非法时提示username invalid:

在struts.xml中struts标签下的嵌套标签:

<global-results>
			<result name="login" type="redirect">/login.jsp</result>
			<result name="usernameInvalid">/usernameInvalid.jsp</result>
		</global-results>

		<global-exception-mappings>
			<exception-mapping result="" exception=""></exception-mapping>
		</global-exception-mappings>

或者mapping信息或result信息也可以放到局部:

<action name="login" class="com.test.action.LoginAction">
				<exception-mapping result="usernameInvalid" exception="com.test.exception.UsernameException"></exception-mapping>
				<result name="success">/result.jsp</result>
				<result name="input">/login2.jsp</result>
			</action>

因此,既可以局部异常映射/结果(mapping/result),也可以全局映射。寻找规则:先找局部,在找全局。

时间: 2024-11-05 22:50:01

第十五部分_Struts2.1拦截器深度剖析、异常处理的相关文章

SpringMVC学习(十二)——SpringMVC中的拦截器

SpringMVC学习(十二)--SpringMVC中的拦截器 SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理.本文主要总结一下SpringMVC中拦截器是如何定义的,以及测试拦截器的执行情况和使用方法. SpringMVC中拦截器的定义和配置 SpringMVC中拦截器的定义 在SpringMVC中,定义拦截器要实现HandlerInterceptor接口,并实现该接口中提供的三个方法,如下: public class Inter

五 、 Kafka producer 拦截器(interceptor) 和 六 、Kafka Streaming案例

五 Kafka producer 拦截器(interceptor) 5.1 拦截器原理 Producer 拦截器(interceptor)是在 Kafka 0.10 版本被引入的,主要用于实现 clients 端的定 制化控制逻辑. 对于 producer 而言,interceptor 使得用户在消息发送前以及 producer 回调逻辑前有机会 对消息做一些定制化需求,比如修改消息等.同时,producer 允许用户指定多个 interceptor 按序作用于同一条消息从而形成一个拦截链(in

[十四]SpringBoot 之 Spring拦截器(HandlerInterceptor)

过滤器属于Servlet范畴的API,与spring 没什么关系. Web开发中,我们除了使用 Filter 来过滤请web求外,还可以使用Spring提供的HandlerInterceptor(拦截器). HandlerInterceptor 的功能跟过滤器类似,但是提供更精细的的控制能力:在request被响应之前.request被响应之后.视图渲染之前以及request全部结束之后.我们不能通过拦截器修改request内容,但是可以通过抛出异常(或者返回false)来暂停request的执

第二十五课、布局管理器(四)

一.栈式布局管理器 1.栈式布局管理器(QStatckedLayout)概要 (1).所有组件垂直于屏幕的方向上被管理 (2).每次只有一个组件会显示在屏幕上 (3).只有最顶层的组件会被最终显示 2.栈式布局管理器的特点 (1).组件大小一致且充满父组件的显示区 (2).不能直接嵌套其它布局管理器(可以依赖中间组件间接嵌套) (3).能够自由切换需要显示的组件 (4).每次能且仅能显示一个组件 3.QStatckedLayout的用法概要 二.计时器 1.计时器的概念 (1).计时器是工程开发

【转】JMeter学习(二十五)HTTP属性管理器HTTP Cookie Manager、HTTP Request Defaults

Test Plan的配置元件中有一些和HTTP属性相关的元件:HTTP Cache Manager.HTTP Authorization Manager.HTTP Cookie Manager.HTTP Header Manager.HTTP Request Defaults等,这些是什么呢? JMeter不是浏览器,因此其行为并不和浏览器完全一致.这些JMeter提供的HTTP属性管理器用于尽可能模拟浏览器的行为,在HTTP协议层上定制发送给被测应用的HTTP请求. (1)HTTP Reque

第二十五部分_Struts2.1与Spring整合

依赖注入之后.对象销毁之前自动调用方法: 通过类似于之前Spring项目编码的方式,我们可以通过在setXXX()方法中输出相关的语句来获悉依赖关系注入的执行时机,通过下面介绍的方法可以在依赖关系注入完成之后自动执行一些方法. 如果我们想让一个类的实例在所有属性都已经设置好之后,就让它自动执行某些方法,有两种方式: 实现InitializingBean接口,并且实现里面的唯一方法afterPropertiesSet(),这个方法就会在所有的属性注入完之后自动的得到调用. 在该类中定义一个任意名称

IT十八掌作业_java基础第十五天_IO串行化/深度复制

1.使用RandomAccessFile实现文件合成. 2.使用java中的串行化技术实现对象图(100只猫Cat集合,但是Cat的owner不需要串行化)的深度复制. 3.阐述串行化的过程,应用场景,串行ID的用途. 1. package com.it18zhang; /** * 将分割的文件进行合成 */ import java.io.File; import java.io.FileInputStream; import java.io.RandomAccessFile; public c

Java基础第十五天_IO串行化/深度复制

1.使用RandomAccessFile实现文件切割. 答: package app_作业; import java.io.File; import java.io.FileOutputStream; import java.io.RandomAccessFile; public class app15_使用RandomAccessFile实现文件切割 { public static void main(String[] args) throws Exception { // 源文件 File 

Struts2(XWork)拦截器的功能介绍:

  拦截器 名字 说明 Alias Interceptor alias 在不同请求之间将请求参数在不同名字件转换,请求内容不变 Chaining Interceptor chain 让前一个Action的属性可以被后一个Action访问,现在和chain类型的result(<result type="chain">)结合使用. Checkbox Interceptor checkbox 添加了checkbox自动处理代码,将没有选中的checkbox的内容设定为false,