本文分析的源碼涉及三個方面:java
1 struts中使用到的動態代理:ActionProxy 和 ActionInvocationweb
2 struts中是如何處理攔截器和action請求的json
3 struts中是如何處理結果的app
每一個請求都會經過Dispatcher中的serviceAction方法,這個方法中有很重要的一句代碼工具
String namespace = mapping.getNamespace(); String name = mapping.getName(); String method = mapping.getMethod(); //步驟1 ActionProxy proxy = 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! // 後面再分析result if (mapping.getResult() != null) { Result result = mapping.getResult(); result.execute(proxy.getInvocation()); } else { // 步驟2 proxy.execute(); }
這裏面比較重要的一句是獲取proxy的這一句,從container中先獲取一個ActionProxyFactory的實例,而後根據命名空間(struts.xml的package?),action的名字,方法名,上下文生成一個ActionProxy,實際是ActionProxy執行了action。ui
ActionProxyFactory如何建立ActionProxy?this
ActionProxyFactory是一個接口,默認的工廠類是DefaultActionProxyFactory,其中的createActionProxy方法以下:spa
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) { // 建立了一個ActionInvocation實例 ActionInvocation inv = createActionInvocation(extraContext, true); container.inject(inv); // 經過下面的createActionProxy可知ActionProxy中持有了對ActionInvocation的引用 return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext); } protected ActionInvocation createActionInvocation(Map<String, Object> extraContext, boolean pushAction) { return new DefaultActionInvocation(extraContext, pushAction); } public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) { // 傳進去的inv會在prepare的時候用到 DefaultActionProxy proxy = new DefaultActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext); container.inject(proxy); // 有一些初始化操做,好比methodname是null的時候就默認執行execute方法 proxy.prepare(); return proxy; }
在上面的proxy.prepare 中,除了會對methodname有處理,還有重要的一步是invocation.init(this),這裏的invocation就是new DefaultActionProxy的時候傳入的inv,下面看看DefaultActionInvocation中的init會作些什麼debug
public void init(ActionProxy proxy) { // 可見actionInvocation中也持有一個actionProxy的引用 this.proxy = proxy; Map<String, Object> contextMap = createContextMap(); // Setting this so that other classes, like object factories, can use the ActionProxy and other // contextual information to operate ActionContext actionContext = ActionContext.getContext(); if (actionContext != null) { // actionContext也有一個對actioninvocation的引用 actionContext.setActionInvocation(this); } // 建立action實例 createAction(contextMap); if (pushAction) { stack.push(action); contextMap.put("action", action); } invocationContext = new ActionContext(contextMap); invocationContext.setName(proxy.getActionName()); //建立相關攔截器 createInterceptors(proxy); } //具體看下actioninvocation是如何建立一個action的 protected void createAction(Map<String, Object> contextMap) { // load action String timerKey = "actionCreate: " + proxy.getActionName(); try { UtilTimerStack.push(timerKey); // actioninvocation中有一個action的全局變量 // 主要就是這一句,這裏用到了objectfactory,這個工廠類持有對actionfactory的引用,實際是調用了actionfactory的buildaction // todo actionfactory的buildaction 後面再具體看actionfactory 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); } } //建立攔截器就比較簡單了,根據配置文件把全部的攔截器放在一個list中 protected void createInterceptors(ActionProxy proxy) { // 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(); }
理一下前面的:代理
dispatcher中先建立了actionproxy,同時會建立一個actioninvocation,而後對methodname對處理,建立action實例,建立攔截器list。
到目前爲止,dispatcher中的步驟1 完成。如今開始步驟2,步驟2就是具體的要執行action的execute方法。
DefaultActionProxy的execute代碼以下
public String execute() throws Exception { //context 相關後續分析 ActionContext nestedContext = ActionContext.getContext(); ActionContext.setContext(invocation.getInvocationContext()); String retCode = null; String profileKey = "execute: "; try { UtilTimerStack.push(profileKey); // proxy中使用invocation完成action的執行,和java的動態代理是同樣的 retCode = invocation.invoke(); } finally { if (cleanupContext) { ActionContext.setContext(nestedContext); } UtilTimerStack.pop(profileKey); } return retCode; }
接着看看DefaultActionInvocation的invoke作了什麼:
public String invoke() throws Exception { String profileKey = "invoke: "; try { UtilTimerStack.push(profileKey); if (executed) { throw new IllegalStateException("Action has already executed"); } // 這裏的interceptors 類型是Iterator<InterceptorMapping>,就是上面createInterceptors的時候被賦值的 // 遍歷interceptors if (interceptors.hasNext()) { final InterceptorMapping interceptor = interceptors.next(); String interceptorMsg = "interceptor: " + interceptor.getName(); UtilTimerStack.push(interceptorMsg); try { // 每拿到一個攔截器,就執行攔截器的intercept方法,在攔截器的intercept方法中必定會執行incovation.invoke,就會又調到本方法中 // 結合這裏,就可以知道攔截器的執行順序,每一個攔截器執行到invocation.invoke以後都會遞歸的調到下一個攔截器上 // 假設有三個攔截器A B C,把每一個攔截器在invocation.invoke前的代碼成爲invoke前,以後的代碼成爲invoke後 // 執行順序就是:A的invoke前,B的invoke前,c的invoke前,action,c的invoke後,B的invoke後,A的invoke後 resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); } finally { UtilTimerStack.pop(interceptorMsg); } } else { // 攔截器鏈中最後一個攔截器的invation.invoke就會執行action自己 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) { LOG.trace("Executing PreResultListeners for result [#0]", result); 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); } } public String invokeActionOnly() throws Exception { // 這裏的getaction方法就是invocation持有的action實例,即在前面建立action實例的時候建立的 return invokeAction(getAction(), proxy.getConfig()); }
下面看一下invokeAction方法中是怎麼執行action的:
protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception { // 獲取要執行方法名,是否有必要使用默認的execute也早在proxy的prepare中處理過了 String methodName = proxy.getMethod(); if (LOG.isDebugEnabled()) { LOG.debug("Executing action method = #0", methodName); } String timerKey = "invokeAction: " + proxy.getActionName(); try { UtilTimerStack.push(timerKey); Object methodResult; try { //使用onglUtil這個工具類執行action的方法,具體的就要了解onglutil作了啥了。。 methodResult = ognlUtil.callMethod(methodName + "()", getStack().getContext(), action); } catch (MethodFailedException e) { // if reason is missing method, try find version with "do" prefix if (e.getReason() instanceof NoSuchMethodException) { try { String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1) + "()"; methodResult = ognlUtil.callMethod(altMethodName, getStack().getContext(), action); } catch (MethodFailedException e1) { // if still method doesn't exist, try checking UnknownHandlers if (e1.getReason() instanceof NoSuchMethodException) { if (unknownHandlerManager.hasUnknownHandlers()) { try { methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName); } catch (NoSuchMethodException e2) { // throw the original one throw e; } } else { // throw the original one throw e; } // throw the original exception as UnknownHandlers weren't able to handle invocation as well if (methodResult == null) { throw e; } } else { // exception isn't related to missing action method, throw it throw e1; } } } else { // exception isn't related to missing action method, throw it throw e; } } // 處理執行完方法後拿到的methodResult,而後返回處理後的結果 return saveResult(actionConfig, methodResult); } catch (NoSuchPropertyException e) { throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + ""); } catch (MethodFailedException e) { // We try to return the source exception. Throwable t = e.getCause(); 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); } }
saveReult:
// 若是methodresult是Result類型就保存到explicitResult(explicitResult是actioninvocation中一個Rsult類型的全局變量)上,返回null,不然直接返回string的methodResult protected String saveResult(ActionConfig actionConfig, Object methodResult) { if (methodResult instanceof Result) { this.explicitResult = (Result) methodResult; // Wire the result automatically container.inject(explicitResult); return null; } else { return (String) methodResult; } }
再回到invoke中,執行完action以後有一段代碼
// now execute the result, if we're supposed to if (proxy.getExecuteResult()) { executeResult(); } // 具體的executeResult以下 private void executeResult() throws Exception { // create a Result result = createResult(); String timerKey = "executeResult: " + getResultCode(); try { UtilTimerStack.push(timerKey); if (result != null) { // 執行Result的execute 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); } } //具體的createResult以下 public Result createResult() throws Exception { LOG.trace("Creating result related to resultCode [#0]", resultCode); if (explicitResult != null) { 把以前在saveResult中保存到explicitResult中的值賦值到ret中,同時explicitResult清空 Result ret = explicitResult; explicitResult = null; // 若是explicitResult不爲空,即以前得到的結果直接就是一個result類型,那麼直接返回 // 不然以前得到結果是一個普通的字符串,須要找到字符串對應的Result return ret; } ActionConfig config = proxy.getConfig(); // 配置文件中配置的result節點信息 Map<String, ResultConfig> results = config.getResults(); ResultConfig resultConfig = null; try { // 嘗試獲取resultCode對應的resultconfig resultConfig = results.get(resultCode); } catch (NullPointerException e) { if (LOG.isDebugEnabled()) { LOG.debug("Got NPE trying to read result configuration for resultCode [#0]", resultCode); } } if (resultConfig == null) { // If no result is found for the given resultCode, try to get a wildcard '*' match. resultConfig = results.get("*"); } if (resultConfig != null) { try { // 根據resultconfig 構造一個Result返回 return objectFactory.buildResult(resultConfig, invocationContext.getContextMap()); } catch (Exception e) { if (LOG.isErrorEnabled()) { LOG.error("There was an exception while instantiating the result of type #0", e, resultConfig.getClassName()); } 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; }
那麼,Result究竟是什麼?
// Result接口很簡單,只提供一個execute方法 public interface Result extends Serializable { /** * Represents a generic interface for all action execution results. * Whether that be displaying a webpage, generating an email, sending a JMS message, etc. * * @param invocation the invocation context. * @throws Exception can be thrown. */ public void execute(ActionInvocation invocation) throws Exception; }
struts在配置action的時候,會相應的配置result節點,其中包括result的type,每個type其實對應一個具體的Result實現類,上面的objectFactory.buildResult實則調的是resultFactory的buildResult。
默認的ResultFactory是StrutsResultFactory
public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception { // config中有配置type,找到對應的具體result實現類 String resultClassName = resultConfig.getClassName(); Result result = null; if (resultClassName != null) { // 生成具體result實現類的實例 // 回到上面的result.execute,就可知道這個時候獲取了result實例,不一樣type的result的execute有不一樣的處理 result = (Result) objectFactory.buildBean(resultClassName, extraContext); Map<String, String> params = resultConfig.getParams(); if (params != null) { setParameters(extraContext, result, params); } } return result; }
至此,大概知道了struts的動態代理的使用,如何用動態代理模式處理action請求和攔截器,如何處理result。
todo:
1 上面只是大概流程,可找幾點具體分析
2 context相關
3 struts的工廠類 ObjectFactory及具體的各類工廠類
4 msite如今使用的json result是如何處理的。