3. dispatcher.serviceAction(request, response, servletContext, mapping);
这个部分实现了具体的Action调用,由于代码很长,我们来一步一步解说。
(1) 这个方法首先创建了一个名称为extraContext的Map对象,我们来看看它到底是做什么用途的。
这个就是#createContextMap()的源代码,它保存了request,session,application,mapping的信息,这些信息以后可以统一在此对象中查找。
(2) 接下来,
如果request中已经有了一个ValueStack对象,将其保存下来,留待以后恢复,并把它进行一些封装后也存入extraContext中。
(3) 接下来是一些准备工作,如,获取了namespace,name,method等。
(4) 这一步就开始到重点了:构建一个ActionProxy对象,它负责对真实的Action进行调用,并可以在调用Action前后调用拦截器(Interceptor),源代码如下:
下面来看一下ActionProxyFactory的默认实现StrutsActionProxyFactory的#createActionProxy()是怎样构建ActionProxy对象的:
由上述的源代码可见,方法返回了一个StrutsActionProxy对象作为ActionProxy的默认实现。
还有一个关键是proxy调用的#prepare()方法,
这里边创建了一个DefaultActionInvocation对象作为ActionInvocation对象的默认实现。下边又接着调用#resolveMethod()方法:
这个方法实现了Action执行方法的设定,如果config中配置有方法名,那么就将这个方法名作为执行方法名,否则就用默认的execute。
(5) 构建好ActionProxy对象后,将request中的ValueStack更换成上面extraContext中设定的ValueStack。
(6) 之后就要轮到执行Action了。
如果ActionMapping中的Result不是null,那么就直接跳转到Result。否则调用ActionProxy的#execute()。
这里调用了DefaultActionInvocation的invoke()去实现Action的调用。
整个方法主要由2个if从句分割,在(1)处的if从句中,主要实现了拦截器的"递归"调用,说它是递归调用,其实是一种非传统的递归。传统的递归应该是函数调用自身,最后达成一定条件后退出,但是这里是将自身的引用作为参数传递给intercept(),然后在intercept()内部再调用DefaultActionInvocation的invoke(),实现了递归调用。
利用这种方式,实现了拦截器和Action的如下的调用逻辑:
Interceptor1
Interceptor2
Interceptor3
Action
Interceptor3
Interceptor2
Interceptor1
最后,当interceptors.hasNext()返回false时,也就是全部拦截器调用完毕之后,函数调用了invokeActionOnly();去实现Action的调用:
invokeActionOnly()内部是使用invokeAction()去实现Action的调用的,源代码如下:
由这句Object methodResult = method.invoke(action, new Object[0]);可以看出,最后通过反射实现了Action的执行方法的调用。
调用完方法之后,invoke()方法的流程来到了(2)处,由于刚刚调用完Action的那次invoke()调用此时executed为false,所以可以进入此处的if语句。
(2)-1处调用了在PreResultListener中的定义的一些执行Result前的操作。
(2)-2处则根据配置文件中的设置执行Result。
于是,最终的调用顺序应该是:
Interceptor1
Interceptor2
Interceptor3
Action
PreResultListener
Result
Interceptor3
Interceptor2
Interceptor1
这个部分实现了具体的Action调用,由于代码很长,我们来一步一步解说。
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
ActionMapping mapping) throws ServletException {
Map<String, Object> extraContext = createContextMap(request, response, mapping, context);
......
}
(1) 这个方法首先创建了一个名称为extraContext的Map对象,我们来看看它到底是做什么用途的。
public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,
ActionMapping mapping, ServletContext context) {
Map requestMap = new RequestMap(request);// 封装了请求对象
Map params = null;// 封装了http参数
if (mapping != null) {
params = mapping.getParams();// 从ActionMapping中获取Action的参数Map
}
Map requestParams = new HashMap(request.getParameterMap());
if (params != null) {
params.putAll(requestParams);// 并将请求中的参数也放入Map中
} else {
params = requestParams;
}
Map session = new SessionMap(request);// 封装了session
Map application = new ApplicationMap(context);// 封装了ServletContext
/* 将各个Map放入extraContext中 */
Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);
extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
return extraContext;
}
这个就是#createContextMap()的源代码,它保存了request,session,application,mapping的信息,这些信息以后可以统一在此对象中查找。
(2) 接下来,
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
ActionMapping mapping) throws ServletException {
Map<String, Object> extraContext = createContextMap(request, response, mapping, context);
ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
if (stack != null) {
extraContext.put(ActionContext.VALUE_STACK, ValueStackFactory.getFactory().createValueStack(stack));
}
......
}
如果request中已经有了一个ValueStack对象,将其保存下来,留待以后恢复,并把它进行一些封装后也存入extraContext中。
(3) 接下来是一些准备工作,如,获取了namespace,name,method等。
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
ActionMapping mapping) throws ServletException {
......
ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
if (stack != null) {
extraContext.put(ActionContext.VALUE_STACK, ValueStackFactory.getFactory().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();
......
}
(4) 这一步就开始到重点了:构建一个ActionProxy对象,它负责对真实的Action进行调用,并可以在调用Action前后调用拦截器(Interceptor),源代码如下:
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
ActionMapping mapping) throws ServletException {
......
try {
......
Configuration config = configurationManager.getConfiguration();
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
namespace, name, extraContext, true, false);
proxy.setMethod(method);// 设定要调用的方法名
......
}
下面来看一下ActionProxyFactory的默认实现StrutsActionProxyFactory的#createActionProxy()是怎样构建ActionProxy对象的:
public ActionProxy createActionProxy(String namespace, String actionName, Map extraContext,
boolean executeResult, boolean cleanupContext) throws Exception {
ActionProxy proxy = new StrutsActionProxy(namespace, actionName, extraContext, executeResult, cleanupContext);
container.inject(proxy);
proxy.prepare();
return proxy;
}
由上述的源代码可见,方法返回了一个StrutsActionProxy对象作为ActionProxy的默认实现。
还有一个关键是proxy调用的#prepare()方法,
public void prepare() throws Exception {
String profileKey = "create DefaultActionProxy: ";
try {
......
invocation = new DefaultActionInvocation(objectFactory, unknownHandler, this, extraContext, true, actionEventListener);
resolveMethod();
} finally {
UtilTimerStack.pop(profileKey);
}
}
这里边创建了一个DefaultActionInvocation对象作为ActionInvocation对象的默认实现。下边又接着调用#resolveMethod()方法:
private void resolveMethod() {
if (!TextUtils.stringSet(this.method)) {
this.method = config.getMethodName();
if (!TextUtils.stringSet(this.method)) {
this.method = "execute";
}
}
}
这个方法实现了Action执行方法的设定,如果config中配置有方法名,那么就将这个方法名作为执行方法名,否则就用默认的execute。
(5) 构建好ActionProxy对象后,将request中的ValueStack更换成上面extraContext中设定的ValueStack。
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
ActionMapping mapping) throws ServletException {
......
try {
......
proxy.setMethod(method);
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
......
}
(6) 之后就要轮到执行Action了。
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
ActionMapping mapping) throws ServletException {
......
try {
......
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
if (mapping.getResult() != null) {
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {
proxy.execute();
}
......
}
如果ActionMapping中的Result不是null,那么就直接跳转到Result。否则调用ActionProxy的#execute()。
public String execute() throws Exception {
......
try {
......
retCode = invocation.invoke();
} finally {
......
}
return retCode;
}
这里调用了DefaultActionInvocation的invoke()去实现Action的调用。
public String invoke() throws Exception {
......
try {
......
if (interceptors.hasNext()) {// (1)
final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
UtilTimerStack.profile("interceptor: "+interceptor.getName(),
new UtilTimerStack.ProfilingBlock<String>() {
public String doProfiling() throws Exception {
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
return null;
}
});
} else {
resultCode = invokeActionOnly();
}
if (!executed) {// (2)
if (preResultListeners != null) {// (2)-1
for (Iterator iterator = preResultListeners.iterator();
iterator.hasNext();) {
PreResultListener listener = (PreResultListener) iterator.next();
String _profileKey="preResultListener: ";
try {
UtilTimerStack.push(_profileKey);
listener.beforeResult(this, resultCode);
}
finally {
UtilTimerStack.pop(_profileKey);
}
}
}
if (proxy.getExecuteResult()) {// (2)-2
executeResult();
}
executed = true;
}
return resultCode;
}
finally {
UtilTimerStack.pop(profileKey);
}
}
整个方法主要由2个if从句分割,在(1)处的if从句中,主要实现了拦截器的"递归"调用,说它是递归调用,其实是一种非传统的递归。传统的递归应该是函数调用自身,最后达成一定条件后退出,但是这里是将自身的引用作为参数传递给intercept(),然后在intercept()内部再调用DefaultActionInvocation的invoke(),实现了递归调用。
利用这种方式,实现了拦截器和Action的如下的调用逻辑:
Interceptor1
Interceptor2
Interceptor3
Action
Interceptor3
Interceptor2
Interceptor1
最后,当interceptors.hasNext()返回false时,也就是全部拦截器调用完毕之后,函数调用了invokeActionOnly();去实现Action的调用:
public String invokeActionOnly() throws Exception {
return invokeAction(getAction(), proxy.getConfig());
}
invokeActionOnly()内部是使用invokeAction()去实现Action的调用的,源代码如下:
protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
String methodName = proxy.getMethod();
if (LOG.isDebugEnabled()) {
LOG.debug("Executing action method = " + actionConfig.getMethodName());
}
String timerKey = "invokeAction: "+proxy.getActionName();
try {
UtilTimerStack.push(timerKey);
Method method;
try {
method = getAction().getClass().getMethod(methodName, new Class[0]);
} catch (NoSuchMethodException e) {
try {
String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
method = getAction().getClass().getMethod(altMethodName, new Class[0]);
} catch (NoSuchMethodException e1) {
throw e;
}
}
Object methodResult = method.invoke(action, new Object[0]);
if (methodResult instanceof Result) {
this.result = (Result) methodResult;
return null;
} else {
return (String) methodResult;
}
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
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);
}
}
由这句Object methodResult = method.invoke(action, new Object[0]);可以看出,最后通过反射实现了Action的执行方法的调用。
调用完方法之后,invoke()方法的流程来到了(2)处,由于刚刚调用完Action的那次invoke()调用此时executed为false,所以可以进入此处的if语句。
(2)-1处调用了在PreResultListener中的定义的一些执行Result前的操作。
(2)-2处则根据配置文件中的设置执行Result。
private void executeResult() throws Exception {
result = createResult();// 根据配置文件构建Result
String timerKey = "executeResult: "+getResultCode();
try {
UtilTimerStack.push(timerKey);
if (result != null) {
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);
}
}
于是,最终的调用顺序应该是:
Interceptor1
Interceptor2
Interceptor3
Action
PreResultListener
Result
Interceptor3
Interceptor2
Interceptor1