:20px; font-family:微软雅黑,黑体,Arial,Helvetica,sans-serif"Struts2拦截器原理 span class


发一下牢骚和主题无关:


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("求请处置完了...");

    }

}

输出结果: