首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > Web前端 >

struts源码之10

2012-12-27 
struts源码之十ActionMapping创建完成,就开始执行exece方法。?if (mapping null) {boolean handled ex

struts源码之十

ActionMapping创建完成,就开始执行exece方法。

?

if (mapping == null) {boolean handled = execute.executeStaticResourceRequest(request, response);if (!handled) {chain.doFilter(request, response);}} else {execute.executeAction(request, response, mapping);}

?

因为mapping创建了,所以一定不为空,一定执行execut方法。

?

public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {        dispatcher.serviceAction(request, response, servletContext, mapping);    }

?

核心还是在Dispatcher,

代码如下:

?

public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,                              ActionMapping mapping) throws ServletException {        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;        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 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 {                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);        }    }

?

?

首先是创建一个额外的 actionContext

Map<String, Object> extraContext = createContextMap(request, response, mapping, context);

?

然后获取ValueStack

ValueStack在处理request的时候放入的,所以现在可以直接取出来。

所以执行下面的代码。

if (stack != null) {            extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));        }

?

额外的actionContext里面添加一个ValueStack

public ValueStack createValueStack(ValueStack stack) {        ValueStack result = new OgnlValueStack(stack, xworkConverter, compoundRootAccessor, allowStaticMethodAccess);        container.inject(result);        stack.getContext().put(ActionContext.CONTAINER, container);        return result;    }

?

然后获取action 相关的信息,通过actionMapping获取。

String namespace = mapping.getNamespace();            String name = mapping.getName();            String method = mapping.getMethod();

?

ActionProxy很重要。

ActionProxy为Action的代理对象?。ActionProxy通过ConfigurationManager询问框架的配置文件,找到需要调用的Action类。

ActionProxy创建一个ActionInvocation的实例。ActionInvocation在ActionProxy层之下,它表示了

??Action的执行状态,或者说它控制的Action的执行步骤。它持有Action实例和所有的Interceptor。?

 ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(                    namespace, name, method, extraContext, true, false);

?

实现的代码如下:

 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);    }

?

创建一个 ActionInvocation,默认的实现是Default.

ActionInvocation代表一个action的执行状态。

定义如下:

?

public interface ActionInvocation extends Serializable {    /**     * Get the Action associated with this ActionInvocation.     *     * @return the Action     */    Object getAction();   boolean isExecuted();   ActionContext getInvocationContext();  /**     * Get the ActionProxy holding this ActionInvocation.     *     * @return the ActionProxy.     */    ActionProxy getProxy(); Result getResult() throws Exception;    /**     * Gets the result code returned from this ActionInvocation.     *     * @return the result code     */    String getResultCode();/**     * Invokes the next step in processing this ActionInvocation.     * <p/>     * If there are more Interceptors, this will call the next one. If Interceptors choose not to short-circuit     * ActionInvocation processing and return their own return code, they will call invoke() to allow the next Interceptor     * to execute. If there are no more Interceptors to be applied, the Action is executed.     * If the {@link ActionProxy#getExecuteResult()} method returns <tt>true</tt>, the Result is also executed.     *     * @throws Exception can be thrown.     * @return the return code.     */    String invoke() throws Exception;....部分代码}

?

需要说明的是 ActionProxy getProxy();

?功能是获取当前ActionInvocation的action代理。

最终的action不是具体实现,而是通过ActionProxy代理创建完成。

?

public interface ActionProxy {    /**     * Gets the Action instance for this Proxy.     *     * @return the Action instance     */    Object getAction();    /**     * Gets the alias name this ActionProxy is mapped to.     *     * @return the alias name     */    String getActionName();    /**     * Gets the ActionConfig this ActionProxy is built from.     *     * @return the ActionConfig     */    ActionConfig getConfig();    /**     * Sets whether this ActionProxy should also execute the Result after executing the Action.     *     * @param executeResult <tt>true</tt> to also execute the Result.     */    void setExecuteResult(boolean executeResult);    /**     * Gets the status of whether the ActionProxy is set to execute the Result after the Action is executed.     *     * @return the status     */    boolean getExecuteResult();    /**     * Gets the ActionInvocation associated with this ActionProxy.     *     * @return the ActionInvocation     */    ActionInvocation getInvocation();    /**     * Gets the namespace the ActionConfig for this ActionProxy is mapped to.     *     * @return the namespace     */    String getNamespace();    /**     * Execute this ActionProxy. This will set the ActionContext from the ActionInvocation into the ActionContext     * ThreadLocal before invoking the ActionInvocation, then set the old ActionContext back into the ThreadLocal.     *     * @return the result code returned from executing the ActionInvocation     * @throws Exception can be thrown.     * @see ActionInvocation     */    String execute() throws Exception;    /**     * Gets the method name to execute, or <tt>null</tt> if no method has been specified (meaning <code>execute</code> will be invoked).     *     * @return the method to execute     */    String getMethod();    /**     * Gets status of the method value's initialization.     *     * @return true if the method returned by getMethod() is not a default initializer value.     */    boolean isMethodSpecified();    }

