开发者学堂课程【Java Web开发系列课程 - Struts2框架入门:拦截器】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/537/detail/7315
拦截器
内容介绍:
一、拦截器的定义
二、拦截器的作用
三、拦截器与过滤器的区别
四、拦截器的执行流程
五、拦截器的实现
一、拦截器的定义
1.STRUTS2框架内部流程图分析
拦截器与过滤器十分相似。过滤器是指过滤所有操作,在每次请求一个 Jsp 或 Servlet 之前,均会首先进入一次过滤器,执行结束后,然后二次通过过滤器返回。
拦截器与此极为相似,在执行一个 Action 之前,首先会经过一系列的拦截器,Action执行结束后,再次经过一系列拦截器返回。为了方便理解,我们结合 STRUTS2框架内部流程图进行分析。
其中 Interceptor 1、Interceptor 2、Interceptor 3即为拦截器,从该框架内部流程图可知,在执行 Action 之前,首先经过这一系列拦截器,执行结束后,再次通过这一系列拦截器。
STRUTS2框架内部流程图如下:
2. 拦截器与拦截器栈
Interceptor 拦截器类似于我们前面学过的过滤器,是可以在 action 执行前后执行的代码,是我们在做 web 开发时经常用到的技术,如权限控制、日志等。我们也可以将多个 Interceptor 连接在一起组成 Interceptor 栈。
Struts 拦截器,每个拦截器只有一个对象实例,因此,采用单例模式(多个过滤器组成过滤器链,而过滤器也属于单例模式),所有引用这个拦截器的 Action 都共享这一拦截器实例,因此,在拦截器中如果使用类变量,要注意同步问题,也就是要注意线程安全问题。
(1)拦截器
拦截器是在访问某个方法、字段之前或之后实施拦截;
拦截器是 AOP 的一种实现(AOP 在 Spring 中会进行详细介绍,可简单理解为其是通过动态代理实现的);
(2)拦截器栈
拦截器栈就是将拦截器按一定的顺序联结成一条链;
在访问拦截器的方法或字段时,拦截器链中的拦截器就会按照之前定义的顺序依次被调用;
(3)实现原理
Struts2拦截器的实现原理相对简单,当请求 Struts2的 action 时,Struts2会查找配置文件,并根据配置实例化相应拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器。
3. 总结
(1)拦截器
拦截器与过滤器很相似,在 action 执行的前后执行,Struts2的核心功能都是通过拦截器实现的。
前几节我们学习过 Struts2的设值、Struts2的类型转化、Struts2的结构、Struts2的验证,以及之前学习过的 model driven(模型驱动),和之后要学习到的文件的上传与下载,这些功能都要依靠拦截器来实现。
也可以自行编写拦截器以增强 Struts2的功能。这也体现了 Struts2深受开发者喜爱的原因,即虽然 Struts2的功能很多,但是我们可以自行去配置其功能,如不同的应用其应用方向不同,所需的配置也不同,我们根据个人需求,增加或去除相应的功能,以在一定程度上提高执行效率。
(2)拦截器栈
由多个拦截器组成组成。从Struts2的工作方式出发,Struts2首先读取配置文件 struts-default.xml,其中定义了很多拦截器,默认情况下使用的是名为 default Stack的拦截器栈,其中包含18个拦截器,如下:
且其中部分拦截器不会被使用,如若不使用 model driven,拦截器 ModelDriven 就不会被使用,再如不进行文件上传操作,那么拦截器 fileUpload 就不会被使用,再如不使用验证功能,则拦截器 validaction 就不会被使用,再如不使用类型转化功能,则拦截器 conversionError 就不会被使用,等等。
因此,在了解每个拦截器的功能之后,就可以挑选自己必须使用的拦截器进行配置。而各个拦截器组成了一个栈,即在执行 action 之前会按照顺序依次执行拦截器代码,再执行 action,因此,我们可以自行对其进行装配。
二、拦截器的作用
之前我们学过,公共的数据可以在 filter 中进行处理,对于 action 的一些公共处理代码可以放到拦截器中进行实行,代码可以重复利用,可以相应地提高效率。
如:权限控制、日志等公共的内容都可以放到拦截器中进行。前文中提到“拦截器是AOP 的一种实现”,其实 AOP 也会进行公共代码处理。
三、拦截器与过滤器的区别
两者的概念十分地相似,但也有一些不同之处:
1.两者的过滤内容不同。
过滤器属于 Web 容器,可以过滤一切请求(包括 action、servlet、jsp、html 等),而拦截器隶属于 Struts2框架,只能拦截 action,无法拦截对 jsp 的请求。
2. 两者的实现方式不同。
过滤器内部采用函数回调来实现,而拦截器采用动态代理来实现。
四、拦截器的执行流程
拦截器与过滤器有许多共同之处。在执行action之前,会依次经过多个拦截器,这多个拦截器之间的执行是通过多个连接是通过责任链的设计模式来实现的。
拦截器的执行流程图见下页:
根据图示可知,请求进入后,首先执行 filter,接下来依次执行第1个、第2个、…、第N 个拦截器,再执行 action,再执行 result 结果值,值再通过这N个拦截器,返回。整个过程采用的是责任链的方式进行的。
这个流程图十分重要,要求必须掌握对于设计模式的内容大家在课后可以自行了解,这部分内容也又很大的实际意义,但是一般在编写管理系统时,对设计模式的涉及量较少。
五、拦截器的实现步骤
1. 编写拦截器
包括两种方式,即实现 Interceptor 接口或继承 Abstract Interceptor 类。
2. 在 struts.xml 中配置拦截器
3. 在 action 中引用拦截器
以下为实现过程案例:
在已有的文件夹中 Copy Project,并命名 Project name 为13struts2_interceptor。
关闭原文件,点击新建的 Project,在其“type filter text”中点击“Web”,将其Web Context-root 改为/interceptor,将 src -- cn.sxt.action 中的文件删除。然后点击上方操作栏中的“New Java Class”,在 Name 中输入 HelloAction。并对弹出页面进行编辑:
package cn.sxt.action;
import com.opensymphony.xwork2.Action;
public class HelloAction {
public String execute () {
System.out.println("execute");
return Action.SUCCESS ;
}
左侧目录中点击 struts.xml 进行配置(以下均展示部分配置文件),
编辑图中红色框线中的内容,对 action 进行配置,即改为如下内容:<action name=”hello”class=”cn.sxt.action.HelloAction”>
<result>/index.jsp</result>
<action>
左侧目录中点击 WebRoot,将其中的 register.jsp 删除。
左侧目录中点击 src,再在右侧点击 New Java Package,新建一个包,并命名为cn.sxt.interceptor。再点击 New Java Class 建立拦截器,此处我们尝试建立一个时间拦截器 TimeInterceptor,用来计算 action 的执行时间
但其实在实际应用中,时间拦截器并没有太大的实际意义,且时间拦截器在 Struts2中也执行了相关操作。
点击 Package Explorer,寻找 interceptor 中的 TimerInterceptor 查询源码,然后在右侧点击 TimerInterceptor.class 中的 Attach Source 进行关联,其 Location path 为 E:/resouces/150526/stru
ts2/xwork-assembly-2.1.6-all.zip.
再点击 External File。
可以查询得知其操作,即写入日志等,如下:
说明该拦截器主要在 invocation 中执行 invokeUnderTiming,双击invokeUnderTiming 可以发现其内包含的计算时间的操作。
其中包含了起始时间、执行 result 与重视时间。
以下为具体的时间拦截器配置过程:
(1)实现 Interceptor 接口
public class TimeInterceptor implements Interceptor {
//注意此处的Interceptor的包一定要用引用正确,
即 com.opensymphony.xwork2.interceptor
//且其中包括很多方法,核心的方法是 intercept
//与过滤器相近,过滤器的核心方法是 doFilter
//通过此处 intercept 方法可以得出 ActionInvocation(见 Struts2拦截器示意图中拦截器左侧的框图)
}
}
(2)继承 Abstract Interceptor 类
<!--编写拦截器-->
public class TimeInterceptor extends AbstractInterceptor {
//双击AbstractInterceptor发现其并未进行任何操作
//此处提供两种方法的原因:
“继承”可以节省代码编写;
“类”仅支持单继承,若一个软件需要其他的类实现某些功能,只能通过实现接口的方式进行,故而不能只提供类继承这一种方式。
也就是设计模式中的适配器模式,这种模式在 Spring 中使用较多,在做窗体应用程序时,有很多事件与实现。虽然并未系统学习过设计模式的内容,但其实我们在很多情况下都在使用它。
本节多次使用了设计模式,在 structs、interceptor、拦截器中就使用了设计模式,如拦截器的实现是一种 AOP 的方式,即代理设计模式,多个拦截器一同工作时,每个拦截器拦截后再进入另一个拦截器的过程是一个责任链的设计模式,包括多个设计模式,即组合设计模式。
@Override
public string intercept (ActionInvocation invocation) throws
Exception
long start =System.currentTimeMillis();
//long 类型,记录开始时间
//执行下一个拦截器,当拦截器执行结束后,执行 action
String result =invocation.invoke();
//返回结果类型为 string,且方法为 invocation.invoke
long end =System.currentTimeMillis();
//long 类型,记录当前执行结束的时间
System.out.println("执行 action 所用时间为:"+(end-start)
+"ms");
return result;
}
}
<!--配置拦截器-->
其实在刚刚的 struts-default.xml 中的<interceptors>中就配置了
很多拦截器。
具体配置方法如下:
在 struts.xml 中的 package 中进行配置:
<package name=”default”extends=”struts-default”name
space=”/”>
<interceptors>
//“interceptors”中的“s”可理解为英语中的复数,故指可配置多
个拦截器
<interceptor name=”time”class=”cn.sxt.interceptor.
TimeInterceptor”>
</interceptors>
<!--引用拦截器-->
<action name=”hello”class=”cn.sxt.interceptor.HelloAc
tion”>
<result>/index.jsp</result>
<interceptor-ref name=”time”/>
</action>
<package>
</struts>
运行代码,点击 Console 查看结果。
然后在地址为 http://localhost:8080/interceptor/hollo.action 的网页进行搜索,显示结果为“This is my JSP page”,返回后 console 界面显示“执行该 Action 所用时间为:1262ms”。
该次执行时间较长,再次执行,显示“执行该 Action 所用时间为:3ms”,而此时仅执行了上面编写的这一个拦截器。若电脑的 CPU 足够迅速,甚至会显示0ms。