刨根問底-struts-http請求邏輯執行

    

    上一節分析了FilterDispatcher中的doFilter()方法中的前半部分,主要是分析了經過actionMapper.getMapping獲取ActionMapping對象,解析請求路徑,尋找出namespace,name等信息。 java

    這章詳細講解doFilter()後面的重點dispatcher.serviceAction()。 mvc

    這裏主要的工做是根據ActionMapping對象,建立一個新的action對象,action對象是經過java反射機制差建立的,這裏也就是說明了爲何struts2是單實例。而後再建立action方法實例,而且執行該方法。 app

整個流程如圖: 函數


   一、 繼續分析doFilter(),正式執行action ui

dispatcher.serviceAction(request, response, servletContext, mapping);
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
                              ActionMapping mapping) throws ServletException {
        //建立mvc運行的數據環境
        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;
    //沒有找到已存在的valueStack,則從ActionContext中獲取當前線程的values
        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,這裏已經徹底進入xwork的世界了
            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 {
            //執行ActionProxy,真正運行xwork中的mvc實現
                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);
        }
    }

註釋:(1) //createContextMap方法主要把Application、Session、Request的key value值拷貝到Map中  this

(2) config.getContainer().getInstance(ActionProxyFactory.class)建立一個Action的代理對象,ActionProxyFactory是建立ActionProxy的工廠              spa

參考實現類:DefaultActionProxy和DefaultActionProxyFactory   線程

(3)createActionProxy()方法建立一個action 的代理對象 debug

二、createActionProxy()建立一個新的action對象,源碼: 代理

public ActionProxy createActionProxy(String namespace, String actionName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {
        return createActionProxy(namespace, actionName, null, extraContext, executeResult, cleanupContext);
    }

 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);
    }
註釋:(1)首先調用了帶有5個參數的createActionProxy方法,在這個方法並無複雜的處理,知識簡單的調用了多了一個參數methodName的createActionProxy(),請注意這個methodName參數賦值爲null.

(2)建立了DefaultActionInvocation對象,而且把上面封裝的參數map傳進去

在這首先有必要詳細講解一下DefaultActionInvocation這個類,這個類主要的做用操做ActionProxy,而後再回來:

三、DefaultActionInvocation中init()方法:

public void init(ActionProxy proxy) {
        this.proxy = proxy;
        //建立上下文環境,這裏的contextMap與ActionContext的上下文環境一致
        Map<String, Object> contextMap = createContextMap();

        // Setting this so that other classes, like object factories, can use the ActionProxy and other
        // contextual information to operate
        //將ActionInvocation對象設置到actionContext中,這樣作得好處能夠利用actionContext的數據共享特性,將ActionInvocation在整個執行週期共享。
        ActionContext actionContext = ActionContext.getContext();

        if (actionContext != null) {
            actionContext.setActionInvocation(this);
        }

        createAction(contextMap);//這裏是重點,建立action對象
        //將action對象置於valueStack中
        if (pushAction) {
            stack.push(action);
            contextMap.put("action", action);
        }

        invocationContext = new ActionContext(contextMap);
        invocationContext.setName(proxy.getActionName());

        // get a new List so we don't get problems with the iterator if someone changes the list
        List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
        interceptors = interceptorList.iterator();//攔截器
    }

註釋:(1)建立上下文環境

(2)ActionInvocation對象的共享

(3)建立action對象,createAction(contextMap)建立Action,struts2中每個Request都會建立一個新的Action

(4)將action對象置入valueStack

(5)建立ActionInvocation的上下文環境

(6)將攔截器堆棧置於初始調度狀態,而且把proxy中的攔截器,傳值給全局變量interceptors(Iterator<InterceptorMapping>)

3.1 看看createAction()方法