?

ActionProxy是Action的一个代理类,也就是说Action的调用是通过ActionProxy实现的,其实就是调用了ActionProxy.execute()方法,而该方法又调用了ActionInvocation.invoke()方法。归根到底,最后调用的是DefaultActionInvocation.invokeAction()方法。
DefaultActionInvocation()->init()->createAction()。
最后通过调用ActionProxy.exute()-->ActionInvocation.invoke()-->Intercepter.intercept()-->ActionInvocation.invokeActionOnly()-->invokeAction()
这里的步骤是先由ActionProxyFactory创建ActionInvocation和ActionProxy.

?

继续回到执行代码中

 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;    }

?

ActionInvocation里的ActionProxy是在这里创建的默认提供的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 = StringEscapeUtils.escapeHtml4(actionName);        this.namespace = namespace;        this.executeResult = executeResult;        this.method = StringEscapeUtils.escapeEcmaScript(StringEscapeUtils.escapeHtml4(methodName));    }

?

然后调用代理的预处理工作

?proxy.prepare();

protected void prepare() {        String profileKey = "create DefaultActionProxy: ";        try {            UtilTimerStack.push(profileKey);            config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);            if (config == null && unknownHandlerManager.hasUnknownHandlers()) {                config = unknownHandlerManager.handleUnknownAction(namespace, actionName);            }            if (config == null) {                throw new ConfigurationException(getErrorMessage());            }            resolveMethod();            if (!config.isAllowedMethod(method)) {                throw new ConfigurationException("Invalid method: " + method + " for action " + actionName);            }            invocation.init(this);        } finally {            UtilTimerStack.pop(profileKey);        }    }

?

首先是获取ActionConfig

?config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);

