1.启动web应用程序

  在web.xml中配置了StrutsPrepareAndExecuteFilter拦截器,因此web应用程序启动时,服务器将创建StrutsPrepareAndExecuteFilter实例对象,并调用对象的init()完成初始化,init()方法在Filter的整个生命周期之后只会被执行一次

  在init()方法中,根据配置信息初始化PrepareOperations 和ExecuteOperations对象

2.客户端发送请求

  在web.xml中配置的StrutsPrepareAndExecuteFilter拦截所有请求,因此,请求被拦截,调用doFilter方法

  在doFilter方法中,创建ActionContext对象,并将ActionContext对象放入ThreadLocal中

          判断当前请求是否为不需要拦截的请求,是,直接调用chain.doFilter方法放行

          否则,获取ActionMapping对象,判断该对象是否为nul

            如果是null,放行

            否则,调用ExecuteOperations的executeAction方法,在该方法中调用Dispatcher的serviceAction方法

      ActionMapping对象,就是struts.xml中的配置

3.Dispatcher的serviceAction方法    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);  //创建ActionContext对象
            prepare.assignDispatcherToThread();
            if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { // 判断当前请求是否是action请求
                chain.doFilter(request, response);
            } else {
                request = prepare.wrapRequest(request);
                ActionMapping mapping = prepare.findActionMapping(request, response, true);
  
//创建ActionMapping对象,创建ActionMapping的过程要借助ActionMapper,如果ActionMapping对象不是null,将该对象放入request域中
//mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
if (mapping == null) { boolean handled = execute.executeStaticResourceRequest(request, response); if (!handled) { chain.doFilter(request, response); } } else { execute.executeAction(request, response, mapping); } } } finally { prepare.cleanupRequest(request); } }

4.Dispatcher的serviceAction方法

  在该方法中,获取Configuration对象,根据Configuration创建ActionProxy,将控制权交给ActionProxy,调用ActionProxy的execute()

    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
                              ActionMapping mapping) throws ServletException {

        Map<String, Object> extraContext = createContextMap(request, response, mapping, context); // 创建ValueStack的Map属性

        // 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();
            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 {
                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 {
                    if (LOG.isWarnEnabled()) {
                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);
        }
    }

5.ActionProxy的execute方法

  调用ActionInvocation的invoke方法

    public String execute() throws Exception {
        ActionContext nestedContext = ActionContext.getContext();
        ActionContext.setContext(invocation.getInvocationContext());

        String retCode = null;

        String profileKey = "execute: ";
        try {
            UtilTimerStack.push(profileKey);

            retCode = invocation.invoke();
        } finally {
            if (cleanupContext) {
                ActionContext.setContext(nestedContext);
            }
            UtilTimerStack.pop(profileKey);
        }

        return retCode;
    }

 

6.ActionInvocation的invoke方法

  依次调用配置的所有拦截器的intercept方法

  调用到最后一个拦截器时,调用invokeActionOnly方法

    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 {
                                resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
                            }
                finally {
                    UtilTimerStack.pop(interceptorMsg);
                }
            } else {
                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);
        }
    }

7.拦截器的Intercept方法

  实现拦截器的功能

8.ActionInvocation的invokeActionOnly方法

  根据配置的顺序,依次调用拦截器的intercept方法,当拦截器调用完之后,调用invokeActionOnly方法,再该方法中调用invokeAction方法,调用目标action方法