protected void createAction(Map<String, Object> contextMap) {
        // load action
        String timerKey = "actionCreate: " + proxy.getActionName();
        try {
            UtilTimerStack.push(timerKey);
            action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
        } catch (InstantiationException e) {
            throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());
        } catch (IllegalAccessException e) {
            throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig());
        } catch (Exception e) {
            String gripe = "";

            if (proxy == null) {
                gripe = "Whoa!  No ActionProxy instance found in current ActionInvocation.  This is bad ... very bad";
            } else if (proxy.getConfig() == null) {
                gripe = "Sheesh.  Where'd that ActionProxy get to?  I can't find it in the current ActionInvocation!?";
            } else if (proxy.getConfig().getClassName() == null) {
                gripe = "No Action defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";
            } else {
                gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ",  defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";
            }

            gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");
            throw new XWorkException(gripe, e, proxy.getConfig());
        } finally {
            UtilTimerStack.pop(timerKey);
        }

        if (actionEventListener != null) {
            action = actionEventListener.prepare(action, stack);
        }
    }
註釋: action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap)建立action對象

3.2buildAction()方法:

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

  public Object buildBean(String className, Map<String, Object> extraContext) throws Exception {
        return buildBean(className, extraContext, true);
    }

   public Object buildBean(String className, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
        Class clazz = getClassInstance(className);
        Object obj = buildBean(clazz, extraContext);
        if (injectInternal) {
            injectInternalBeans(obj);
        }
        return obj;
    }
註釋:最後經過java反射機制建立對象,這裏就是爲何struts2是單實例的緣由吧。由於每次請求來都是java反射機制建立一個新的對象。

四、DefaultActionInvocation中的invoke()方法:

/**
     * @throws ConfigurationException If no result can be found with the returned code
     */
    public String invoke() throws Exception {
        String profileKey = "invoke: ";
        try {
            UtilTimerStack.push(profileKey);

            if (executed) {
                throw new IllegalStateException("Action has already executed");
            } //遞歸執行interceptor                                                                     if (interceptors.hasNext()) { //經過調用Invocation.invoke()實現遞歸牡循環     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 {//當全部interceptor都執行完,最後執行Action,invokeActionOnly會調用invokeAction()方法 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                                            //在Result返回以前調用preResultListeners                                                //經過executed控制,只執行一次                                                                       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()) { //執行Result    executeResult();
                }

                executed = true;
            }

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

註釋:(1)執行每個攔截器

(2)執行action方法,而且返回結果類型

(3)執行每個PreResultListener對象的beforeResult()方法

(4)執行結果

這個流程就是典型的struts流程圖中的中間的那一部分。

4.1 invokeActionOnly()方法:

public String invokeActionOnly() throws Exception {
        return invokeAction(getAction(), proxy.getConfig());
    }

 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 {//java反射機制獲得要執行的方法                                                            method = getAction().getClass().getMethod(methodName, new Class[0]);
            } catch (NoSuchMethodException e) {
                // hmm -- OK, try doXxx instead
                try { //若是沒有對應的方法,則使用do+Xxxx來再次得到方法                                             String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
                    method = getAction().getClass().getMethod(altMethodName, new Class[0]);
                } 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) {//執行Method     methodResult = method.invoke(action, new Object[0]);
            }

            if (methodResult instanceof Result) {
                this.explicitResult = (Result) methodResult;

                // Wire the result automatically
                container.inject(explicitResult);
                return null;
            } else {
                return (String) 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);
        }
    }
註釋:(1)getAction()得到就是上面 createAction()建立的action對象,而後經過java反射機制得到要執行的方法

(2)執行該方法

4.2action執行完了,還要根據ResultConfig返回到view,也就是在invoke方法中調用executeResult方法

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);
        }
    }
註釋:(1)經過createResult()建立Result


(2)執行該結果

4.2.1 createResult()

