当前位置: 代码迷 >> Web前端 >> struts源码之10
  详细解决方案

struts源码之10

热度:452   发布时间:2012-12-27 10:17:10.0
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都是这个类创建的。
struts2包装了ObjectFactory,自成了一个StrutsObjectFactory,他是继承自ObjectFactory的一个类,因此struts2默认产生action的类就不使用xwork的ObjectFactory了,而是使用StrutsObjectFactory。

struts2的插件struts2-spring-plugin-2.1.8.1.jar,又做了一个对象工厂,StrutsSpringObjectFactory,它的父类是xwork的SpringObjectFactory,最原始的父类也是ObjectFactory,我想xwork提供SpringObjectFactory的目的就是让实现这继承这个类,实现spring工厂。

于是struts2中也就是说有3个 对象工厂,
org.apache.struts2.spring.StrutsSpringObjectFactory(struts2的spring插件提供)

org.apache.struts2.impl.StrutsObjectFactory(struts2自带)

com.opensymphony.xwork2.ObjectFactory(xwork自带)。
他们都可以用于产生action。

在xwork中默认使用ObjectFactory,在struts2中默认使用StrutsObjectFactory。使用struts2的spring插件的时候默认使用StrutsSpringObjectFactory,这些都是自动的,其中前两个都是struts2的内容。都是由
<constant name="struts.objectFactory" value="spring" />
来控制,这里的value,就是
<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" class="org.apache.struts2.spring.StrutsSpringObjectFactory" />

在这bean里面定义的是哪个工厂,struts2就用哪个工厂。


但是不管是哪个工厂最终这些都是有xwork控制完成的。
我们可以查看
xwork的DefaultActionInvocation类的createAction(Map<String, Object> contextMap)方法。这个方法就是工厂对象产生action的方法。我们可以看到这个类中的定义的对象工厂是
protected ObjectFactory objectFactory;
然后没有实例化,而是通过xwork的注入机制注入
@Inject
public void setObjectFactory(ObjectFactory fac) {
this.objectFactory = fac;
}
这个地方用的是最原始的ObjectFactory工厂,但是通过我们的配置为什么能变成StrutsSpringObjectFactory或StrutsObjectFactory呢?
这个还没有看完,单我想是多态造成的,因为ObjectFactory是StrutsSpringObjectFactory和StrutsObjectFactory的父类,可以用任意的子类实例化父类。
我想是通过解读
<constant name="struts.objectFactory" value="xia" />
这个配置,来确定注入 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方法是实现整个拦截器机制的核心方法。


?
?核心代码如下:
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。在这个方法中,保证了这些层次的有序调用和执行

  相关解决方案