`
foo
  • 浏览: 25074 次
  • 来自: 北京
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

Struts2源代码分析之Struts2 Interceptor模式

阅读更多

首先贴出众所周知的Struts工作流程图:

clip_image003

流程解说:

clip_image001       1、客户端向Servlet容器(如Tomcat)提交一个请求

 

 

 

 

clip_image001       2、请求经过一系列过滤器(如ActionContextCleanUp过滤器等)

 

 

 

 

clip_image001       3、核心控制器被调用,询问ActionMapper来决定请求是否需要调用某个Action

 

 

 

 

clip_image001       4、如果ActionMapper决定需要调用某个Action,核心控制器把控制权委派给ActionProxy (备注:JSP请求无需调用Action

 

 

clip_image001       5ActionProxy通过Configuration Manager询问框架的配置文件(struts.xml),找到需调用的Action

 

 

 

 

clip_image001       6ActionProxy创建一个ActionInvocation的实例

 

 

 

 

clip_image001       7 ActionInvocation负责调用Action,在此之前会依次调用所有配置的拦截器

 

 

 

 

clip_image001       8Action执行完毕,ActionInvocation负责根据结果码字符串在struts.xml的配置中找到对应的返回结果

 

 

 

 

clip_image001       9、拦截器被再次执行

 

 

 

 

clip_image001       10、过滤器被再次执行

 

 

clip_image001      

 

 

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         寻找代理Actionexecute方法[ActionProxy是一个接口,我们观察他的一个缺省实现DefaultActionProxy]:

public class DefaultActionProxy implements ActionProxy, Serializable {

 

 

        public String execute() throws Exception {

 

 

        //省略

 

 

        try {

 

 

            UtilTimerStack.push(profileKey);

 

 

          //每个ActionProxy会持有一个唯一的ActionInvocation, struts2Interceptor模式就在这里体现

 

 

            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对象执行前后可以有机会执行拦截器,达到如下效果图:

 

 

clip_image005

 

 

实现流程:

 

 

clip_image007

 

 

流程说明:

 

 

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的代码】,因为执行时的上下文环境中迭代器是有下一个拦截器的。故方法直接执行结束。至此嗲用结束。

 

 

 

 

 

我们来简化一下下拦截器模式流程图:

 

 

clip_image009

 

 

时间紧张,代码不再示范,我们看看这位朋友的博客上的代码示范:

http://www.cnblogs.com/west-link/archive/2011/06/22/2086591.html

拦截器模式为我们提供了一种拦截方法调用或消息的途径,整个过程是自动的、透明的,下面是一个简单的拦截器示意图:

 

 

clip_image011

从图中可以看到,拦截器可以访问到方法调用的输入参数和返回结果,这样的话,拦截器能做的事儿就多啦,比如: 
  1
、验证输入参数是否正确 
  2
、偷偷地修改参数的值,例如参数类型的自动转换等 
  3
、依赖注入 
  4
、修改返回结果的内容、格式等

 

 

  下面是一个包含我们要拦截的方法的类:

 

 

public class Action{ 
      
// 拦截器集合的迭代器 
      private Iterator interceptors; 
      
// 输入参数 
      private Parameter param; 
      
// 返回结果 
      private Result result; 
      
// 构造函数 
      public Action(Parameter param); 
      
// 这个方法是我们要拦截的(具体实现见下面) 
      public Result invoke(); 
      
// 其他... 
  }

 

 

 

 

下面声明一个拦截器接口:

 

 

public interface 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){ 
        
return null
    } 
    
// 继续调用过程 
    return action.invoke(); 
}

 

 

 

 

所以,在所有的拦截器的实现中,action.invoke()这一行代码的位置或者有无非常重要,通过控制它我们就能在调用的前后进行拦截,甚至不调用它!

 

 

public interface MoneyAware{ 
    
public void 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 Interceptor详解

    NULL 博文链接:https://weizhilizhiwei.iteye.com/blog/1005210

    struts2完全教程源代码

    struts2完全教程源代码 ognl session scope validator interceptor

    深入浅出Struts2(附源码)

    作者处处从实战出发,在丰富的示例中直观地探讨了许多实用的技术,如数据类型转换、文件上传和下载、提高Struts 2应用的安全性、调试与性能分析、FreeMarker、Velocity、Ajax,等等。跟随作者一道深入Struts 2,聆听...

    Struts 2.1.8_学习源码

    Struts 2.1.8 学习源码内容 Struts2_01FirstDemo : 跑通第一个Struts2的实例 Struts2_02CURD : 关于Struts2的增、删、...Struts2_05Interceptor : Struts2拦截器的使用 Struts2_06FileUpload : Struts2上传文件的使用

    struts2 拦截器interceptor教程 示例源码

    struts2 拦截器_教程.zip

    Struts2入门教程(全新完整版)

    7.开启struts2自带的开发模式常量 6 8.vo传参模式 7 9.ModerDriven传参模式(不建议采用) 7 10.为什么要使用struts2代替struts1.x 7 二、struts.xml配置及例程 7 1.配置文件的优先级 7 2.配置形式 8 3.package配置...

    struts2流程与流程图

    一个请求在Struts 2框架中的处理大概分为以下几个步骤。...Struts 2的核心控制器是FilterDispatcher,有3个重要的方法:destroy()、doFilter()和Init(),可以在Struts 2的下载文件夹中找到源代码,如代码1所示。

    Struts2演示源码

    该代码演示了Action中result的四种转发类型、多文件上传、自定义拦截器、对Action中方法进行输入校验以及OGNL表达式等内容。

    Struts拦截器及token拦截器防止重复提交例子源码

    一、理解Struts2拦截器 1. Struts2拦截器是在访问某个Action或Action的某个方法,字段之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现. 2. 拦截器栈(Interceptor Stack)。Struts2...

    Struts2拦截器详解源码

    Interceptor拦截器详解和部分源码的指导,方便深入学习struts2,增加理解

    Struts2 的核心——拦截器[Interceptor]

    NULL 博文链接:https://wang4717.iteye.com/blog/796941

    Struts2源码分析之ParametersInterceptor拦截器

    主要介绍了Struts2源码分析之ParametersInterceptor拦截器,ParametersInterceptor拦截器其主要功能是把ActionContext中的请求参数设置到ValueStack中,,需要的朋友可以参考下

    struts2 拦截器的使用(继承抽象拦截器)

    NULL 博文链接:https://johnson2132.iteye.com/blog/720398

    牛牧struts2.x授课源码(全)

    自己边看视频边做的~ ...Structs2_Interceptor Structs2_Model2 Structs2_OGNL1 Structs2_OGNL2 Structs2_OtherTag Structs2_ResultTypes Structs2_TypeConvert Structs2_UITag Structs2_Validation

    Struts 2的基石——拦截器(Interceptor)

    NULL 博文链接:https://chenyajie.iteye.com/blog/1980865

    学生信息管理系统 JSP课程设计(内含完整源码和设计说明手册)

    学生成绩管理系统JSP课程设计 可实现学生和老师两个端的登录;...等功能 运用技术: ...3、数据验证:HTML5的原生验证模式、Struts2的Validation验证框架以及Interceptor拦截器、JavaEE的Filter过滤器

    Strut2 源码API +TagAPI +示例

    Strut2 源码API +TagAPI +示例 在Struts 2中实现文件上传 - Max On Java - BlogJava.files Struts 2的基石——拦截器(Interceptor) - Max On Java - BlogJava.htm Struts 2中的OGNL - Max On Java - BlogJava.htm

    struts自我学习过程程序以及说明

    本资源包括struts的学习程序,能够完全运行,当然,我是完全调试运行出来的,都是源代码原封上传,还有说明文档。还有自我总结资料,放到下一个文件夹当中上传,这里压缩只有这么多了,希望能去下载,那个全部是文档...

    当当网全套源码(附带邮箱验证功能)

    Struts2,JDBC(连接池),jQuery,Ajax 2)设计思想 MVC和分层设计思想 a.显示层:JSP组件(jQuery,Ajax) b.控制层:Struts2控制器组件、Action组件 c.业务层:Bean组件 d.数据访问层:DAO组件(JDBC) 4.数据库...

    SpringBoot 自定义拦截器 的 源代码

    SpringBoot 自定义拦截器 的 源代码 SpringBoot 自定义拦截器 一、自定义拦截器 二、编写控制器 三、添加拦截器对象,注入到容器的配置类中 另一种写法 四、最后application运行 我们对拦截器并不陌生,无论是 ...

Global site tag (gtag.js) - Google Analytics