Struts2的Action调用还是要从FilterDispatcher的#doFilter()方法开始说起。
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
ServletContext servletContext = getServletContext();
String timerKey = "FilterDispatcher_doFilter: ";
try {
UtilTimerStack.push(timerKey);
/* (1) 对请求进行包装 */
request = prepareDispatcherAndWrapRequest(request, response);
ActionMapping mapping;
try {
/* (2) 获得Action Mapping */
mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());
} catch (Exception ex) {
LOG.error("error getting ActionMapping", ex);
dispatcher.sendError(request, response, servletContext,
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
return;
}
if (mapping == null) {// 当Mapping为空时,检查是否访问的为静态资源
String resourcePath = RequestUtils.getServletPath(request);
if ("".equals(resourcePath) && null != request.getPathInfo()) {
resourcePath = request.getPathInfo();
}
if (serveStatic && resourcePath.startsWith("/struts")) {
String name = resourcePath.substring("/struts".length());
findStaticResource(name, request, response);
} else {
// this is a normal request, let it pass through
chain.doFilter(request, response);
}
// The framework did its job here
return;
}
/* (3) 调用被请求的Action的执行方法(例如execute)和拦截器等 */
dispatcher.serviceAction(request, response, servletContext, mapping);
} finally {
try {
ActionContextCleanUp.cleanUp(req);
} finally {
UtilTimerStack.pop(timerKey);
}
}
}
主要有三个部分需要重点说明:
(1) 对请求进行包装;
(2) 获得Action Mapping;
(3) 调用被请求的Action的执行方法(例如execute)和拦截器等。
下面来一一详细说明:
1. 对请求进行包装:request = prepareDispatcherAndWrapRequest(request, response);
下面来看一下#prepareDispatcherAndWrapRequest()的源代码:
protected HttpServletRequest prepareDispatcherAndWrapRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException {
Dispatcher du = Dispatcher.getInstance();// (1)
if (du == null) {
Dispatcher.setInstance(dispatcher);
dispatcher.prepare(request, response);// (2)
} else {
dispatcher = du;
}
try {
request = dispatcher.wrapRequest(request, getServletContext());// (3)
} catch (IOException e) {
String message = "Could not wrap servlet request with MultipartRequestWrapper!";
LOG.error(message, e);
throw new ServletException(message, e);
}
return request;
}
(1) 这个方法先是获取一个Dispatcher的实例,获取是通过ThreadLocal的方式,可见Struts2框架为每一个线程都提供了一个Dispatcher对象,所以在编写Action的时候不需要考虑多线程的问题了。
(2) 如果是第一次访问FilterDispatcher,那么du应该为null,这时要调用Dispatcher的#prepare()方法:
public void prepare(HttpServletRequest request, HttpServletResponse response) {
String encoding = null;
if (defaultEncoding != null) {
encoding = defaultEncoding;
}
Locale locale = null;
if (defaultLocale != null) {
locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
}
if (encoding != null) {
try {
request.setCharacterEncoding(encoding);
} catch (Exception e) {
LOG.error("Error setting character encoding to '" + encoding + "' - ignoring.", e);
}
}
if (locale != null) {
response.setLocale(locale);
}
if (paramsWorkaroundEnabled) {
request.getParameter("foo");
}
}
通过源代码可以看出,此方法主要为设置编码和Locale。
(3) 第三步是对请求的包装,request = dispatcher.wrapRequest(request, getServletContext()); 源代码如下:
public HttpServletRequest wrapRequest(HttpServletRequest request, ServletContext servletContext) throws IOException {
if (request instanceof StrutsRequestWrapper) {// 判断request是否是StrutsRequestWrapper的对象,保证对request只包装一次。
return request;
}
String content_type = request.getContentType();
if (content_type != null && content_type.indexOf("multipart/form-data") != -1) {
MultiPartRequest multi = getContainer().getInstance(MultiPartRequest.class);
request = new MultiPartRequestWrapper(multi, request, getSaveDir(servletContext));
} else {
request = new StrutsRequestWrapper(request);
}
return request;
}
判断Content-Type是否是multipart/form-data,如果是的话返回一个MultiPartRequestWrapper的对象处理文件上传,否则返回StrutsRequestWrapper的对象处理普通请求。