共计 12527 个字符,预计需要花费 32 分钟才能阅读完成。
本文剖析的源码波及三个方面:
1 struts 中应用到的动静代理:ActionProxy 和 ActionInvocation
2 struts 中是如何解决拦截器和 action 申请的
3 struts 中是如何处理结果的
每个申请都会通过 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。
ActionProxyFactory 如何创立 ActionProxy?
ActionProxyFactory 是一个接口,默认的工厂类是 DefaultActionProxyFactory,其中的 createActionProxy 办法如下:
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 会做些什么
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 是如何解决的。