要使用Sruts2,需要注册一个过滤器:StrutsPrepareAndExecuteFilter。FilterDispatcher自2.1.3以后已过时。
注:以下的Sruts都指Sruts2。
既然StrutsPrepareAndExecuteFilter是Struts的执行入口,那就从它开始分析。
StrutsPrepareAndExecuteFilter类有三个成员变量:
protected PrepareOperations prepare;
protected ExecuteOperations execute;
protected List<Pattern> excludedPatterns = null;
三个成员变量都会在初始化方法(只会执行一次)中实例化:
public void init(FilterConfig filterConfig) throws ServletException {
InitOperations init = new InitOperations();
try {
FilterHostConfig config = new FilterHostConfig(filterConfig);
init.initLogging(config);
Dispatcher dispatcher = init.initDispatcher(config);
init.initStaticContentLoader(config, dispatcher);
prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
postInit(dispatcher, filterConfig);
} finally {
init.cleanup();
}
}
PrepareOperations类主要是为Action的执行做准备,通过它的findActionMapping()方法可以返回一个ActionMapping对象。
ExecuteOperations类通过ActionMapping来执行Action。
初始化方法中会产生一个Dispatcher对象,并且将其传递给PrepareOperations和ExecuteOperations。该对象会在后面用到。
excludedPatterns则是URL过滤,被过滤掉的URL不会被Struts执行。
接下来就是过滤器的执行方法doFilter()(每一次请求都会执行):
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
prepare.setEncodingAndLocale(request, response);
prepare.createActionContext(request, response);
prepare.assignDispatcherToThread();
if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
chain.doFilter(request, response);
} else {
request = prepare.wrapRequest(request);
ActionMapping mapping = prepare.findActionMapping(request, response, true);
if (mapping == null) {
boolean handled = execute.executeStaticResourceRequest(request, response);
if (!handled) {
chain.doFilter(request, response);
}
} else {
execute.executeAction(request, response, mapping);
}
}
} finally {
prepare.cleanupRequest(request);
}
}
先判断URL是否被过滤:如果是,则跳到下一个过滤器;如果不是,则通过prepare找到ActionMapping。如果没有找到ActionMapping,则把当前的URL当作静态资源处理;如果找到ActionMapping,则执行Action。
接下来执行execute.executeAction()方法:
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
dispatcher.serviceAction(request, response, servletContext, mapping);
}
该方法调用了dispatcher.serviceAction()方法:
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) {
if (handleException || devMode) {
sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
} else {
throw new ServletException(e);
}
} finally {
UtilTimerStack.pop(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();
}
这段代码根据传入的ActionMapping,创建了一个ActionProxy,然后调用了proxy.execute()方法。
ActionProxy的创建使用了工厂模式,工厂接口是ActionProxyFactory,Struts提供的实现类是StrutsActionProxyFactory。
这里调用的工厂方法是StrutsActionProxyFactory父类DefaultActionProxyFactory的方法:
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);
}
在创建ActionProxy对象之前先创建了ActionInvocation对象。接着回调了子类StrutsActionProxyFactory的工厂方法:
public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
StrutsActionProxy proxy = new StrutsActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
container.inject(proxy);
proxy.prepare();
return proxy;
}
可以看到ActionProxy的实现类是StrutsActionProxy。在创建Proxy的时候传入了先前创建好的Invocation对象。在创建ActionProxy实例后,调用proxy.prepare()方法做一些准备工作:
protected void prepare() {
super.prepare();
}
调用了父类的prepare()方法,StrutsActionProxy的父类是DefaultActionProxy。看一下DefaultActionProxy.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);
}
}
在这里调用了invocation.init()方法初始化invocation(invocation是在创建proxy的时候传给proxy的)。invocation.init()方法:
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();
}
在这里把proxy传给了invocation,可见proxy与invocation是一对一的关系,它们相互引用。这个方法干了两件重要的事:创建Action实例和获取Action的拦截器。
1.创建Action实例createAction():
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);
}
}
从上面的代码可以看到,struts的Action实例都是由ObjectFactory创建的,ObjectFactory是可以配置的,struts集成spring就是在这个基础上完成的。
2.获取拦截器:
List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
interceptors = interceptorList.iterator();
返回的拦截器是放在迭代器里面的,这对struts实现拦截器机制至关重要,后面会看到。
至此,ActionPorxy和ActionInvocation创建完毕!现在回到dispatcher.serviceAction()方法中,在dispatcher.serviceAction()方法中调用了proxy.execute()方法(proxy的实现类是StrutsActionProxy)开始正式执行Action:
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);
}
}
这里调用了ActionInvocation的invoke()方法(实现类是DefaultActionInvocation),invoke方法做了下面几件事。
1.调用拦截器:
if (interceptors.hasNext()) {
final InterceptorMapping interceptor = interceptors.next();
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
}
finally {
UtilTimerStack.pop(interceptorMsg);
}
} else {
resultCode = invokeActionOnly();
}
我们一般会在拦截器里面再次调用ActionInvocation.invoke()方法,所以ActionInvocation.invoke()会被递归调用,直到所有的拦截器都被调用(但是此时所有的拦截器都还没有执行完毕!)。
2.当迭代器迭代完毕后(所有的拦截器都被调用),会调用真正执行Action的方法invokeActionOnly():
public String invokeActionOnly() throws Exception {
return invokeAction(getAction(), proxy.getConfig());
}
invokeAction(Object action, ActionConfig actionConfig):
UtilTimerStack.push(timerKey);
boolean methodCalled = false;
Object methodResult = null;
Method method = null;
try {
method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);
} catch (NoSuchMethodException e) {
// hmm -- OK, try doXxx instead
try {
String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
method = getAction().getClass().getMethod(altMethodName, EMPTY_CLASS_ARRAY);
} catch (NoSuchMethodException e1) {
// well, give the unknown handler a shot
if (unknownHandlerManager.hasUnknownHandlers()) {
try {
methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
methodCalled = true;
} catch (NoSuchMethodException e2) {
// throw the original one
throw e;
}
} else {
throw e;
}
}
}
if (!methodCalled) {
methodResult = method.invoke(action, EMPTY_OBJECT_ARRAY);//执行Action方法,也就是我们在Action中定义的并且在配置文件中配置的方法。
}
return saveResult(actionConfig, methodResult);
保存返回的结果saveResult(ActionConfig actionConfig, Object 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;
}
}
很显然,我们定义的action返回的值常常是String或者void类型的。
3.处理结果:
// 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;
}
executeResult():
private void executeResult() throws Exception {
result = createResult();
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);
}
}
在该方法中会先创建一个结果result对象createResult():
public Result createResult() throws Exception {
if (explicitResult != null) {
Result ret = explicitResult;
explicitResult = null;
return ret;
}
ActionConfig config = proxy.getConfig();
Map<String, ResultConfig> results = config.getResults();
ResultConfig resultConfig = null;
try {
resultConfig = results.get(resultCode);
} catch (NullPointerException e) {
// swallow
}
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 {
return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
} catch (Exception e) {
LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);
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;
}
通过前面的分析,explicitResult并没有设置,所以会执行下面的代码。由下面的代码可知,result对象也是由objectFactory创建的。
result是我们在配置文件通过result-type配置的,所有的result都实现了接口com.opensymphony.xwork2.Result。默认的result-type是dispatcher,实现类是ServletDispatcherResult。
创建完result对象后,会执行result.execute()方法。如果配置的result-type是dispatcher(默认的),则ServletDispatcherResult类的doExecute()方法会被调用:
public void doExecute(String finalLocation, ActionInvocation invocation) throws Exception {
if (LOG.isDebugEnabled()) {
LOG.debug("Forwarding to location " + finalLocation);
}
PageContext pageContext = ServletActionContext.getPageContext();
if (pageContext != null) {
pageContext.include(finalLocation);
} else {
HttpServletRequest request = ServletActionContext.getRequest();
HttpServletResponse response = ServletActionContext.getResponse();
RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation);
//add parameters passed on the location to #parameters
// see WW-2120
if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf("?") > 0) {
String queryString = finalLocation.substring(finalLocation.indexOf("?") + 1);
Map<String, Object> parameters = getParameters(invocation);
Map<String, Object> queryParams = urlHelper.parseQueryString(queryString, true);
if (queryParams != null && !queryParams.isEmpty())
parameters.putAll(queryParams);
}
// if the view doesn't exist, let's do a 404
if (dispatcher == null) {
response.sendError(404, "result '" + finalLocation + "' not found");
return;
}
//if we are inside an action tag, we always need to do an include
Boolean insideActionTag = (Boolean) ObjectUtils.defaultIfNull(request.getAttribute(StrutsStatics.STRUTS_ACTION_TAG_INVOCATION), Boolean.FALSE);
// If we're included, then include the view
// Otherwise do forward
// This allow the page to, for example, set content type
if (!insideActionTag && !response.isCommitted() && (request.getAttribute("javax.servlet.include.servlet_path") == null)) {
request.setAttribute("struts.view_uri", finalLocation);
request.setAttribute("struts.request_uri", request.getRequestURI());
dispatcher.forward(request, response);
} else {
dispatcher.include(request, response);
}
}
}
实际上就是调用了javax.servlet.RequestDispatcher.forward()方法。
由于是递归调用,此时拦截器还没有调用完毕。结果处理完后,会以相反的顺序调用拦截器。
至此,一个Action请求大致执行完毕!我们可以看到,执行Action的核心方法是ActionInvocation.invoke()。
最后附上struts2的原理图: