本文分析的源码涉及三个方面:
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 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 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 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 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 interceptorList = new ArrayList(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,就是上面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 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 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 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是如何处理的。