com.opensymphony.webwork下有两个类:public class ServletActionContext extends ActionContext implements WebWorkStatics;
这个类是静态工具类,其内的方法均依赖com.opensymphony.xwork.ActionContext类,而这个类提供了Action类的执行上下文.我们跟踪这个上下文的生命周期:
客户请求发送到Webserver,它将请求转发到Servlet容器,容器匹配一个Sertlet,然后调用其sevice()方法.在WebWork中这个servlet是ServletDispatcher,其中的serviceAction方法通过代理模式从WebWork进入到XWork对象世界,在那里对客户请求数据对象执行处理,通过执行根据配置上下文和执行上下文设置的Action和result把响应数据发回客户端,发回的动作可能发生在Result中,也可能发生在Interceptor中.开始代码之旅.
下面是serviceAction代码:
public void serviceAction(HttpServletRequest request, HttpServletResponse response, String namespace, String actionName, Map requestMap, Map parameterMap, Map sessionMap, Map applicationMap) {
HashMap extraContext = createContextMap(requestMap, parameterMap, sessionMap, applicationMap, request, response, getServletConfig());
extraContext.put(SERVLET_DISPATCHER, this);
// If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
OgnlValueStack stack = (OgnlValueStack) request.getAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY);
if (stack != null) {
extraContext.put(ActionContext.VALUE_STACK,new OgnlValueStack(stack));
}
try {
ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy(namespace, actionName, extraContext);
request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY, proxy.getInvocation().getStack());
proxy.execute();
// If there was a previous value stack then set it back onto the request
if (stack != null){
request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY,stack);
}
} catch (ConfigurationException e) {
log.error("Could not find action", e);
sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
} catch (Exception e) {
log.error("Could not execute action", e);
sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
}
}
方法createContextMap执行数据的包装:
public static HashMap createContextMap(Map requestMap, Map parameterMap, Map sessionMap, Map applicationMap, HttpServletRequest request, HttpServletResponse response, ServletConfig servletConfig) {
HashMap extraContext = new HashMap();
extraContext.put(ActionContext.PARAMETERS, parameterMap);
extraContext.put(ActionContext.SESSION, sessionMap);
extraContext.put(ActionContext.APPLICATION, applicationMap);
extraContext.put(ActionContext.LOCALE, (locale == null) ? request.getLocale() : locale);
extraContext.put(HTTP_REQUEST, request);
extraContext.put(HTTP_RESPONSE, response);
extraContext.put(SERVLET_CONFIG, servletConfig);
extraContext.put(ComponentInterceptor.COMPONENT_MANAGER, request.getAttribute(ComponentManager.COMPONENT_MANAGER_KEY));
// helpers to get access to request/session/application scope
extraContext.put("request", requestMap);
extraContext.put("session", sessionMap);
extraContext.put("application", applicationMap);
extraContext.put("parameters", parameterMap);
AttributeMap attrMap = new AttributeMap(extraContext);
extraContext.put("attr", attrMap);
return extraContext;
}
众所周知,不仅WebWork能使用XWork,而且还可以有类似的FtpWork,MailWork,JMSWork能够替代WebWork来使用Xwork,为什么能够这样,因为Xwork的使用方式.在上面的serviceAction方法中:
ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy(namespace, actionName, extraContext);
request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY, proxy.getInvocation().getStack());
proxy.execute();
这些代码就是这种方式的体现:动作代理-动作调用-具体的动作.为使客户代码不绑定到特定代理,XWork使用了一个工厂模式,提供工厂类 ActionProxyFactory.
方法createActionProxy实际调用导致一个ActionProxy接口的缺省实现DefaultActionProxy被实例化:
它的构造器是:
protected ActionInvocation invocation;
protected DefaultActionProxy(String namespace, String actionName, Map extraContext, boolean executeResult) throws Exception {
if (LOG.isDebugEnabled()) {
LOG.debug("Creating an DefaultActionProxy for namespace " + namespace + " and action name " + actionName);
}
this.actionName = actionName;
this.namespace = namespace;
this.executeResult = executeResult;
this.extraContext = extraContext;
config = ConfigurationManager.getConfiguration().getRuntimeConfiguration().getActionConfig(namespace, actionName);
if (config == null) {
String message;
throw new ConfigurationException(message);
}
prepare();
}
protected void prepare() throws Exception {
//创建ActionInvocation 实例化的仍然是由代理工厂类的工厂方法进行,实例化该接口的缺省 实现DefaultActionInvocation.
invocation = ActionProxyFactory.getFactory().createActionInvocation(this, extraContext);
}
下面是proxy.execute()的细节:
public String execute() throws Exception {
ActionContext nestedContext = ActionContext.getContext();
ActionContext.setContext(invocation.getInvocationContext());
String retCode = null;
try {
retCode = invocation.invoke();
} finally {
ActionContext.setContext(nestedContext);
}
return retCode;
}
最后,看看这个类: DefaultActionInvocation类的invoke方法是如何处理extraContext,先看初始化过程,从构造器开始:
protected DefaultActionInvocation(ActionProxy proxy, Map extraContext, boolean pushAction) throws Exception {
this.proxy = proxy;
this.extraContext = extraContext;
this.pushAction = pushAction;
init();
}
方法init定义,把extraContext转换到invocationContext中,并获得拦截器清单.
private void init() throws Exception {
Map contextMap = createContextMap();
createAction();
if (pushAction) {
stack.push(action);
}
invocationContext = new ActionContext(contextMap);
invocationContext.setName(proxy.getActionName());
List interceptorList = new ArrayList(proxy.getConfig().getInterceptors());
interceptors = interceptorList.iterator();
}
在下面这个方法中获得动作上下文contextMap,并把extraContext包含其中.
protected Map createContextMap() {
Map contextMap;
if ((extraContext!=null)&& (extraContext.containsKey(ActionContext.VALUE_STACK))) {
// In case the ValueStack was passed in
stack = (OgnlValueStack) 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 = new OgnlValueStack();
// 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);
return contextMap;
}
protected void createAction() {
// load action
try {
action = ObjectFactory.getObjectFactory().buildAction(proxy.getConfig());
} catch (InstantiationException e) {
throw new XworkException("Unable to intantiate Action!", e);
} catch (IllegalAccessException e) {
throw new XworkException("Illegal access to constructor, is it public?", e);
} catch (ClassCastException e) {
throw new XworkException("Action class " + proxy.getConfig().getClassName() + " does not implement " + Action.class.getName(), e);
} 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);
}
}
初始化结束了, 接下来看看invocation.invoke()到底做了什么:
public String invoke() throws Exception {
if (executed) {
throw new IllegalStateException("Action has already executed");
}
if (interceptors.hasNext()) {
Interceptor interceptor = (Interceptor) interceptors.next();
在这个类中,并没有调用getResult(),因为这个方法是由某些拦截器调用的,例如: ExecuteAndWaitInterceptor, TokenSessionStoreInterceptor,如果自己需要,也可以创建自己的拦截器,并在其中访问它.
通过Interceptor接口中的String intercept(ActionInvocation invocation) throws Exception可以看到, 参数invocation已经携带了所有能处理的数据了.
resultCode = interceptor.intercept(this);
} else {
resultCode = invokeAction(getAction(), proxy.getConfig());
}
if (!executed) {
if (preResultListeners != null) {
for (Iterator iterator = preResultListeners.iterator();
iterator.hasNext();) {
PreResultListener listener = (PreResultListener) iterator.next();
listener.beforeResult(this, resultCode);
}
}
now execute the result, if we're supposed to,如果不期望执行Result,那么在代理中设置proxy.getExecuteResult(false)即可,有时候是需要这样的,如TokenSessionStoreInterceptor;
if (proxy.getExecuteResult()) {
executeResult();
}
executed = true;
}
return resultCode;
}
在这个方法中,调用了Result接口的方法execute(ActionInvocation),当该方法返回时一个请求响应回合就完成了.
private void executeResult() throws Exception {
result = createResult();
if (result != null) {
result.execute(this);
} else if (!Action.NONE.equals(resultCode)) {
LOG.warn("No result defined for action " + getAction().getClass().getName() + " and result " + getResultCode());
}
}
public Result createResult() throws Exception {
Map results = proxy.getConfig().getResults();
ResultConfig resultConfig = (ResultConfig) results.get(resultCode);
Result newResult = null;
if (resultConfig != null) {
try {
newResult = ObjectFactory.getObjectFactory().buildResult(resultConfig);
} catch (Exception e) {
LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);
throw e;
}
}
return newResult;
}
public Result getResult() throws Exception {
Result returnResult = result;
// If we've chained to other Actions, we need to find the last result
while (returnResult instanceof ActionChainResult) {
ActionProxy aProxy = ((ActionChainResult) returnResult).getProxy();
if (aProxy != null) {
Result proxyResult = aProxy.getInvocation().getResult();
if ((proxyResult != null) && (aProxy.getExecuteResult())) {
returnResult = proxyResult;
} else {
break;
}
} else {
break;
}
}
return returnResult;
}
protected String invokeAction(Action action, ActionConfig actionConfig) throws Exception {
if (proxy.getConfig().getMethodName() == null) {
return getAction().execute();
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Executing action method = " + actionConfig.getMethodName());
}
try {
Method method = actionConfig.getMethod(action.getClass());
if (action instanceof Proxy) {
try {
return (String) Proxy.getInvocationHandler(action).invoke(action, method, new Object[0]);
} catch (Throwable throwable) {
throwable.printStackTrace();
throw new Exception("Error invoking on proxy: " + throwable.getMessage());
}
} else {
return (String) method.invoke(action, new Object[0]);
}
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("Method '" + actionConfig.getMethodName() + "()' is not defined in action '" + getAction().getClass() + "'");
} catch (InvocationTargetException e) {
// We try to return the source exception.
Throwable t = e.getTargetException();
if (t instanceof Exception) {
throw (Exception) t;
} else {
throw e;
}
}
}
}
}