9.ActionInvocation的invokeAction方法

    protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
        String methodName = proxy.getMethod();

        if (LOG.isDebugEnabled()) {
            LOG.debug("Executing action method = " + actionConfig.getMethodName());
        }

        String timerKey = "invokeAction: " + proxy.getActionName();
        try {
            UtilTimerStack.push(timerKey);

            boolean methodCalled = false;
            Object methodResult = null;
            Method method = null;
            try {
                method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);
            } catch (NoSuchMethodException e) {
                // hmm -- OK, try doXxx instead
                try {
                    String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
                    method = getAction().getClass().getMethod(altMethodName, EMPTY_CLASS_ARRAY);
                } catch (NoSuchMethodException e1) {
                    // well, give the unknown handler a shot
                    if (unknownHandlerManager.hasUnknownHandlers()) {
                        try {
                            methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
                            methodCalled = true;
                        } catch (NoSuchMethodException e2) {
                            // throw the original one
                            throw e;
                        }
                    } else {
                        throw e;
                    }
                }
            }

            if (!methodCalled) {
                methodResult = method.invoke(action, EMPTY_OBJECT_ARRAY);
            }

            return saveResult(actionConfig, methodResult);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
        } catch (InvocationTargetException e) {
            // We try to return the source exception.
            Throwable t = e.getTargetException();

            if (actionEventListener != null) {
                String result = actionEventListener.handleException(t, getStack());
                if (result != null) {
                    return result;
                }
            }
            if (t instanceof Exception) {
                throw (Exception) t;
            } else {
                throw e;
            }
        } finally {
            UtilTimerStack.pop(timerKey);
        }
    }

 

10.ActionInvocation的executeResult方法

  invokeAction方法返回之后(表示目标action方法执行完成),调用executeResult方法,在该方法中调用Result的execute方法渲染结果

    private void executeResult() throws Exception {
        result = createResult();

        String timerKey = "executeResult: " + getResultCode();
        try {
            UtilTimerStack.push(timerKey);
            if (result != null) {
                result.execute(this);
            } else if (resultCode != null && !Action.NONE.equals(resultCode)) {
                throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()
                        + " and result " + getResultCode(), proxy.getConfig());
            } else {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("No result returned for action " + getAction().getClass().getName() + " at " + proxy.getConfig().getLocation());
                }
            }
        } finally {
            UtilTimerStack.pop(timerKey);
        }
    }

11.结果返回之后的后续代码,将结果返回给客户端

 

涉及的对象

StrutsPrepareAndExecuteFilter

  doFilter方法,(1)创建ActionContext对象(2)如果该请求是需要拦截的请求,根据请求创建ActionMapping对象(3)如果ActionMapping对象不是null,则最终调用Dispatcher的serviceAction方法

ActionMapping

  该对象中存放的就是struts.xml中配置的一个一个的<action>

  获取ActionMapping对象要借助ActionMapper对象

ActionMapper

Dispatcher

ActionProxy

  通过ActionProxyFactory获取

Configuration

ConfigurationManager

ActionInvocation

Action

Result

 

总结:

请求首先经过StrutsPrepareAndExecuteFilter

调用dofilter方法

  如果该请求是需要拦截的请求,通过ActionMapper获取ActionMapping,如果ActionMapping不是null,表明再struts.xml中配置了该请求对应的<action>

调用execute.executeAction(request, response, mapping);

调用dispatcher.serviceAction(request, response, servletContext, mapping);

  先对值栈进行处理

    通过ActionMapping、ConfigurationManager、Configuration、ActionFactory获取ActionProxy

    将值栈放入request请求域中

    调用proxy.execute();

    调用retCode = invocation.invoke();

如果当期拦截器不是最后一个,调用拦截器的intercept方法     resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); 在每个拦截器的intercept方法最后return invocation.invoke(); 以这样的方式依次调用拦截器,直到最后一个拦截器

如果当前拦截器是最后一个,调用ActionInvocation的invokeActionOnly方法  resultCode = invokeActionOnly();在拦截器调用完成之后,调用invokeAction方法执行目标Action方法  return invokeAction(getAction(), proxy.getConfig());

    调用invokeAction(getAction(), proxy.getConfig());

      String methodName = proxy.getMethod();

      method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);

      methodResult = method.invoke(action, EMPTY_OBJECT_ARRAY);

调用executeResult()方法渲染结果

  result = createResult();

  result.execute(this);

依次执行每个拦截器的后续代码finally

执行后续代码,将结果返回