public Result createResult() throws Exception { //若是Action中直接返回的Result類型,在invokeAction()保存在explicitResult     if (explicitResult != null) {
            Result ret = explicitResult;
            explicitResult = null;

            return ret;
        }
        ActionConfig config = proxy.getConfig();
        Map<String, ResultConfig> results = config.getResults();

        ResultConfig resultConfig = null;

        synchronized (config) {
            try { //返回的是String則從config中獲得當前Action的Results列表    resultConfig = results.get(resultCode);
            } catch (NullPointerException e) {
                // swallow
            }
            if (resultConfig == null) {
                // If no result is found for the given resultCode, try to get a wildcard '*' match.//若是找不到對應name的ResultConfig,則使用name爲*的Result                               //說明能夠用*通配全部的Result   resultConfig = results.get("*");
            }
        }

        if (resultConfig != null) {
            try { //建立Result      return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
            } catch (Exception e) {
                LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);
                throw new XWorkException(e, resultConfig);
            }
        } else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {
            return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);
        }
        return null;
    }
註釋:(1) 若是Action中直接返回的Result類型,在invokeAction()保存在explicitResult     


(2)若是explicitResult爲null,就經過resultCode在返回列表中查找相應的返回配置、

(3)經過objectFactory.buildResult建立result對象

五、返回到步驟2createActionProxy()中,建立完成DefaultActionInvocation對象,繼續調用createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext)

public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {

        DefaultActionProxy proxy = new DefaultActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
        container.inject(proxy);
        proxy.prepare();
        return proxy;
    }
註釋:(1)建立DefaultActionProxy對象

(2)執行proxy.prepare()

六、DefaultActionProxy的構造函數:

protected DefaultActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
        
        this.invocation = inv;
		this.cleanupContext = cleanupContext;
		if (LOG.isDebugEnabled()) {
			LOG.debug("Creating an DefaultActionProxy for namespace " + namespace + " and action name " + actionName);
		}

		this.actionName = actionName;
		this.namespace = namespace;
		this.executeResult = executeResult;
        this.method = methodName;
    }
七、 DefaultActionProxy的prepare() 代碼:

protected void prepare()  {
        String profileKey = "create DefaultActionProxy: ";
        try {
            UtilTimerStack.push(profileKey);
            config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);
    
            if (config == null && unknownHandler != null) {
                config = unknownHandler.handleUnknownAction(namespace, actionName);
            }
            if (config == null) {
                String message;
    
                if ((namespace != null) && (namespace.trim().length() > 0)) {
                    message = LocalizedTextUtil.findDefaultText(XWorkMessages.MISSING_PACKAGE_ACTION_EXCEPTION, Locale.getDefault(), new String[]{
                        namespace, actionName
                    });
                } else {
                    message = LocalizedTextUtil.findDefaultText(XWorkMessages.MISSING_ACTION_EXCEPTION, Locale.getDefault(), new String[]{
                        actionName
                    });
                }
                throw new ConfigurationException(message);
            }

            resolveMethod();
            
            if (!config.isAllowedMethod(method)) {
                throw new ConfigurationException("Invalid method: "+method+" for action "+actionName);
            }

            invocation.init(this);

        } finally {
            UtilTimerStack.pop(profileKey);
        }
    }
註釋:(1)config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName),經過namespace和actionName查找struts.xml配置中相對應的 config 

(2) resolveMethod()解析方法名稱

(3) invocation.init(this)

八、resolveMethod()

private void resolveMethod() {
        // if the method is set to null, use the one from the configuration
        // if the one from the configuration is also null, use "execute"
        if (!TextUtils.stringSet(this.method)) {
            this.method = config.getMethodName();
            if (!TextUtils.stringSet(this.method)) {
                this.method = "execute";
            }
        }
    }
註釋;這裏判斷this.method是否爲空,這裏是null,由於上面建立 DefaultActionProxy對象時候這個參數爲null。因此須要從config.getMethodName()得到方法名稱,若是方法名稱還爲空,這裏就會默認 "execute"。哈哈,這就是爲何咱們不配置action的方法名稱,默認的執行execute方法。

八、到這裏就建立好ActionProxy對象,返回到步驟1serviceAction方法中,分析ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                    namespace, name, method, extraContext, true, false)。

建立完成ActionProxy對象,就會執行 proxy.execute()

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;
    }
註釋:這裏調用invocation.invoke()。
相關文章
相關標籤/搜索