zoukankan      html  css  js  c++  java
  • 拦截器、执行Struts2拦截器原理by小雨

    发一下牢骚和主题无关:

        

    Struts2拦截器理原  

        

     

        拦截器是struts2处置的心核,本文主要说struts2的拦截器的基本理原/实现,其它框架处置的货色就不说了,得自己再看了。
    struts2本版:2.2.3

        

        当一个求请来了后,从org.apache.struts2.dispatcher.ng.filter.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);
                
    //建创actionContext
                prepare.createActionContext(request, response);
                prepare.assignDispatcherToThread();
       //如果不是struts的求请则续继由其它过滤器执行
                if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
                    chain.doFilter(request, response);
                } else {
    //包装request,对有文件上传的特别处置下
                    request = prepare.wrapRequest(request);
    //查找对应的ActionMapping
                    ActionMapping mapping = prepare.findActionMapping(request, response, true);
    //如果找不到ActionMapping则作当静态资源来处置
                    if (mapping == null) {
                        boolean handled = execute.executeStaticResourceRequest(request, response);
                        if (!handled) {
                            chain.doFilter(request, response);
                        }
                    } else {
       //应用ActionMapping来执行action
                        execute.executeAction(request, response, mapping);
                    }
                }
            } finally {
                prepare.cleanupRequest(request);
            }
        }

       
    踪跟execute.executeAction(),则到了 org.apache.struts2.dispatcher.Dispatcher,如下:

        

         public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
                                  ActionMapping mapping) throws ServletException {
            Map<String, Object> extraContext = createContextMap(request, response, mapping, context);

            // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
            ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
            boolean nullStack = stack == null;
            if (nullStack) {
                ActionContext ctx = ActionContext.getContext();
                if (ctx != null) {
                    stack = ctx.getValueStack();
                }
            }
            if (stack != null) {
                extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
            }

            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();
       //应用StrutsActionProxyFactory(ActionProxyFactory的一个实现 )建创action代理对象
       //proxy实际上是org.apache.struts2.impl.StrutsActionProxy类型
                ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                        namespace, name, method, extraContext, true, false);


                request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());

                // 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 {
    //执行action
                    proxy.execute();
                }

                // If there was a previous value stack then set it back onto the request
                if (!nullStack) {
                    request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
                }
            } catch (ConfigurationException e) {
                // WW-2874 Only log error if in devMode
                if(devMode) {
                    String reqStr = request.getRequestURI();
                    if (request.getQueryString() != null) {
                        reqStr = reqStr + "?" + request.getQueryString();
                    }
                    LOG.error("Could not find action or result\n" + reqStr, e);
                }
                else {
                    LOG.warn("Could not find action or result", e);
                }
                sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
            } catch (Exception e) {
                sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
            } finally {
                UtilTimerStack.pop(timerKey);
            }
        }

        
    DefaultActionProxyFactory建创ActionProxy,在com.opensymphony.xwork2.DefaultActionProxyFactory:

        

         public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {
            
            ActionInvocation inv = new DefaultActionInvocation(extraContext, true);

            container.inject(inv);
            return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
        }

          

        接下来看看 

        org.apache.struts2.impl.StrutsActionProxy的execute()方法,如下:

        

         public String execute() throws Exception {
            ActionContext previous = ActionContext.getContext();
            ActionContext.setContext(invocation.getInvocationContext());
            try {        
    //这里就是调用拦截器的入口了
                return invocation.invoke();
            } finally {
                if (cleanupContext)
                    ActionContext.setContext(previous);
            }
        }

       
    最关键的,com.opensymphony.xwork2.DefaultActionInvocation.invoke()方法,这个DefaultActionInvocation是ActionInvocation的一个实现类,如下:

        

      //保存了执行当前action方法时需要调用的拦截器栈,按照struts.xml中配制的拦截器顺序,从前到后,依次加入到了这个Iterator里面
      protected Iterator<InterceptorMapping> interceptors; 

      public String invoke() throws Exception {
            String profileKey = "invoke: ";
            try {
                UtilTimerStack.push(profileKey);

                if (executed) {
                    throw new IllegalStateException("Action has already executed");
                }
               
     //如果当前还有下一个,则续继执行拦截器
                if (interceptors.hasNext()) {
                    final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
                    String interceptorMsg = "interceptor: " + interceptor.getName();
                    UtilTimerStack.push(interceptorMsg);
                    try {
         //执行拦截器的intercept()方法,并将当前ActionInvocation对象传递给这个方法
         //这样,当一个拦截器执行完自己的处置后,需要让框架续继执行下一个拦截器的时候,直接应用actionInvocation.invoke()方法,当前这个方法又会被调一次,这其实就是一个递归了,递归方法是ActionInvocation.invoke(),结束条件是interceptors.hasNext()
                                    resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
                                }
                    finally {
                        UtilTimerStack.pop(interceptorMsg);
                    }
                } else { //拦截器全部都执行了,那么最后来执行action,跳出递归了
                    resultCode = invokeActionOnly();
                }

                // this is needed because the result will be executed, then control will return to the Interceptor, which will
                // return above and flow through again
                if (!executed) {
                    if (preResultListeners != null) {
                        for (Object preResultListener : preResultListeners) {
                            PreResultListener listener = (PreResultListener) preResultListener;

                            String _profileKey = "preResultListener: ";
                            try {
                                UtilTimerStack.push(_profileKey);
                                listener.beforeResult(this, resultCode);
                            }
                            finally {
                                UtilTimerStack.pop(_profileKey);
                            }
                        }
                    }

                    // now execute the result, if we're supposed to
                    if (proxy.getExecuteResult()) {
                        executeResult();
                    }

                    executed = true;
                }

                return resultCode;
            }
            finally {
                UtilTimerStack.pop(profileKey);
            }
        }

       
    基本理原到此为止,下面弄个小例子再说明一下:

        

    //拦截器,相当于struts2的拦截器
     public interface 
    Interceptor {
        String intercept(InvocationContext context);
    }

    //很多拦截器的实现
    public class ExceptionInterceptor implements Interceptor {

        public String intercept(InvocationContext context) {
            // 对异常的处置
            System.out.println("\t\t\tExceptionInterceptor 处置异常");
            return context.invoke();
        }
    }
    public class FileUploadInterceptor implements Interceptor {

        public String intercept(InvocationContext context) {
            // 处置文件上传相关
            System.out.println("\t\t\tFileUploadInterceptor 处置文件上传");
            return context.invoke();
        }
    }
    public class ParameterInterceptor implements Interceptor {

        public String intercept(InvocationContext context) {
            // 处置求请的参数
            System.out.println("\t\t\tParameterInterceptor 处置求请参数");
            return context.invoke();
        }
    }

    //执行拦截器的invocation上下文,相当于struts2的ActionInvocation
    public class InvocationContext {

        
    // 这里存放当前执行当前action所需要执行的拦截器栈
        private Iterator<Interceptor> interceptorIterator = null;
        private String prefix = "";

        public InvocationContext() {
            
    // 模拟从配制文件中相应的规则取拦截器栈
            ArrayList<Interceptor> list = new ArrayList<Interceptor>();
            list.add(new ExceptionInterceptor());
            list.add(new FileUploadInterceptor());
            list.add(new ParameterInterceptor());
            interceptorIterator = list.iterator();
        }

        public String 
    invoke() {
            
    // 是否还有拦截器需要执行
            if (interceptorIterator.hasNext()) {
                
    // 获取下一个需要执行的拦截器
                Interceptor interceptor = interceptorIterator.next();
                String name = interceptor.getClass().getName();
                name = prefix + name;
                System.out.println(name + " intercept start...");
                prefix += "\t";
               
     // Interceptor的所有intercept方法实现里面,最后都调用了InvocationContext.invoke()方法
                // 其实就是一个递归,只不过invoke()的下一个递归是在Interceptor.intercept()里面调用的
                // 所以说为什么Interceptor.intercept()方法要加个InvocationContext的参数呢,作用就在于此

                String result = interceptor.intercept(this);
                System.out.println(name + " intercept end...");
                return result;
            } else {
    // 所有的拦截器都执行完了,那就来执行action对应的方法
                return executeAction();
            }
        }

        private String executeAction() {
            System.out.println(prefix + "executeAction success.");
            return "success";
        }
    }

    //模拟求请进行测试
    public class Test {

        public static void main(String[] args) {
            InvocationContext context = new InvocationContext();
            System.out.println("求请开始了...");
            context.invoke();
            System.out.println("求请处置完了...");
        }
    }
    输出结果:
    Struts2拦截器理原【原创】 - sun - 学无止境

    文章结束给大家分享下程序员的一些笑话语录: 手机终究会变成PC,所以ip会比wm更加畅销,但是有一天手机强大到一定程度了就会发现只有wm的支持才能完美享受。就好比树和草,草长得再高也是草,时间到了条件成熟了树就会窜天高了。www.ishuo.cn

  • 相关阅读:
    算分-DESIGN THECHNIQUES
    高级数据结构笔记摘要
    红黑树和AVL树笔记摘要
    索引笔记摘要
    检索笔记摘要
    外排序笔记摘要
    内排序笔记摘要
    线性表总结
    数论的一点前置知识
    线段交模板
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3026120.html
Copyright © 2011-2022 走看看