struts2 处理请求流程分析(结合源码)3

 2.3、dispatcher.serviceAction(request, response, servletContext, mapping);方法分析

Java代码   收藏代码
  1. public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,  
  2.                             ActionMapping mapping) throws ServletException {  
  3.     //包装了Http的四个作用域,extraContext 保存了所有的servlet 容器的作用域和struts2 包装的容器作用域  
  4.       Map<String, Object> extraContext = createContextMap(request, response, mapping, context);  
  5.   
  6.       // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action  
  7.       //如果之前有ValueStack 值栈存在,则用这个,否则创建一个新的,保存在extraContext 中  
  8.       ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);  
  9.       if (stack != null) {  
  10.           extraContext.put(ActionContext.VALUE_STACK, ValueStackFactory.getFactory().createValueStack(stack));  
  11.       }  
  12.       String timerKey = "Handling request from Dispatcher";  
  13.       try {  
  14.           UtilTimerStack.push(timerKey);  
  15.           //获得action 的配置信息  
  16.           String namespace = mapping.getNamespace();  
  17.           String name = mapping.getName();  
  18.           String method = mapping.getMethod();  
  19.   
  20.           Configuration config = configurationManager.getConfiguration();  
  21.           //创建一个ActionProxy  
  22.           ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(  
  23.                   namespace, name, extraContext, truefalse);  
  24.           //如果method 为空,则设为“excue”  
  25.           proxy.setMethod(method);  
  26.           //保存值栈供struts2 使用  
  27.           request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());  
  28.   
  29.           // if the ActionMapping says to go straight to a result, do it!  
  30.           //如果result 不为空的话,进行调转  
  31.           if (mapping.getResult() != null) {  
  32.               Result result = mapping.getResult();  
  33.               //注入的是ActionInvaction  
  34.               result.execute(proxy.getInvocation());  
  35.           } else {  
  36.               proxy.execute();  
  37.           }  
  38.   
  39.           // If there was a previous value stack then set it back onto the request  
  40.           if (stack != null) {  
  41.               request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);  
  42.           }  
  43.       } catch (ConfigurationException e) {  
  44.           LOG.error("Could not find action or result", e);  
  45.           sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);  
  46.       } catch (Exception e) {  
  47.           throw new ServletException(e);  
  48.       } finally {  
  49.           UtilTimerStack.pop(timerKey);  
  50.       }  
  51.   }  

 

    (1)createContextMap(request, response, mapping, context);方法

Java代码   收藏代码
  1. public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,  
  2.             ActionMapping mapping, ServletContext context) {  
  3.   
  4.         // request map wrapping the http request objects  
  5.         Map requestMap = new RequestMap(request);  
  6.   
  7.         // parameters map wrapping the http paraneters.  
  8.         Map params = null;  
  9.         if (mapping != null) {  
  10.             params = mapping.getParams();  
  11.         }  
  12.         Map requestParams = new HashMap(request.getParameterMap());  
  13.         if (params != null) {  
  14.             params.putAll(requestParams);  
  15.         } else {  
  16.             params = requestParams;  
  17.         }  
  18.   
  19.         // session map wrapping the http session  
  20.         Map session = new SessionMap(request);  
  21.   
  22.         // application map wrapping the ServletContext  
  23.         Map application = new ApplicationMap(context);  
  24.         //对上面的http 作用域包装的map 进行封装  
  25.         Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);  
  26.        //把mapping 也放进map 里  
  27.         extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);  
  28.         return extraContext;  
  29.     }  

   由此可以看出struts2 对servlet 容器的作用域都进行包装成相应的Map ,然后放在extraContext  统一进行保存。

来看看extraContext  这个map 里放的是全部servlet 容器作用域还有相应的struts2的包装map,和 locale,从下面的源码中可以看出。

