struts的動態代理:ActionProxy和ActionInvocation

本文分析的源碼涉及三個方面: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是如何處理的。

相關文章
相關標籤/搜索