public synchronized ActionConfig getActionConfig(String namespace, String name) {            ActionConfig config = findActionConfigInNamespace(namespace, name);            // try wildcarded namespaces            if (config == null) {                NamespaceMatch match = namespaceMatcher.match(namespace);                if (match != null) {                    config = findActionConfigInNamespace(match.getPattern(), name);                    // If config found, place all the matches found in the namespace processing in the action's parameters                    if (config != null) {                        config = new ActionConfig.Builder(config)                                .addParams(match.getVariables())                                .build();                    }                }            }            // fail over to empty namespace            if ((config == null) && (namespace != null) && (!"".equals(namespace.trim()))) {                config = findActionConfigInNamespace("", name);            }            return config;        }

?

 ActionConfig findActionConfigInNamespace(String namespace, String name) {            ActionConfig config = null;            if (namespace == null) {                namespace = "";            }            Map<String, ActionConfig> actions = namespaceActionConfigs.get(namespace);            if (actions != null) {                config = actions.get(name);                // Check wildcards                if (config == null) {                    config = namespaceActionConfigMatchers.get(namespace).match(name);                    // fail over to default action                    if (config == null) {                        String defaultActionRef = namespaceConfigs.get(namespace);                        if (defaultActionRef != null) {                            config = actions.get(defaultActionRef);                        }                    }                }            }            return config;        }

?

如果config创建失败,抛出异常

下面是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 (StringUtils.isEmpty(this.method)) {            this.method = config.getMethodName();            if (StringUtils.isEmpty(this.method)) {                this.method = ActionConfig.DEFAULT_METHOD;            }            methodSpecified = false;        }    }

?

然后是invocation的初始化。

invocation.init(this);

?

public void init(ActionProxy proxy) {        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.setActionInvocation(this);        }        createAction(contextMap);        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();    }

?

interceptor初始化的时候先创建一个createContextMap

contextmap是通过刚开始的时候额外的 contextmap提供的。

protected Map<String, Object> createContextMap() {        Map<String, Object> contextMap;        if ((extraContext != null) && (extraContext.containsKey(ActionContext.VALUE_STACK))) {            // In case the ValueStack was passed in            stack = (ValueStack) extraContext.get(ActionContext.VALUE_STACK);            if (stack == null) {                throw new IllegalStateException("There was a null Stack set into the extra params.");            }            contextMap = stack.getContext();        } else {            // create the value stack            // this also adds the ValueStack to its context            stack = valueStackFactory.createValueStack();            // create the action context            contextMap = stack.getContext();        }        // put extraContext in        if (extraContext != null) {            contextMap.putAll(extraContext);        }        //put this DefaultActionInvocation into the context map        contextMap.put(ActionContext.ACTION_INVOCATION, this);        contextMap.put(ActionContext.CONTAINER, container);        return contextMap;    }

?

然后把当前的actioninvocation放入context 中,然后返回

contextMap.put(ActionContext.ACTION_INVOCATION, this);        contextMap.put(ActionContext.CONTAINER, container);

?

然后讲当前的actioninvocation 放入actioncontext中

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

?

然后创建action

?createAction(contextMap);

具体代码如下

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创建

 action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);

?

具体的代码如下:

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;    }

?

public Class getClassInstance(String className) throws ClassNotFoundException {        if (ccl != null) {            return ccl.loadClass(className);        }        return ClassLoaderUtil.loadClass(className, this.getClass());    }

?

通过class类加载器创建

然后讲当前的action注入容器中。

if (injectInternal) {
??????????? injectInternalBeans(obj);
??????? }

?

struts2中的ObjectFactory到底是什么呢?

ObjectFactory,是xwork中很重要的一个类,是产生action的地方。单独使用xwork的时候,action都是这个类创建的。
来控制,这里的value,就是
<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" />

在这bean里面定义的是哪个工厂,这个配置,来确定注入 objectFactory的那个子类。

我们可以在DefaultActionInvocation的createAction方法打印一下objectFactory
会发现,使用不同的对象工厂,那么这里会打印相应的工厂。绝对不只是ObjectFactory这个父类?然后返回到init方法中,创建interceptorList
List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());        interceptors = interceptorList.iterator();
?最终是通过ActionConfig创建并返回的ActionProxy创建完成,返回到Dispatcher中?
// 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 {                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);            }
?然后执行execute方法
public String execute() throws Exception {        ActionContext previous = ActionContext.getContext();        ActionContext.setContext(invocation.getInvocationContext());        try {// This is for the new API://            return RequestContextImpl.callInContext(invocation, new Callable<String>() {//                public String call() throws Exception {//                    return invocation.invoke();//                }//            });            return invocation.invoke();        } finally {            if (cleanupContext)                ActionContext.setContext(previous);        }    }
? return invocation.invoke();?
public String invoke() throws Exception {        String profileKey = "invoke: ";        try {            UtilTimerStack.push(profileKey);            if (executed) {                throw new IllegalStateException("Action has already executed");            }            if (interceptors.hasNext()) {                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 {                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) {                    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);        }    }
?核心的是?resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);然后执行一系列的interceptor?所有的interceptor继承AbstractInterceptor
public abstract class AbstractInterceptor implements Interceptor {    /**     * Does nothing     */    public void init() {    }        /**     * Does nothing     */    public void destroy() {    }    /**     * Override to handle interception     */    public abstract String intercept(ActionInvocation invocation) throws Exception;}
?Interceptor说明Interceptor的接口定义没有什么特别的地方,除了init和destory方法以外,intercept方法是实现整个拦截器机制的核心方法。
struts源码之10
??核心代码如下:
if (interceptors.hasNext()) {                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 {                resultCode = invokeActionOnly();            }
?

每个拦截器中的代码的执行顺序,在Action之前,拦截器的执行顺序与堆栈中定义的一致;而在Action和Result之后,拦截器的执行顺序与堆栈中定义的顺序相反。

Interceptor拦截类型

从上面的分析,我们知道,整个拦截器的核心部分是invocation.invoke()这个函数的调用位置。事实上,我们也正式根据这句代码的调用位置,来进行拦截类型的区分的。在Struts2中,Interceptor的拦截类型,分成以下三类:

1. before

before拦截,是指在拦截器中定义的代码,它们存在于invocation.invoke()代码执行之前。这些代码,将依照拦截器定义的顺序,顺序执行

2. after

after拦截,是指在拦截器中定义的代码,它们存在于invocation.invoke()代码执行之后。这些代码,将一招拦截器定义的顺序,逆序执行

?

PreResultListener
有的时候,before拦截和after拦截对我们来说是不够的,因为我们需要在Action执行完之后,但是还没有回到视图层之前,做一些事情。Struts2同样支持这样的拦截,这种拦截方式,是通过在拦截器中注册一个PreResultListener的接口来实现的。

如:在拦截器中使用如下代码,其中MyPreResultListener实现了PreResultListener 接口并在beforeResult方法中做了一些事情然后在拦截器类中加入action.addPreResultListener(new MyPreResultListener());

?

从源码中,我们可以看到,我们之前提到的Struts2的Action层的4个不同的层次,在这个方法中都有体现,他们分别是:拦截器(Interceptor)、Action、PreResultListener和Result。在这个方法中,保证了这些层次的有序调用和执行

热点排行