• 1. 第九章:拦截器 主讲:刘雷
  • 2. 学习目标拦截器的概念 配置拦截器 使用拦截器
  • 3. 拦截器的概念拦截器(Interceptor)是动态拦截Action调用的对象,类似于Servlet中的过滤器。在执行Action的业务逻辑处理方法(execute())之前,Struts2会首先执行在struts.xml中引用的拦截器。 拦截器是struts2的一个重要特性。Struts2框架的大多数核心功能都是通过拦截器来实现的,像避免表单重复提交、类型转换、对象组装、验证、文件上传等,都是在拦截器的帮助下实现的。拦截器之所以称为“拦截器”,是因为它可以在Action执行之前和执行之后拦截调用。 Struts2将它的核心功能放到拦截器中实现,而不是分散到Action中实现,有利于系统的解耦,使得功能的实现类似于个人电脑的组装,变成了可插拔的,需要某个功能就“插入”一个拦截器,不需要某个功能就“拔出”一个拦截器。你可以任意组合拦截器来为Action提供附加的功能,而不需要修改Action的代码。
  • 4. 拦截器的工作方式拦截器围绕着Action和Result的执行而执行,其工作方式如图: 从图中可以看到,在Action和Result执行之前,为Action配置的拦截器将首先被执行,在Action和Result执行之后,拦截器将重新获得控制权,然后按照与先前调用相反的顺序依次执行。在整个执行过程中,任何一个拦截器都可以选择直接返回,从而终止余下的拦截器、Action和Result的执行。例如:当一个未授权的用户访问受保护的资源时,执行身份验证的拦截器可以直接返回。
  • 5. Struts2内置拦截器Struts2内建了大量的拦截器,这些拦截器以name-class对的形式配置在 struts-default.xml文件中,其中name是拦截器的名字,就是以后使用该拦截器的唯一标识;class则指定了该拦截器的实现类,如果我们定义的package继承了Struts2的struts-default包,则可以自由使用下面定义的拦截器,否则必须自己定义这些拦截器。
  • 6. Struts2内置拦截器大部分时候,开发者无需手动控制这些拦截器,因为struts-default.xml文件中已经配置了这些拦截器,只要我们定义的包继承了系统的struts-default包,就可以直接使用这些拦截器。
  • 7. 编写拦截器类在struts2中要编写拦截器类,必须实现com.opensymphony.xwork2.interceptor.Interceptor接口,该接口定义了如下的三个方法: void init(): 该方法在拦截器实例创建后、intercept()方法被调用之前调用,用于初始化拦截器所需要的资源,例如数据库连接的初始化。该方法只执行一次。 void destroy(): 该方法在拦截器实例被销毁之前调用,用于释放在init()方法中分配的资源。该方法只执行一次。 String intercept(ActionInvocation invocation) throws Exception: 该方法在Action执行之前被调用,拦截器为Action提供的附加功能在该方法中实现。利用invocation参数,可以获取action执行的状态。在intercept()方法中,如果要继续执行后续的部分(包括余下的应用于Action的拦截器、Action和Result),可以调用invocation.invoke()。如果要终止后续的执行,可以直接返回一个结果码,框架将根据这个结果码来呈现对应的结果视图。
  • 8. TimerInterceptor.javaimport com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.Interceptor; public class TimerInterceptor implements Interceptor { public void destroy(){} public void init(){} public String intercept(ActionInvocation invocation) throws Exception { long startTime = System.currentTimeMillis(); String result = invocation.invoke(); long executionTime = System.currentTimeMillis() - startTime; System.out.println("Action的执行花费的毫秒数是:" + executionTime); return result; } }
  • 9. Struts.xml index.jsp
  • 10. 说明这是一个非常简单的拦截器,用于输出Action执行花费的时间。在invocation.invoke()调用的前后,你可以添加自己的逻辑代码。Invocation.invoke()调用之前的代码将在Action执行之前执行,invocation.ivoke()调用之后的代码将在Action执行之后执行。 为了简化拦截器的开发,struts2还提供了一个抽象类: com.opensymphony.xwork2.interceptor.AbstractInterceptor 它实现了Interceptor接口,并给出了init()和destroy()方法的空实现。我们编写的拦截器类也可以选择继承AbstractInterceptor类,如果不需要init()和destroy()方法,那么你只需要重写抽象的intercept()方法就可以了。 Struts2还提供了一个特殊的拦截器抽象基类: com.opensymphony.xwork2.interceptor.MethodFilterInterceptor 这个拦截器可以指定要拦截或排除的方法列表。通常情况下,拦截器将拦截Action的所有方法调用,但在某些应用场景中,对某些方法的拦截将会出现一些问题。例如:对表单字段进行验证的拦截器,当我们通过doDefault()方法输出表单时,该方法不应该被拦截,因此此时表单字段都没有数据。
  • 11. 说明在struts2中,从MethodFilterInterceptor继承的拦截器类有: TokenInterceptor TokenSessionStoreInterceptor DefaultWorkflowInterceptor ExecuteAndWaitInterceptor ValidationInterceptor ParametersInterceptor PrepareInterceptor MethodFilterInterceptor通过指定included/excluded方法列表来选择拦截器或排除的方法,可以设置的参数如下: excludeMethods-------要排除的方法。 includeMethods--------要拦截的方法。 例如:有如下的拦截器配置: input,back,cancel execute
  • 12. 说明当执行Action的input、back和cancel方法时,验证拦截器将不执行对输入数据的验证。当执行Action的execute方法时,验证拦截器将执行对输入数据的验证。 在设置拦截器或排除的方法时,如果有多个方法,那么以逗号(,)分隔,如上所示。如果一个方法的名字同时出现在execludeMethods和includeMethods参数中,那么它会被当作要拦截的方法。也就是说, includeMethods优先于execludeMethods。 在编写拦截器类的时候要注意,拦截器必须是无状态的,换句话说,在拦截器类中不应该有实例变量。这是因为struts2对每一个体Action的请求使用的是同一个拦截器实例来拦截调用,如果拦截器有状态,在多个线程(客户端的每个请求将由服务器端的一个线程来服务)同时访问一个拦截器实例的情况下,拦截器的状态将不可预测。
  • 13. 拦截器栈在很多时候,且些指定的拦截器需要被多个Action所使用,这个时候,如果我们为每一个Action都分别配置拦截器的话,不仅麻烦,而且不利后期的维护,此时就需要用到拦截器栈。 所谓拦截器栈就是将一些拦截器组合起来进行统一管理。 拦截器栈的使用与拦截器的使用非常相似,唯一有区别的地方,就是拦截器栈的布署方式。
  • 14. 配置拦截器一在配置拦截器的时候可以为拦截器传递参数,对于已经配置好的拦截器,在引用时还可以覆盖它的参数。 例如,defaultStack拦截栈中的validation拦截器配置如下: input,back,cancel 在Action配置中,我们想要覆盖validation拦截器的excludeMethods参数的设置,可以配置如下: default index.jsp 但这样会有一个问题,这是因为defaultStack拦截器栈包含了validation和workflow拦截器,这两个拦截器都可以有excludeMethods参数,采用上述方式,框架将无法知道你是要为哪一个拦截器传递参数。
  • 15. 配置拦截器二为了覆盖defaultStack拦截器栈中validation拦截器的excludeMethods参数设置,我们可以在Action的配置中直接引用defaultStack拦截器栈中的所有拦截器,然后在引用validation拦截器时,设置参数,如下所示:
  • 16. 配置拦截器三很明显,这种配置太麻烦,也太冗余了。为此,struts2为我们提供了另外一种方式来覆盖拦截器的参数设置,那就是在指定参数名时,加上拦截器名前缀,例如:validation.excludeMethods.采用这种方式,我们仍然可以继续引用defaultStack拦截器栈,如下所示: default index.jsp 这样一来就简洁多了。
  • 17. 安全验证拦截器一<%@ page language="java" import="java.util.*" pageEncoding="gbk"%> <%@ taglib prefix="s" uri="/struts-tags" %>
    login.jsp
  • 18. 安全验证拦截器二<%@ page language="java" import="java.util.*" pageEncoding="gbk"%> <%@ taglib prefix="s" uri="/struts-tags" %> 欢迎你登录后台系统
    success.jsp
  • 19. 安全验证拦截器三LoginAction.java
  • 20. 安全验证拦截器三LoginAction继承自ActionSupport类,这是因为我们要利用基类的addActionError()方法来添加用户登录失败的提示信息。LoginAction还实现了SessionAware接口,ServletConfig拦截器将会向LoginAction注入session Map对象。 当用户登录成功后,我们将user对象保存到session中。在我们编写的执行安全验证的拦截器中,可以根据session中是否存在user对象,来判断用户是否登录。
  • 21. 安全验证拦截器四AuthenticationInterceptor拦截器负责判断用户是否已经登录,如果用户已经登录,则继续执行余下的拦截器、Action和Result的调用;否则,返回Action.LOGIN结果代码,将请求重定向到登录页面。 AuthenticationInterceptor类的代码如下:
  • 22. 安全验证拦截器五 struts.xml配置Action和拦截器
  • 23. 举例说明如果一个结果在多个Action或者拦截器中使用,那么就应该将它定义为全局的结果,就像本例的login结果。 resource action的配置没有指定类名,因此默认使用com.opensymphony.xwork2.ActionSupport类。由于我们只是利用Action来转发请求,所以简单地使用ActionSupport类就可以了。 注意:只对需要保护的Action设置引用securityStackk拦截器栈;对于LoginAction,本身就是用来登录的, 因此不要对它引用securityStack拦截器栈。 测试与发布: http://localhost:8080/项目名/resource.action 在登录后,再次访问/resource.action。看看结果。
  • 24. 拦截器的高级应用如何自定义拦截器 指定拦截器作用于某个方法 多个拦截器的执行顺序 拦截器在解决重复提交中的作用
  • 25. 如何自定义拦截器实现Interceptor接口 继承AbstractInterceptor类,此类实现了Interceptor接口 继承MethodFilterInterceptor,可实现方法过滤拦截
  • 26. struts.xml
  • 27. 指定拦截器作用于某个方法需要拦截器作用的方法,使用excludeMethods方法 不需要拦截器作用的方法,使用includeMethods方法
  • 28. struts.xml测试: http://localhost:8888/testInterceptor_self/test1.action http://localhost:8888/testInterceptor_self/test2.action
  • 29. 多个拦截器的执行顺序
  • 30. 三个拦截器类
  • 31. struts.xml
  • 32. 拦截器在解决重复提交中的作用标签 Token或TokenSession拦截器
  • 33. 文字过滤拦截器实例说明 例如:网上有些论坛要求会员发帖的内容不能带有脏字,如果会员发帖时使用不文明语言,通常情况下,系统会以“*”替代这些脏字。在struts2中可以使用拦截器来实现这个功能。
  • 34. Action类
  • 35. 拦截器类
  • 36. struts.xml配置文件
  • 37. 成功页面
  • 38. 谢谢!