• Struts2源码浅析-请求处理(转)


    本文转自http://blog.csdn.net/java2000_wl/article/details/7576035

    StrutsPrepareAndExecuteFilter doFilter方法为请求的入口

    doFilter方法主要做以下几件事

    一: 根据配置的常量  设置当前request 字符编码  response国际化Locale信息

    二: 创建ActionContext数据对象

    三: 请求处理 

            1.分析url   根据url 创建ActionMapping对象   这些操作主要由ActionMapper接口实现类完成

            2. 执行请求逻辑动作

                   ①.根据请求映射对象ActionMapping  创建ActionProxy 主要由ActionProxyFactory接口完成

                   ②.执行Action 逻辑(拦截器,Action对应方法)

    四:清理ActionContext数据对象

    流程分析

    1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {  
    2.        HttpServletRequest request = (HttpServletRequest) req;  
    3.        HttpServletResponse response = (HttpServletResponse) res;  
    4.        try {  
    5.         //根据struts2常量配置   设置当前request Encoding 即request.setCharacterEncoding(encoding)  
    6.         //当前 response 设置国际化Locale信息 即response.setLocale(locale);  
    7.            prepare.setEncodingAndLocale(request, response);  
    8.            //创建ActionContext数据对象  包括ValueStack  
    9.            prepare.createActionContext(request, response);  
    10.            //将当前的Dispatcher保存到ThreadLocal中   
    11.            prepare.assignDispatcherToThread();  
    12.         if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {  
    13.             chain.doFilter(request, response);  
    14.         } else {  
    15.             //包装request 返回StrutsRequestWrapper  
    16.             //StrutsRequestWrapper继承了HttpServletRequestWrapper  
    17.             request = prepare.wrapRequest(request);  
    18.             //创建ActionMapping  
    19.             ActionMapping mapping = prepare.findActionMapping(request, response, true);  
    20.             if (mapping == null) {  
    21.                 boolean handled = execute.executeStaticResourceRequest(request, response);  
    22.                 if (!handled) {  
    23.                     chain.doFilter(request, response);  
    24.                 }  
    25.             } else {  
    26.                 //真正执行Action的地方  
    27.                 execute.executeAction(request, response, mapping);  
    28.             }  
    29.         }  
    30.        } finally {  
    31.         //清除ActionContext   
    32.            prepare.cleanupRequest(request);  
    33.        }  
    34.    }  

    一:设置编码字符  国际化Locale信息,PrepareOperations#setEncodingAndLocale方法 调用了 Dispatcher#prepare方法  真正做事的还是Dispatcher

    PrepareOperations类的setEncodingAndLocale 方法

    1. public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) {  
    2.     dispatcher.prepare(request, response);  
    3. }  

    Dispatcher的prepare方法

    1. public void prepare(HttpServletRequest request, HttpServletResponse response) {  
    2.     //struts.i18n.encoding 常量  
    3.     String encoding = null;  
    4.     if (defaultEncoding != null) {  
    5.         encoding = defaultEncoding;  
    6.     }  
    7.     Locale locale = null;  
    8.     if (defaultLocale != null) {  
    9.         locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());  
    10.     }  
    11.     if (encoding != null) {  
    12.         try {  
    13.             request.setCharacterEncoding(encoding);  
    14.         } catch (Exception e) {  
    15.             LOG.error("Error setting character encoding to '" + encoding + "' - ignoring.", e);  
    16.         }  
    17.     }  
    18.     if (locale != null) {  
    19.         response.setLocale(locale);  
    20.     }  
    21. }  

    二:创建ActionContext数据对象 由PrepareOperations#createActionContext方法完成

    1. public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {  
    2.     ActionContext ctx;  
    3.     Integer counter = 1;  
    4.     Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);  
    5.     if (oldCounter != null) {  
    6.         counter = oldCounter + 1;  
    7.     }  
    8.     //ActionContext与 ValueStack 是主从关系  
    9.     //ActionContext中持有ValueStack  
    10.     ActionContext oldContext = ActionContext.getContext();  
    11.     if (oldContext != null) {  
    12.         ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));  
    13.     } else {  
    14.         //ValueStack是由ValueStackFactory 创建  
    15.         ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();  
    16.         stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));  
    17.         //将创建的ValueStack 保存到ActionContext  
    18.         ctx = new ActionContext(stack.getContext());  
    19.     }  
    20.     request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);  
    21.     //将当前ActionContext 保存到ThreadLocal中  
    22.     ActionContext.setContext(ctx);  
    23.     return ctx;  
    24. }  

    三: 请求处理

    1.创建ActionMapping对象  一个ActionMapping对象  对应一次请求,

    ActionMapping定义

    1. public class ActionMapping {  
    2.   
    3.     private String name;  
    4.     private String namespace;  
    5.     private String method;  
    6.     private String extension;  
    7.     private Map<String, Object> params;  
    8.     private Result result;  
    9. }  

    PrepareOperations findActionMapping方法  创建ActionMaping对象的动作转交给ActionMapper接口的getMapping方法完成

    1. public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {  
    2.     ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);  
    3.     if (mapping == null || forceLookup) {  
    4.         try {  
    5.             //ActionMapper 默认实现DefaultActionMapper  
    6.             //getMapping方法主要负责 查找对应的ActionConfig对象  根据ActionConfig对象创建 ActionMapping  
    7.             mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());  
    8.             if (mapping != null) {  
    9.                 request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);  
    10.             }  
    11.         } catch (Exception ex) {  
    12.             //错误信息  
    13.             dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);  
    14.         }  
    15.     }  
    16.     return mapping;  
    17. }  

    ActionMapper 的实现类 DefaultActionMapper , getMapping方法  分析url ,根据url查找到的ActionConfig对象 创建ActionMapping

    1. public ActionMapping getMapping(HttpServletRequest request,  
    2.                                 ConfigurationManager configManager) {  
    3.     //实例化ActionMapping  
    4.     ActionMapping mapping = new ActionMapping();  
    5.     String uri = getUri(request);  
    6.     int indexOfSemicolon = uri.indexOf(";");  
    7.     uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;  
    8.     uri = dropExtension(uri, mapping);  
    9.     if (uri == null) {  
    10.         return null;  
    11.     }  
    12.     // name, namspace处理  
    13.     parseNameAndNamespace(uri, mapping, configManager);  
    14.     handleSpecialParameters(request, mapping);  
    15.     if (mapping.getName() == null) {  
    16.         return null;  
    17.     }  
    18.     //处理action!menthod 形式  
    19.     parseActionName(mapping);  
    20.     return mapping;  
    21. }  
    1. protected void parseNameAndNamespace(String uri, ActionMapping mapping,  
    2.                                       ConfigurationManager configManager) {  
    3.      String namespace, name;  
    4.      int lastSlash = uri.lastIndexOf("/");  
    5.      if (lastSlash == -1) {  
    6.          namespace = "";  
    7.          name = uri;  
    8.      } else if (lastSlash == 0) {  
    9.          namespace = "/";  
    10.          name = uri.substring(lastSlash + 1);  
    11.      } else if (alwaysSelectFullNamespace) {  
    12.          // Simply select the namespace as everything before the last slash  
    13.          namespace = uri.substring(0, lastSlash);  
    14.          name = uri.substring(lastSlash + 1);  
    15.      } else {  
    16.         //配置元素管理器  
    17.          Configuration config = configManager.getConfiguration();  
    18.          String prefix = uri.substring(0, lastSlash);  
    19.          namespace = "";  
    20.          boolean rootAvailable = false;  
    21.          //config.getPackageConfigs()   
    22.          for (Object cfg : config.getPackageConfigs().values()) {  
    23.              String ns = ((PackageConfig) cfg).getNamespace();  
    24.              //匹配nameSpace   
    25.              if (ns != null && prefix.startsWith(ns) && (prefix.length() == ns.length() || prefix.charAt(ns.length()) == '/')) {  
    26.                  if (ns.length() > namespace.length()) {  
    27.                      namespace = ns;  
    28.                  }  
    29.              }  
    30.              if ("/".equals(ns)) {  
    31.                  rootAvailable = true;  
    32.              }  
    33.          }  
    34.          name = uri.substring(namespace.length() + 1);  
    35.          if (rootAvailable && "".equals(namespace)) {  
    36.              namespace = "/";  
    37.          }  
    38.      }  
    39.      if (!allowSlashesInActionNames && name != null) {  
    40.          int pos = name.lastIndexOf('/');  
    41.          if (pos > -1 && pos < name.length() - 1) {  
    42.              name = name.substring(pos + 1);  
    43.          }  
    44.      }  
    45.      //TODO  
    46.      mapping.setNamespace(namespace);  
    47.      mapping.setName(name);  
    48.  }  

    处理action!menthod 形式

    1. protected ActionMapping parseActionName(ActionMapping mapping) {  
    2.        if (mapping.getName() == null) {  
    3.            return mapping;  
    4.        }  
    5.        if (allowDynamicMethodCalls) {  
    6.            String name = mapping.getName();  
    7.            int exclamation = name.lastIndexOf("!");  
    8.            if (exclamation != -1) {  
    9.                mapping.setName(name.substring(0, exclamation));  
    10.                mapping.setMethod(name.substring(exclamation + 1));  
    11.            }  
    12.        }  
    13.        return mapping;  
    14.    }  

    ActionMapping创建 时序图

     

     2.1根据请求映射对象ActionMapping 创建ActionProxy

    回到StrutsPrepareAndExecuteFilter doFilter方法

    1. //执行Action 方法入口  
    2. execute.executeAction(request, response, mapping);  

    ExecuteOperations 的 executeAction方法 把处理逻辑转交给了 Dispatcher的serviceAction方法

    1. public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {  
    2.     dispatcher.serviceAction(request, response, servletContext, mapping);  
    3. }  

    Dispatcher的serviceAction方法主要完成 1.创建ActionProxy,ActionInvocation,Action对象 2.执行对应的方法

    ActionProxy是个接口  接口定义

    1. public interface ActionProxy {  
    2.   
    3.     /** 
    4.      * 我们定义的Action 
    5.      * ActionInvocation 中也持有Action引用 
    6.      * <br>------------------------------<br> 
    7.      * @return 
    8.      */  
    9.     Object getAction();  
    10.   
    11.     String getActionName();  
    12.   
    13.     /** 
    14.      * 初始化阶段 包装的对象 
    15.      * <br>------------------------------<br> 
    16.      * @return 
    17.      */  
    18.     ActionConfig getConfig();  
    19.   
    20.     /** 
    21.      * 该值决定 执行完Action对应的方法后 是否执行Result相关操作 
    22.      */  
    23.     void setExecuteResult(boolean executeResult);  
    24.   
    25.     /** 
    26.      * 
    27.      * @return the status 
    28.      */  
    29.     boolean getExecuteResult();  
    30.   
    31.     /** 
    32.      * 执行器 
    33.      */  
    34.     ActionInvocation getInvocation();  
    35.   
    36.     String getNamespace();  
    37.   
    38.     String execute() throws Exception;  
    39.       
    40.     /** 
    41.      * Action 对应的方法 
    42.      */  
    43.     String getMethod();  
    44. }  

    ActionInvocation 负责执行所有的拦截器Interceptor和我们定义的Action方法

    ActionInvocation接口定义 

    1. /** 
    2.  * Action 执行器 
    3.  */  
    4. public interface ActionInvocation extends Serializable {  
    5.   
    6.     /** 
    7.      * 我们定义的Action 
    8.      * <br>------------------------------<br> 
    9.      * @return 
    10.      */  
    11.     Object getAction();  
    12.   
    13.     boolean isExecuted();  
    14.   
    15.     ActionContext getInvocationContext();  
    16.   
    17.     ActionProxy getProxy();  
    18.   
    19.     Result getResult() throws Exception;  
    20.   
    21.     /** 
    22.      * Action 对应方法执行完成后  返回的Result   
    23.      * <br>------------------------------<br> 
    24.      * @return 
    25.      */  
    26.     String getResultCode();  
    27.   
    28.     void setResultCode(String resultCode);  
    29.   
    30.     ValueStack getStack();  
    31.   
    32.     void addPreResultListener(PreResultListener listener);  
    33.       
    34.     /** 
    35.      * 执行所有操作 
    36.      * 1.执行拦截器 
    37.      * 2.调用invokeActionOnly方法执行 action对应方法 
    38.      * <br>------------------------------<br> 
    39.      * @return 
    40.      * @throws Exception 
    41.      */  
    42.     String invoke() throws Exception;  
    43.   
    44.     /** 
    45.      * 执行我们定义的Action 方法  在invoke方法中调用 
    46.      * <br>------------------------------<br> 
    47.      * @return 
    48.      * @throws Exception 
    49.      */  
    50.     String invokeActionOnly() throws Exception;  
    51.   
    52.     void setActionEventListener(ActionEventListener listener);  
    53.   
    54.     /** 
    55.      *  这个方法会创建我们定义的Action 
    56.      * <br>------------------------------<br> 
    57.      * @param proxy 
    58.      */  
    59.     void init(ActionProxy proxy) ;  
    60. }  

    Dispatcher的serviceAction方法

    1. public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException {  
    2.     //1.保存request, session, 等web元素到map中  
    3.     Map<String, Object> extraContext = createContextMap(request, response, mapping, context);  
    4.     // 省略  
    5.     //TODO  
    6.     String namespace = mapping.getNamespace();  
    7.     String name = mapping.getName();  
    8.     String method = mapping.getMethod();  
    9.     //2.初始化完成之后  解析的xml数据对象 会保存到 Configuration#runtimeConfiguration变量中  
    10.     Configuration config = configurationManager.getConfiguration();  
    11.     //3.创建ActionProxy,ActionInvocation 返回StrutsActionProxy  
    12.     ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, truefalse);  
    13.     request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());  
    14.     //执行Action 对应方法入口  
    15.     if (mapping.getResult() != null) {  
    16.         Result result = mapping.getResult();  
    17.         result.execute(proxy.getInvocation());  
    18.     } else {  
    19.     //StrutsActionProxy#execute()方法  
    20.     //4.实际是转到了ActionInvocation#invoke方法  
    21.         proxy.execute();  
    22.     }  
    23. }  

    createContextMap 方法 保存web元素 最终已map的形式返回

    1. public Map<String, Object> createContextMap(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping, ServletContext context) {  
    2.     Map requestMap = new RequestMap(request);  
    3.     Map params = new HashMap(request.getParameterMap());  
    4.     Map session = new SessionMap(request);  
    5.     Map application = new ApplicationMap(context);  
    6.     //包装成map集合  
    7.     Map<String, Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);  
    8.     if (mapping != null) {  
    9.         extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);  
    10.     }  
    11.     return extraContext;  
    12. }  
    1. public HashMap<String, Object> createContextMap(Map requestMap, Map parameterMap, Map sessionMap, Map applicationMap, HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) {  
    2.         HashMap<String, Object> extraContext = new HashMap<String, Object>();  
    3.         extraContext.put(ActionContext.PARAMETERS, new HashMap(parameterMap));  
    4.         extraContext.put(ActionContext.SESSION, sessionMap);  
    5.         extraContext.put(ActionContext.APPLICATION, applicationMap);  
    6.         Locale locale;  
    7.         if (defaultLocale != null) {  
    8.             locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());  
    9.         } else {  
    10.             locale = request.getLocale();  
    11.         }  
    12.   
    13.         extraContext.put(ActionContext.LOCALE, locale);  
    14.         //extraContext.put(ActionContext.DEV_MODE, Boolean.valueOf(devMode));  
    15.   
    16.         // value为HttpServletRequest  
    17.         //HttpServletRequest = extraContext.get('StrutsStatics.HTTP_REQUEST')  
    18.         extraContext.put(StrutsStatics.HTTP_REQUEST, request);  
    19.         extraContext.put(StrutsStatics.HTTP_RESPONSE, response);  
    20.         extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext);  
    21.         //value为包装过的 元素map   
    22.         //获取方式extraContext.get('request').requestMap.get('key')  
    23.         extraContext.put("request", requestMap);  
    24.         extraContext.put("session", sessionMap);  
    25.         extraContext.put("application", applicationMap);  
    26.         extraContext.put("parameters", parameterMap);  
    27.   
    28.         AttributeMap attrMap = new AttributeMap(extraContext);  
    29.         extraContext.put("attr", attrMap);  
    30.   
    31.         return extraContext;  
    32.     }  

    创建ActionProxy,ActionInvocation 是由ActionProxyFactory实现类完成

    DefaultActionProxyFactory createActionProxy方法

    1. public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {  
    2.     //创建ActionInvocation  
    3.      ActionInvocation inv = new DefaultActionInvocation(extraContext, true);  
    4.      //对该ActionInvocation 实例 进行依赖注入 即引用了@Inject注解的 方法 变量..  
    5.      container.inject(inv);  
    6.      //创建ActionProxy    
    7.      //ActionProxy,ActionInvocation都互相持有对方的引用  
    8.      //子类StrutsActionProxyFactory 重写了该方法  
    9.      return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);  
    10.  }  

    StrutsActionProxyFactory 的createActionProxy方法,   StrutsActionProxyFactory是DefaultActionProxyFactory的子类

    1. public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {  
    2.     StrutsActionProxy proxy = new StrutsActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);  
    3.     container.inject(proxy);  
    4.     //这里会创建action  
    5.     proxy.prepare();  
    6.     return proxy;  
    7. }  

    StrutsActionProxyFactory 的prepare方法  主要完成调用ActionInvocation  init方法 创建我们定义的Action  也可能是从Spring容器获得

    1. protected void prepare()  {  
    2.  //从configuration runtimeConfiguration对象中获取ActionConfig  
    3.  config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);  
    4.  //...略  
    5.  //ActionConfig method为空 默认设置为"execute"    
    6.  resolveMethod();  
    7.  //检查method 在Action是否存在  
    8.  if (!config.isAllowedMethod(method)) {  
    9.      throw new ConfigurationException("Invalid method: "+method+" for action "+actionName);  
    10.  }  
    11.  //创建Action 可能是从spring容器中获得  
    12.  invocation.init(this);  
    13. }  

    DefaultActionInvocation  init方法  

    1. public void init(ActionProxy proxy) {  
    2.     this.proxy = proxy;  
    3.     Map<String, Object> contextMap = createContextMap();  
    4.     ActionContext actionContext = ActionContext.getContext();  
    5.     if (actionContext != null) {  
    6.         actionContext.setActionInvocation(this);  
    7.     }  
    8.     // 创建Action  
    9.     createAction(contextMap);  
    10.     if (pushAction) {  
    11.         stack.push(action);  
    12.         contextMap.put("action", action);  
    13.     }  
    14.     invocationContext = new ActionContext(contextMap);  
    15.     invocationContext.setName(proxy.getActionName());  
    16.     //从当前mapping对应的ActionConfig获得拦截器  
    17.     List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());  
    18.     interceptors = interceptorList.iterator();  
    19. }  
    1. protected void createAction(Map<String, Object> contextMap) {  
    2.     //省略...  
    3.     // load action  
    4.      //Action的创建在此发生  
    5.     action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);  
    6.     //省略...  
    7.     if (actionEventListener != null) {  
    8.         action = actionEventListener.prepare(action, stack);  
    9.     }  
    10. }  

    ObjectFactory 主要负责bean 的创建  继承关系:

    以spring为例 StrutsSpringObjectFactory

    ObjectFactory bulidAction方法 转到bulidBean方法

    1. public Object buildAction(String actionName, String namespace, ActionConfig config, Map<String, Object> extraContext) throws Exception {  
    2.     return buildBean(config.getClassName(), extraContext);  
    3. }  

    SpringObjectFactory 重写了ObjectFactory 的buildBean方法 

    主要从spring容器中获得bean 这里使用的是spring 的AutowireCapableBeanFactory容器 具有自动装配特性

    1. public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception {  
    2.     Object bean;  
    3.   
    4.     try {  
    5.         // Decide to follow autowire strategy or use the legacy approach which mixes injection strategies  
    6.         if (alwaysRespectAutowireStrategy) {  
    7.             // Leave the creation up to Spring  
    8.             bean = autoWiringFactory.createBean(clazz, autowireStrategy, false);  
    9.             injectApplicationContext(bean);  
    10.             return injectInternalBeans(bean);  
    11.         } else {  
    12.             bean = autoWiringFactory.autowire(clazz, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);  
    13.             bean = autoWiringFactory.applyBeanPostProcessorsBeforeInitialization(bean, bean.getClass().getName());  
    14.             // We don't need to call the init-method since one won't be registered.  
    15.             bean = autoWiringFactory.applyBeanPostProcessorsAfterInitialization(bean, bean.getClass().getName());  
    16.             return autoWireBean(bean, autoWiringFactory);  
    17.         }  
    18.     } catch (UnsatisfiedDependencyException e) {  
    19.         if (LOG.isErrorEnabled())  
    20.             LOG.error("Error building bean", e);  
    21.         // Fall back  
    22.         return autoWireBean(super.buildBean(clazz, extraContext), autoWiringFactory);  
    23.     }  
    24. }  

    ActionProxy, ActionInvocation创建过程结束了 

    当前的ActionInvocation对象中 持有当前映射匹配的拦截器集合interceptors,ActionProxy和我们定义的action对象

    目前还没有执行Action对应方法  万事俱备只欠东风了。

    ActionProxy创建时序图

    2.2 执行Action逻辑   真正执行逻辑是在ActionInvocation的invoke方法中发生

    回到Dispatcher 的serviceAction方法

    1. //StrutsActionProxy#execute()方法  
    2. //实际是转到了ActionInvocation#invoke方法  
    3. proxy.execute();  

    StrutsActionProxy的 execute方法调用ActionInvocation invoke方法

    1. public String execute() throws Exception {  
    2.     ActionContext previous = ActionContext.getContext();  
    3.     ActionContext.setContext(invocation.getInvocationContext());  
    4.     try {  
    5.         return invocation.invoke();  
    6.     } finally {  
    7.         if (cleanupContext)  
    8.             ActionContext.setContext(previous);  
    9.     }  
    10. }  

    ActionInvocation invoke方法

    大致执行步骤1.执行所有的拦截器 2.执行Action对应的Method 3.根据Action执行结果 再执行Result

    1. public String invoke() throws Exception {  
    2.     //...省略  
    3.     //1.先执行所有拦截器  
    4.     if (interceptors.hasNext()) {  
    5.         //这里类似一个递归调用  
    6.         //intercept方法中 invocation.invoke();  
    7.        final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();  
    8.        resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);  
    9.     } else {  
    10.         //2.最后 执行Action对应方法  返回result字符串  
    11.         resultCode = invokeActionOnly();  
    12.     }  
    13.     //...省略  
    14.     if (proxy.getExecuteResult()) {  
    15.         //3.实例化Result对象  
    16.         executeResult();  
    17.     }  
    18.     return resultCode;  
    19. }  

    拦截器的执行  interceptors为一个迭代器Iterator  当执行完一个拦截器   拦截器中又会回调 invoke方法执行下一个  例如:

    1. public String intercept(ActionInvocation invocation) throws Exception {  
    2.     String result = null;  
    3.     //回调到DefaultActionInvocation#invoke方法  
    4.     result = invocation.invoke();  
    5.     return result;  
    6. }  

    拦截器执行完成 接着执行Action 对应方法  在invokeActionOnly发生

    1. public String invokeActionOnly() throws Exception {  
    2.     return invokeAction(getAction(), proxy.getConfig());  
    3. }  
    1. protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {  
    2.     //要执行的方法  
    3.     String methodName = proxy.getMethod();  
    4.   
    5.     try {  
    6.         Object methodResult = null;  
    7.         Method method = null;  
    8.         //获得Method对象  
    9.         method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);  
    10.         //执行Action 方法  
    11.         methodResult = method.invoke(action, new Object[0]);  
    12.         if (methodResult instanceof Result) {  
    13.             this.explicitResult = (Result) methodResult;  
    14.             container.inject(explicitResult);  
    15.             return null;  
    16.         } else {  
    17.             //返回 result Str  
    18.             return (String) methodResult;  
    19.         }  
    20.     } catch (NoSuchMethodException e) {  
    21.         throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");  
    22.     } catch (InvocationTargetException e) {  
    23.         Throwable t = e.getTargetException();  
    24.         if (actionEventListener != null) {  
    25.             //异常处理  
    26.             String result = actionEventListener.handleException(t, getStack());  
    27.             if (result != null) {  
    28.                 return result;  
    29.             }  
    30.         }  
    31.     }   
    32. }  

    执行Result 根据ResultConfig 中的calssName创建result对象  在执行execute

    1. private void executeResult() throws Exception {  
    2.     //根据ResultConfig对象创建Result对象  
    3.     result = createResult();  
    4.     result.execute(this);  
    5. }  
    1. public Result createResult() throws Exception {  
    2.        if (explicitResult != null) {  
    3.            Result ret = explicitResult;  
    4.            explicitResult = null;  
    5.   
    6.            return ret;  
    7.        }  
    8.        ActionConfig config = proxy.getConfig();  
    9.        Map<String, ResultConfig> results = config.getResults();  
    10.        ResultConfig resultConfig = null;  
    11.        try {  
    12.            resultConfig = results.get(resultCode);  
    13.        } catch (NullPointerException e) {  
    14.            // swallow  
    15.        }  
    16.        if (resultConfig == null) {  
    17.            // If no result is found for the given resultCode, try to get a wildcard '*' match.  
    18.            resultConfig = results.get("*");  
    19.        }  
    20.        if (resultConfig != null) {  
    21.            try {  
    22.             //创建Result对象  
    23.             //resultConfig中包含了 className 直  
    24.                return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());  
    25.            } catch (Exception e) {  
    26.                LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);  
    27.                throw new XWorkException(e, resultConfig);  
    28.            }  
    29.        } else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {  
    30.            return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);  
    31.        }  
    32.        return null;  
    33.    }  

    执行Result execute方法  以ServletDispatcherResult为例

    1. public void execute(ActionInvocation invocation) throws Exception {  
    2.     lastFinalLocation = conditionalParse(location, invocation);  
    3.     doExecute(lastFinalLocation, invocation);  
    4. }  
    1. public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {  
    2.     PageContext pageContext = ServletActionContext.getPageContext();  
    3.     if (pageContext != null) {  
    4.         pageContext.include(finalLocation);  
    5.     } else {  
    6.         //...省略  
    7.         if (!insideActionTag && !response.isCommitted() && (request.getAttribute("javax.servlet.include.servlet_path") == null)) {  
    8.             request.setAttribute("struts.view_uri", finalLocation);  
    9.             request.setAttribute("struts.request_uri", request.getRequestURI());  
    10.             //转向  
    11.             dispatcher.forward(request, response);  
    12.         } else {  
    13.             dispatcher.include(request, response);  
    14.         }  
    15.     }  
    16. }  

    请求时序图:

  • 相关阅读:
    Gitkraken使用教程
    request.getHeader中区分大小写参数
    MySql 中查询列表中添加序号
    解决windows 下mysql 表名自动转成小写的问题
    MYSQL服务无法启动,服务没有任何错误;解决方法
    idea中设置一键生成方法注释和类注释
    Windows10下安装MySQL8.0.21-64
    navicat连接mysql出现2059错误的解决方法
    解决tomca在eclipse中正常启动,在bin下启动闪退问题
    QT线程的结束
  • 原文地址:https://www.cnblogs.com/kivi/p/3191765.html
Copyright © 2020-2023  润新知