Java代码   收藏代码
  1. public HashMap<String,Object> createContextMap(Map requestMap,  
  2.                                    Map parameterMap,  
  3.                                    Map sessionMap,  
  4.                                    Map applicationMap,  
  5.                                    HttpServletRequest request,  
  6.                                    HttpServletResponse response,  
  7.                                    ServletContext servletContext) {  
  8.        HashMap<String,Object> extraContext = new HashMap<String,Object>();  
  9.        extraContext.put(ActionContext.PARAMETERS, new HashMap(parameterMap));  
  10.        extraContext.put(ActionContext.SESSION, sessionMap);  
  11.        extraContext.put(ActionContext.APPLICATION, applicationMap);  
  12.   
  13.        Locale locale;  
  14.        if (defaultLocale != null) {  
  15.            locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());  
  16.        } else {  
  17.            locale = request.getLocale();  
  18.        }  
  19.   
  20.        extraContext.put(ActionContext.LOCALE, locale);  
  21.        //extraContext.put(ActionContext.DEV_MODE, Boolean.valueOf(devMode));  
  22.   
  23.        extraContext.put(StrutsStatics.HTTP_REQUEST, request);  
  24.        extraContext.put(StrutsStatics.HTTP_RESPONSE, response);  
  25.        extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext);  
  26.   
  27.        // helpers to get access to request/session/application scope  
  28.        extraContext.put("request", requestMap);  
  29.        extraContext.put("session", sessionMap);  
  30.        extraContext.put("application", applicationMap);  
  31.        extraContext.put("parameters", parameterMap);  
  32.   
  33.        AttributeMap attrMap = new AttributeMap(extraContext);  
  34.        extraContext.put("attr", attrMap);  
  35.   
  36.        return extraContext;  
  37.    }  

 

(2)ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                    namespace, name, extraContext, true, false);默认由DefaultActionProxyFactory类创建ActionProxy 。

Java代码   收藏代码
  1. public ActionProxy createActionProxy(String namespace, String actionName, Map extraContext, boolean executeResult, boolean cleanupContext) throws Exception {  
  2.        //创建ActionProxy  
  3.        ActionProxy proxy = new DefaultActionProxy(namespace, actionName, extraContext, executeResult, cleanupContext);  
  4.        container.inject(proxy);  
  5.       //为了创建ActionInvocation  
  6.        proxy.prepare();  
  7.        return proxy;  
  8.    }  

 

    proxy.prepare(); 在这方法中创建ActionInvocation(默认为DefaultActionInvocation),主要由ActionInvocation来调度Action 的实际操作

Java代码   收藏代码
  1. public void prepare() throws Exception {  
  2.        String profileKey = "create DefaultActionProxy: ";  
  3.        try {  
  4.            UtilTimerStack.push(profileKey);  
  5.            config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);  
  6.      
  7.            if (config == null && unknownHandler != null) {  
  8.                config = unknownHandler.handleUnknownAction(namespace, actionName);  
  9.            }  
  10.            if (config == null) {  
  11.                String message;  
  12.      
  13.                if ((namespace != null) && (namespace.trim().length() > 0)) {  
  14.                    message = LocalizedTextUtil.findDefaultText(XWorkMessages.MISSING_PACKAGE_ACTION_EXCEPTION, Locale.getDefault(), new String[]{  
  15.                        namespace, actionName  
  16.                    });  
  17.                } else {  
  18.                    message = LocalizedTextUtil.findDefaultText(XWorkMessages.MISSING_ACTION_EXCEPTION, Locale.getDefault(), new String[]{  
  19.                        actionName  
  20.                    });  
  21.                }  
  22.                throw new ConfigurationException(message);  
  23.            }  
  24.              
  25.            invocation = new DefaultActionInvocation(objectFactory, unknownHandler, this, extraContext, true, actionEventListener);  
  26.           //如果method 为空,则this.method = "execute";  
  27.            resolveMethod();  
  28.        } finally {  
  29.            UtilTimerStack.pop(profileKey);  
  30.        }  
  31.    }  

 

   在创建ActionInvocation 的时候有个主要的方法 init();

