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都是这个类创建的。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。在这个方法中,保证了这些层次的有序调用和执行