Struts2源代码分析之Struts2 Interceptor模式
首先贴出众所周知的Struts工作流程图:
流程解说:
1、客户端向Servlet容器(如Tomcat)提交一个请求
2、请求经过一系列过滤器(如ActionContextCleanUp过滤器等)
3、核心控制器被调用,询问ActionMapper来决定请求是否需要调用某个Action
4、如果ActionMapper决定需要调用某个Action,核心控制器把控制权委派给ActionProxy
(备注:JSP请求无需调用Action)
5、ActionProxy通过Configuration Manager询问框架的配置文件(struts.xml),找到需调用的Action类
6、ActionProxy创建一个ActionInvocation的实例
7、 ActionInvocation负责调用Action,在此之前会依次调用所有配置的拦截器
8、Action执行完毕,ActionInvocation负责根据结果码字符串在struts.xml的配置中找到对应的返回结果
9、拦截器被再次执行
10、过滤器被再次执行
l
寻找核心控制器,以下是struts2.3核心控制器StrutsPrepareAndExecuteFilter的关键代码:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
prepare.setEncodingAndLocale(request, response);
prepare.createActionContext(request, response);
prepare.assignDispatcherToThread();
if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
chain.doFilter(request, response);
} else {
request = prepare.wrapRequest(request);
//根据请求上下文获得一个ActionMapping
ActionMapping mapping = prepare.findActionMapping(request, response, true);
if (mapping == null) {
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
chain.doFilter(request, response);
}
} else {
//根据ActionMapping执行该Action,里面还隐藏着很多代码,此处execute是当前类的一个属性“protected
ExecuteOperations execute;”,通过跟踪我们发现” execute.executeAction”方法执行的实际上是另一个Dispatcher类的serviceAction方法
execute.executeAction(request, response, mapping);
}
}
} finally {
prepare.cleanupRequest(request);
}
}
l
寻找Dispatcher类的serviceAction方法:
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
ActionMapping mapping) throws ServletException {
//…省略
String timerKey = "Handling request from Dispatcher";
try {
UtilTimerStack.push(timerKey);
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod();
Configuration config = configurationManager.getConfiguration();
//创建代理ActionProxy
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, method, extraContext, true, false);
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
//执行该ActionProxy
// if the ActionMapping says to go straight to a result, do it!
if (mapping.getResult() != null) {
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {
proxy.execute();
}
} catch (Exception e) {
sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
} finally {
UtilTimerStack.pop(timerKey);
}
}
l
寻找代理Action的execute方法[ActionProxy是一个接口,我们观察他的一个缺省实现DefaultActionProxy]:
public class DefaultActionProxy implements ActionProxy, Serializable {
public String execute() throws Exception {
//…省略
try {
UtilTimerStack.push(profileKey);
//每个ActionProxy会持有一个唯一的ActionInvocation, struts2的Interceptor模式就在这里体现
retCode = invocation.invoke();
} finally {
if (cleanupContext) {
ActionContext.setContext(nestedContext);
}
UtilTimerStack.pop(profileKey);
}
return retCode;
}
}
l
寻找ActionInvocation
public class DefaultActionInvocation implements ActionInvocation {
//每个ActionInvocation对象持有一个InterceptorMapping对象组成的迭代器.
//根据前面的工作原理我们知道代理Action在执行真实Action的前后要执行Interceptor.这里就是struts2
Interceptor模式所在
protected Iterator<InterceptorMapping> interceptors;
public String invoke() throws Exception {
try {
if (interceptors.hasNext()) {
//从迭代器取出一个InterceptorMapping
final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
//从InterceptorMapping取出Interceptor执行其intercept方法,并将当前ActionInvocation实例传递给拦截器对象,核心所在,
//我们下面马上去看一个Interceptor的实现
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
}
finally {
UtilTimerStack.pop(interceptorMsg);
}
} else {
resultCode = invokeActionOnly();
}
return resultCode;
}
finally {
UtilTimerStack.pop(profileKey);
}
}
public String invokeActionOnly() throws Exception {
return invokeAction(getAction(), proxy.getConfig());
}
}
l
寻找Interceptor
public class AliasInterceptor extends AbstractInterceptor {
@Override public String intercept(ActionInvocation invocation) throws Exception {
//执行拦截器业务操作1.
//执行拦截器业务操作2.
return invocation.invoke();//通过ActionInvocation回调invoke方法,再次取出下一个InterceptorMapping
}
}
拦截器模式分析:
以上组后两个步骤体现出拦截器模式,为了便于理解,我用图示说明
解决问题:我们有个一个Action和一组拦截器,要拦截对对Action对象execute方法的执行,以使得在Action对象执行前后可以有机会执行拦截器,达到如下效果图:
实现流程:
流程说明:
1.ActionInvocation发起invoke调用,迭代器中有没有下一个拦截器Mapping,迭代器中有下一个拦截器Mapping,取出该拦截器并执行它的intercept方法,同时将当前实例传递给该方法[红色圆形]
2.执行第一个拦截器的拦截业务,执行完毕后调用上一步传递进来的ActionInvocation实例引用的invoke方法,再次发起对ActionInvocation.invoke的第二次调用[有点递归的意思J]
3.程序流程再次回调invoke方法,和第一部操作一样判断迭代器中有无下一个拦截器Mapping,有的话取出该拦截器并执行它的intercept方法,同时将当前实例传递给该方法[红色圆形]
4.执行第二个拦截器的拦截业务,执行完毕后调用上一步传递进来的ActionInvocation实例引用的invoke方法,再次发起对ActionInvocation.invoke的第三次调用[有点递归的意思J]
5.程序流程再次回调invoke方法,和第一部操作一样判断迭代器中有无下一个拦截器Mapping,但是现在迭代器已经到达末尾了,没有拦截器可以被调用执行,因此程序执行else部分【请观察上面贴出的ActionInvocation的代码】,执行真正的Action实例。
6.真实的 Action执行完毕后程序回到地4步的拦截器里面执行回调代码处,程序继续向下执行拦截器的后半部分代码[当然可能没有代码],由此看出对于当前这个拦截器而言,Action执行前和执行后都经过了该拦截器【想想既然如此我们可以在拦截器里做些什么?参数赋值、文件上传…】
7.当前拦截器的后半部分代码执行完了以后,程序回到第3步调用拦截器的intercept方法处继续执行,由此代码又回到了ActionInvocation,此处程序不在执行else部分【请观察上面贴出的ActionInvocation的代码】,因为执行时的上下文环境中迭代器是有下一个拦截器的。故方法直接执行结束。
8.上一步的ActionInvocation.invoke执行完毕后继续程序回到第2步的拦截器里面执行回调代码处,程序继续向下执行拦截器的后半部分代码[当然可能没有代码],由此看出对于当前这个拦截器而言,Action执行前和执行后都经过了该拦截器【想想既然如此我们可以在拦截器里做些什么?参数赋值、文件上传…】
9.当前拦截器的后半部分代码执行完了以后,程序回到第1步调用拦截器的intercept方法处继续执行,由此代码又回到了ActionInvocation,此处程序不在执行else部分【请观察上面贴出的ActionInvocation的代码】,因为执行时的上下文环境中迭代器是有下一个拦截器的。故方法直接执行结束。至此嗲用结束。
我们来简化一下下拦截器模式流程图:
时间紧张,代码不再示范,我们看看这位朋友的博客上的代码示范:
http://www.cnblogs.com/west-link/archive/2011/06/22/2086591.html
拦截器模式为我们提供了一种拦截方法调用或消息的途径,整个过程是自动的、透明的,下面是一个简单的拦截器示意图:
从图中可以看到,拦截器可以访问到方法调用的输入参数和返回结果,这样的话,拦截器能做的事儿就多啦,比如:
1、验证输入参数是否正确
2、偷偷地修改参数的值,例如参数类型的自动转换等
3、依赖注入
4、修改返回结果的内容、格式等
下面是一个包含我们要拦截的方法的类:
publicclass
Action{
//
拦截器集合的迭代器
private Iterator interceptors;
//
输入参数
private Parameter param;
//
返回结果
private Result result;
//
构造函数
public Action(Parameter param);
//
这个方法是我们要拦截的(具体实现见下面)
public Result invoke();
//
其他...
}
下面声明一个拦截器接口:
publicinterface
Interceptor{
public Result intercept(Action action);
}
我们可能会想到以下的拦截方式:
//
先在方法调用之前拦截一下,可以处理输入参数
intercept(param);
//
调用方法
result = action.invoke(param);
//
在方法调用之后再拦截一下,可以处理返回结果
intercept(result);
这种方式的缺点是不能完全地控制方法的调用,比如不能跳过方法的调用而直接返回结果、不能更改所在对象内部的状态等。
下面的实现也能达到方法调用的前后拦截,并且有完全控制该对象的能力
public Result invoke(){
if( interceptors.hasNext() ){
Interceptor interceptor = interceptors.next();
result = interceptor.intercept(this);
}
}
下面我们举几个拦截的具体例子:
//
参数合法性验证拦截器
public ParamInvalidator
implements Interceptor{
public Result intercept(Action action){
Paramemter param = action.getParam();
//
确保参数不为null
if(param ==
null){
addMessege("param is null!");
//
创建一个默认的参数对象
action.setParam(new Paramemter());
}
//
继续调用过程
return action.invoke();
}
}
一般来说,这类拦截器应该放在拦截器集合的最前面,所以,拦截器的被执行顺序是比较重要的!这依赖于具体的实现需求。
如果需求有要求:参数为null时直接返回一个null的结果,停止调用过程,那么我们可以把上面的方法改成这样:
public Result intercept(Action action){
Paramemter param = action.getParam();
if(param ==
null){
returnnull;
}
//
继续调用过程
return action.invoke();
}
所以,在所有的拦截器的实现中,action.invoke()这一行代码的位置或者有无非常重要,通过控制它我们就能在调用的前后进行拦截,甚至不调用它!
publicinterface
MoneyAware{
publicvoid
setMoney(int money);
}
//
依赖注入拦截器
public DependencyInjector
implements Interceptor{
public Result intercept(Action action){
//
如果它实现了MoneyAware接口,那么我们就给它注入5毛钱
if(action
instanceof MoneyAware){
action.setMoney(5);
}
//
继续调用过程
return action.invoke();
}
}
从这个拦截器可以看出,我们能修改被拦截对象的内部状态。
//
结果格式化拦截器
public ResultFormater
implements Interceptor{
public Result intercept(Action action){
//
先调用要拦截的方法
result = action.invoke();
//
将结果格式化
formatResult(result);
//
返回结果
return result;
}
}
这个拦截器就是方法调用后的拦截,所以这种拦截器被执行的时候被放在调用堆栈的最下面,当其他拦截器执行完后,它才被执行!
分享到:
相关推荐
在分析Struts2的源代码之前,你需要首先获取Struts2的源代码,可以通过访问http://www.opensymphony.com/xwork/download.action下载XWork的源码,因为它构成了Struts2的核心。下载的源代码压缩包名为struts-2.1.0-...
6. **Interceptor**: 拦截器是Struts2的核心特性之一,允许在Action执行前后插入自定义逻辑,如日志记录、权限验证等。 7. **ValueStack**: 用于存储Action的属性和OGNL表达式,便于在视图层和控制器之间传递数据。...
Struts2作为一款流行的Java Web框架,其强大的功能之一就是拦截器(Interceptor)。拦截器在MVC模式中扮演着重要角色,它可以对请求进行预处理和后处理,提供了灵活的扩展机制,使得业务逻辑与表现层更加分离。本文...
在深入理解Struts2的工作原理时,源码分析是必不可少的步骤。Struts2的核心设计理念和设计模式相比Struts1.x有了显著的变化,这使得它成为一个独立且成熟的框架。 首先,Struts2的架构基于WebWork的核心,这意味着...
通过深入阅读"struts2源代码分析(个人觉得非常经典).doc"、"struts2源代码分析.docx"和解压后的"struts2源代码.rar",你可以获得Struts2框架的全面理解,从而更好地利用这个框架进行Web应用的开发和维护。同时,源码...
通过分析这个"北大青鸟 struts2 项目源代码",你可以了解到Struts2框架的实战应用,包括请求处理流程、配置文件的编写、Action的设计和实现、视图的渲染以及拦截器的使用。这有助于你更深入地理解和掌握Struts2框架...
在MyEclipse9中,你可以导入这些源码,通过调试和阅读代码,理解Struts2的工作原理。对于初学者来说,这是一条深入理解MVC框架的好途径。你可以逐步分析Action类如何被调用,拦截器如何影响Action的执行流程,以及...
本资料包包含的是《Struts2深入详解》一书的源码分析,涵盖了从第一章到第五章的内容,并附带了相关的jar包,方便读者结合理论与实践进行学习。 首先,让我们从第一章开始,Struts2的基础知识。这一章通常会介绍...
总的来说,Struts2的源码分析涉及Action的创建与执行、Interceptor的调用链、FilterDispatcher的请求调度以及Result的展示机制。通过对这些关键组件的深入理解和代码研究,开发者可以更好地掌握Struts2框架,提高...
李兴华是一位知名的IT教育专家,他在讲解Struts2.0源代码时,通常会深入解析框架的核心机制、工作流程以及如何通过源码理解其实现原理。在“mldn 李兴华 struts2.0 源代码”这个主题中,我们可以学习到以下几个关键...
总的来说,这篇“Struts2源码解读”的博文应该是对Struts2核心机制进行了详细的解析,包括Action、Interceptor、Result等关键组件的工作原理,以及整个请求处理流程。通过学习这些内容,开发者可以深化对Struts2的...
深入学习Struts2的源码,有助于理解其运行机制,从而更好地优化代码、调试问题,甚至开发自己的扩展。对于Java Web开发者来说,掌握Struts2的基本原理和使用技巧,能够显著提高开发效率和应用质量。
通过分析这个Struts2项目源码,你可以学习以下技能: 1. 如何创建Action类,并定义其方法与用户请求对应。 2. 理解配置文件`struts.xml`的结构和作用。 3. 掌握Action与视图的交互,了解结果类型的应用。 4. 学习...
这份"最新Struts2开源实例代码以及Struts2源码"包含了Struts2框架的最新版本2.3.12的应用示例和源码,对于深入理解Struts2的工作机制和开发实践非常有帮助。 首先,我们来看`struts-2.3.12-apps.gz`,这个文件很...
深入研究Struts 1.3.10源代码,可以帮助我们掌握Web应用开发的基本架构,理解MVC模式在实践中的应用,以及学习如何通过配置和编程控制请求流程。同时,通过分析源码,还可以学习到如何处理HTTP请求,验证用户输入,...
本实例"Struts2典型小实例源代码"旨在帮助理解Struts2的核心概念和工作流程。 首先,我们来看"Struts2ActionMethodsExample3",这很可能是展示如何在Struts2中定义和调用Action类的方法。在Struts2中,Action类是...