Java代码   收藏代码
  1. protected DefaultActionInvocation(final ObjectFactory objectFactory, final UnknownHandler handler, final ActionProxy proxy, final Map extraContext, final boolean pushAction, final ActionEventListener actionEventListener) throws Exception {  
  2.     UtilTimerStack.profile("create DefaultActionInvocation: ",   
  3.             new UtilTimerStack.ProfilingBlock<Object>() {  
  4.                 public Object doProfiling() throws Exception {  
  5.                     DefaultActionInvocation.this.proxy = proxy;  
  6.                        DefaultActionInvocation.this.objectFactory = objectFactory;  
  7.                     DefaultActionInvocation.this.extraContext = extraContext;  
  8.                     DefaultActionInvocation.this.pushAction = pushAction;  
  9.                        DefaultActionInvocation.this.unknownHandler = handler;  
  10.                        DefaultActionInvocation.this.actionEventListener = actionEventListener;  
  11.                        init();//这里  
  12.                     return null;  
  13.                 }  
  14.             });  
  15.    }  

  

   init();方法,该方法创建了Action 和ActionContext

Java代码   收藏代码
  1. private void init() throws Exception {  
  2.         
  3.         Map contextMap = createContextMap();  
  4.       //创建Action  
  5.         createAction(contextMap);  
  6.         
  7.         if (pushAction) {  
  8.            //把Action 放进值栈  
  9.             stack.push(action);  
  10.         }  
  11.         //创建ActionContext  
  12.         invocationContext = new ActionContext(contextMap);  
  13.         invocationContext.setName(proxy.getActionName());  
  14.   
  15.         // get a new List so we don't get problems with the iterator if someone changes the list  
  16.         List interceptorList = new ArrayList(proxy.getConfig().getInterceptors());  
  17.         interceptors = interceptorList.iterator();  
  18.     }  

   

    创建Action,通过objectFactory 进行创建,而这个类在struts.properties中可以重写这个属性 。默认为SpringObjectFactory:struts.objectFactory=spring,在前面BeanSelectionProvider中通过配置文件为ObjectFactory设置实现类  

Java代码   收藏代码
  1. protected void createAction(Map contextMap) {  
  2.         // load action  
  3.         String timerKey = "actionCreate: "+proxy.getActionName();  
  4.         try {  
  5.             UtilTimerStack.push(timerKey);  
  6.             action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);  
  7.         } catch (InstantiationException e) {  
  8.             throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());  
  9.         } catch (IllegalAccessException e) {  
  10.             throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig());  
  11.         } catch (Exception e) {  
  12.             String gripe = "";  
  13.   
  14.             if (proxy == null) {  
  15.                 gripe = "Whoa!  No ActionProxy instance found in current ActionInvocation.  This is bad ... very bad";  
  16.             } else if (proxy.getConfig() == null) {  
  17.                 gripe = "Sheesh.  Where'd that ActionProxy get to?  I can't find it in the current ActionInvocation!?";  
  18.             } else if (proxy.getConfig().getClassName() == null) {  
  19.                 gripe = "No Action defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";  
  20.             } else {  
  21.                 gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ",  defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";  
  22.             }  
  23.   
  24.             gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");  
  25.             throw new XWorkException(gripe, e, proxy.getConfig());  
  26.         } finally {  
  27.             UtilTimerStack.pop(timerKey);  
  28.         }  
  29.   
  30.         if (actionEventListener != null) {  
  31.             action = actionEventListener.prepare(action, stack);  
  32.         }  
  33.     }  
  34.   
  35.   
  36.  public Object buildAction(String actionName, String namespace, ActionConfig config, Map extraContext) throws Exception {  
  37.         return buildBean(config.getClassName(), extraContext);  
  38.     }  
  39.   
  40.  public Object buildBean(String className, Map extraContext) throws Exception {  
  41.         return buildBean(className, extraContext, true);  
  42.     }  
  43.   
  44.  public Object buildBean(String className, Map extraContext, boolean injectInternal) throws Exception {  
  45.         Class clazz = getClassInstance(className);  
  46.         Object obj = buildBean(clazz, extraContext);  
  47.         if (injectInternal) {  
  48.             injectInternalBeans(obj);  
  49.         }  
  50.         return obj;  
  51.     }  
