上一節分析了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()。