Java代码   收藏代码
  1.  protected Object injectInternalBeans(Object obj) {  
  2.         if (obj != null && container != null) {  
  3.             container.inject(obj);  
  4.         }  
  5.         return obj;  
  6.     }  

  

    proxy.execute();方法是struts2 中流程的重要方法。

 

Java代码   收藏代码
  1. public String execute() throws Exception {  
  2.     ActionContext nestedContext = ActionContext.getContext();  
  3.     ActionContext.setContext(invocation.getInvocationContext());  
  4.   
  5.     String retCode = null;  
  6.   
  7.     String profileKey = "execute: ";  
  8.     try {  
  9.         UtilTimerStack.push(profileKey);  
  10.     //这个是重点,主要的拦截器功能在这实现,执行返回跳转的字符串  
  11.         retCode = invocation.invoke();  
  12.     } finally {  
  13.         if (cleanupContext) {  
  14.             ActionContext.setContext(nestedContext);  
  15.         }  
  16.         UtilTimerStack.pop(profileKey);  
  17.     }  
  18.   
  19.     return retCode;  
  20. }  

  

   invoke 方法调用了DefaultActionInvocation的invoke()去实现Action的调用

Java代码   收藏代码
  1. public String invoke() throws Exception {  
  2. ...  
  3.     try {  
  4.      ......  
  5.   
  6.         if (interceptors.hasNext()) {// (1)  
  7.             final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();  
  8.             UtilTimerStack.profile("interceptor: "+interceptor.getName(),   
  9.                     new UtilTimerStack.ProfilingBlock<String>() {  
  10.             public String doProfiling() throws Exception {  
  11.                 resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);  
  12.                 return null;  
  13.             }  
  14.             });  
  15.         } else {  
  16.             resultCode = invokeActionOnly();  
  17.         }  
  18.   
  19.         if (!executed) {// (2)  
  20.             if (preResultListeners != null) {// (2)-1  
  21.                 for (Iterator iterator = preResultListeners.iterator();  
  22.                     iterator.hasNext();) {  
  23.                     PreResultListener listener = (PreResultListener) iterator.next();  
  24.                       
  25.                     String _profileKey="preResultListener: ";  
  26.                     try {  
  27.                         UtilTimerStack.push(_profileKey);  
  28.                         listener.beforeResult(this, resultCode);  
  29.                     }  
  30.                     finally {  
  31.                         UtilTimerStack.pop(_profileKey);  
  32.                     }  
  33.                 }  
  34.             }  
  35.   
  36.             if (proxy.getExecuteResult()) {// (2)-2  
  37.                 executeResult();  
  38.             }  
  39.   
  40.             executed = true;  
  41.         }  
  42.   
  43.         return resultCode;  
  44.     }  
  45.     finally {  
  46.         UtilTimerStack.pop(profileKey);  
  47.     }  
  48. }  

 

    整个方法主要由2个if从句分割,在(1)处的if从句中,主要实现了拦截器的"递归"调用,说它是递归调用,其实是一种非传统的递归。传统的递归应该是函数调用自身,最后达成一定条件后退出,但是这里是将自身的引用作为参数传递给intercept(),然后在intercept()内部再调用DefaultActionInvocation的invoke(),实现了递归调用。 

利用这种方式,实现了拦截器和Action的如下的调用逻辑: 

Interceptor1 
Interceptor2 
Interceptor3 
Action 
Interceptor3 
Interceptor2 
Interceptor1 

    最后,当interceptors.hasNext()返回false时,也就是全部拦截器调用完毕之后,函数调用了invokeActionOnly();去实现Action的调用:

Java代码   收藏代码
  1. public String invokeActionOnly() throws Exception {  
  2.     return invokeAction(getAction(), proxy.getConfig());  
  3. }  

 

   invokeActionOnly()内部是使用invokeAction()去实现Action的调用的,源代码如下:

Java代码   收藏代码
  1. protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {  
  2.     String methodName = proxy.getMethod();  
  3.   
  4.     if (LOG.isDebugEnabled()) {  
  5.         LOG.debug("Executing action method = " + actionConfig.getMethodName());  
  6.     }  
  7.   
  8.     String timerKey = "invokeAction: "+proxy.getActionName();  
  9.     try {  
  10.         UtilTimerStack.push(timerKey);  
  11.           
  12.         Method method;  
  13.         try {  
  14.             method = getAction().getClass().getMethod(methodName, new Class[0]);  
  15.         } catch (NoSuchMethodException e) {  
  16.             try {  
  17.                 String altMethodName = "do" + methodName.substring(01).toUpperCase() + methodName.substring(1);  
  18.                 method = getAction().getClass().getMethod(altMethodName, new Class[0]);  
  19.             } catch (NoSuchMethodException e1) {  
  20.                 throw e;  
  21.             }  
  22.         }  
  23.   
  24.         Object methodResult = method.invoke(action, new Object[0]);  
  25.         if (methodResult instanceof Result) {  
  26.             this.result = (Result) methodResult;  
  27.             return null;  
  28.         } else {  
  29.             return (String) methodResult;  
  30.         }  
  31.     } catch (NoSuchMethodException e) {  
  32.         throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");  
  33.     } catch (InvocationTargetException e) {  
  34.         Throwable t = e.getTargetException();  
  35.   
  36.         if (actionEventListener != null) {  
  37.             String result = actionEventListener.handleException(t, getStack());  
  38.             if (result != null) {  
  39.                 return result;  
  40.             }  
  41.         }  
  42.         if (t instanceof Exception) {  
  43.             throw(Exception) t;  
  44.         } else {  
  45.             throw e;  
  46.         }  
  47.     } finally {  
  48.         UtilTimerStack.pop(timerKey);  
  49.     }  
  50. }  

 

    由这句Object methodResult = method.invoke(action, new Object[0]);可以看出,最后通过反射实现了Action的执行方法的调用。 


    调用完方法之后,invoke()方法的流程来到了(2)处,由于刚刚调用完Action的那次invoke()调用此时executed为false,所以可以进入此处的if语句。 
(2)-1处调用了在PreResultListener中的定义的一些执行Result前的操作。 
(2)-2处则根据配置文件中的设置执行Result。

Java代码   收藏代码
  1. private void executeResult() throws Exception {  
  2.     result = createResult();// 根据配置文件构建Result  
  3.   
  4.     String timerKey = "executeResult: "+getResultCode();  
  5.     try {  
  6.         UtilTimerStack.push(timerKey);  
  7.         if (result != null) {  
  8.             result.execute(this);  
  9.         } else if (resultCode != null && !Action.NONE.equals(resultCode)) {  
  10.             throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()   
  11.                     + " and result " + getResultCode(), proxy.getConfig());  
  12.         } else {  
  13.             if (LOG.isDebugEnabled()) {  
  14.                 LOG.debug("No result returned for action "+getAction().getClass().getName()+" at "+proxy.getConfig().getLocation());  
  15.             }  
  16.         }  
  17.     } finally {  
  18.         UtilTimerStack.pop(timerKey);  
  19.     }  
  20. }  

 

于是,最终的调用顺序应该是: 
Interceptor1 
Interceptor2 
Interceptor3 
Action 
PreResultListener 
Result 
Interceptor3 
Interceptor2 
Interceptor1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值