上篇讲了Tomcat实例化一个单例的ActionServlet,根据web.xml配置文件做好相应的初始化工作。这时客户端产生一个.do结尾的request请求,采用get/post方式提交之后,不论哪种方式提交,都会进入到process核心方法中。
ActionServelt
doGet()和doPost()方法
public void <strong>doGet</strong>(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
<strong>process</strong>(request, response);
}
public void <strong>doPost</strong>(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
<strong>process</strong>(request, response);
}
process方法
protected void process(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
ModuleUtils.getInstance().selectModule(request, getServletContext());
ModuleConfig config = getModuleConfig(request);
RequestProcessor processor = getProcessorForModule(config);
if (processor == null) {
processor = getRequestProcessor(config);
}
processor.process(request, response);
}
这个方法主要有两个作用:
首先调用org.apache.struts.util.ModuleUtils类的selectModule()方法,这个方法根据请求对象和servletContext选择负责处理当前请求所属的模块,然后把与该模块相关的ModuleConfig和MessageResources对象存储倒request范围中,这使得框架的其余组件可以方便地从request范围中读取这些对象,从而获取应用配置信息和消息资源。
其次, 实例化RequestProcessor类,然后调用RequestProcessor类的process()方法,来完成十几的预处理请求操作。
RequestProcessor
process()方法
public void process(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// Wrap multipart requests with a special wrapper
request = processMultipart(request);
// Identify the path component we will use to select a mapping
String path = processPath(request, response);
if (path == null) {
return;
}
if (log.isDebugEnabled()) {
log.debug("Processing a '" + request.getMethod() +
"' for path '" + path + "'");
}
// Select a Locale for the current user if requested
processLocale(request, response);
// Set the content type and no-caching headers if requested
processContent(request, response);
processNoCache(request, response);
// General purpose preprocessing hook
if (!processPreprocess(request, response)) {
return;
}
this.processCachedMessages(request, response);
// Identify the mapping for this request
ActionMapping mapping = processMapping(request, response, path);
if (mapping == null) {
return;
}
// Check for any role required to perform this action
if (!processRoles(request, response, mapping)) {
return;
}
// Process any ActionForm bean related to this request
ActionForm form = processActionForm(request, response, mapping);
processPopulate(request, response, form, mapping);
// Validate any fields of the ActionForm bean, if applicable
try {
if (!processValidate(request, response, form, mapping)) {
return;
}
} catch (InvalidCancelException e) {
ActionForward forward = processException(request, response, e, form, mapping);
processForwardConfig(request, response, forward);
return;
} catch (IOException e) {
throw e;
} catch (ServletException e) {
throw e;
}
// Process a forward or include specified by this mapping
if (!processForward(request, response, mapping)) {
return;
}
if (!processInclude(request, response, mapping)) {
return;
}
// Create or acquire the Action instance to process this request
Action action = processActionCreate(request, response, mapping);
if (action == null) {
return;
}
// Call the Action instance itself
ActionForward forward =
processActionPerform(request, response,
action, form, mapping);
// Process the returned ActionForward instance
processForwardConfig(request, response, forward);
}
按照process()方法的执行流程,
一、processMultipart();
1、首先判断是否为post方式,如果不是post方式,则肯定不是上传请求,则直接返回request对象
if (!"POST".equalsIgnoreCase(request.getMethod())) {
return (request);
}
2、获取request对象的ContentType,如果ContentType为multipart/form-datade 话则 new 一个 MultipartRequestWrapper 对象返回。否则直接返回request。
String contentType = request.getContentType(); if ((contentType != null)
&& contentType.startsWith("multipart/form-data")) {
return (new MultipartRequestWrapper(request));
} else {
return (request);
}
这里简答的来说,是用来判断是否有上传功能的需求,如果有上传功能的需求,那么必须把form的contentType改为multipart/form-data,而这个方法就是来解析并打包成一个实现HttpServletRequest的包,为上传文件服务的。
二、 processPath()
String path = processPath(request, response);
if (path == null) {
return;
}
if (log.isDebugEnabled()) {
log.debug("Processing a '" + request.getMethod() +
"' for path '" + path + "'");
}
通过调用processPath方法,获得访问的具体action的名字
三、processLocale()
protected void processLocale(HttpServletRequest request,HttpServletResponse response) {
// Are we configured to select the Locale automatically?
if (!moduleConfig.getControllerConfig().getLocale()) {
return;
}
// Has a Locale already been selected?
HttpSession session = request.getSession();
if (session.getAttribute(Globals.LOCALE_KEY) != null) {
return;
}
// Use the Locale returned by the servlet container (if any)
Locale locale = request.getLocale();
if (locale != null) {
if (log.isDebugEnabled()) {
log.debug(" Setting user locale '" + locale + "'");
}
session.setAttribute(Globals.LOCALE_KEY, locale);
}
}
是本地服务还是国际化服务,这是根据浏览器的设置的(确切的说是根据操作系统设置的)
四、processContent();processNoCache();
其中processContent用来确定内容类型和编码类型
processNoCache根据配置文件中的配置属性来确定某些内容是否进行缓存
五、 processMapping()
根据request,response和action的名字去Struts.config.xml中获取对应的actionmapping,也就是将我们配置好的request要请求的action放入到ActionMapping对象中去。
1、首先去配置文件里找相应的配置信息
// Is there a mapping for this path?
ActionMapping mapping = (ActionMapping) moduleConfig.findActionConfig(path);
2、如果有配置则把它放入request,并返回。
// If a mapping is found, put it in the request and return it
if (mapping != null) {
request.setAttribute(Globals.MAPPING_KEY, mapping);
return (mapping);
}
3、找到“未知的映射路径(如果有的话)”。同样找到了就放到request里并返回他。
// Locate the mapping for unknown paths (if any)
ActionConfig[] configs = moduleConfig.findActionConfigs();
for (int i = 0; i < configs.length; i++) {
if (configs[i].getUnknown()) {
mapping = (ActionMapping) configs[i];
request.setAttribute(Globals.MAPPING_KEY, mapping);
return (mapping);
}
}
4、如果还是没有找到mapping信息则发送错误消息,并返回null
// No mapping can be found to process this request
String msg = getInternal().getMessage("processInvalid");
log.error(msg + " " + path);
response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);
return null;
此时,ActionMapping元素包含了如Action类的名称及在请求中用到的ActionForm的信息,另外还有配置在当前ActionMapping的里的ActionForwards信息。
六、processActionForm()
protected ActionForm processActionForm(HttpServletRequest request,
HttpServletResponse response,
ActionMapping mapping) {
// Create (if necessary) a form bean to use
ActionForm instance = RequestUtils.createActionForm
(request, mapping, moduleConfig, servlet);
if (instance == null) {
return (null);
}
// Store the new instance in the appropriate scope
if (log.isDebugEnabled()) {
log.debug(" Storing ActionForm bean instance in scope '" +
mapping.getScope() + "' under attribute key '" +
mapping.getAttribute() + "'");
}
if ("request".equals(mapping.getScope())) {
request.setAttribute(mapping.getAttribute(), instance);
} else {
HttpSession session = request.getSession();
session.setAttribute(mapping.getAttribute(), instance);
}
return (instance);
}
查看mapping里是否配置name属性或attribute属性来指定ActionForm,如果配置了ActionForm就到session或是Request中进行查找,查到后进行返回;如果没有找到,根据ActionForm的路径进行创建并放入到scope域中。
七、processPopulate()
protected void processPopulate(HttpServletRequest request,
HttpServletResponse response,
ActionForm form,
ActionMapping mapping)
throws ServletException {
if (form == null) {
return;
}
// Populate the bean properties of this ActionForm instance
if (log.isDebugEnabled()) {
log.debug(" Populating bean properties from this request");
}
form.setServlet(this.servlet);
form.reset(mapping, request);
if (mapping.getMultipartClass() != null) {
request.setAttribute(Globals.MULTIPART_KEY,
mapping.getMultipartClass());
}
RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(),
request);
// Set the cancellation request attribute if appropriate
if ((request.getParameter(Constants.CANCEL_PROPERTY) != null) ||
(request.getParameter(Constants.CANCEL_PROPERTY_X) != null)) {
request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
}
}
这个方法主要是用来为ActionForm 填充数据,执行ActionForm的reset重置方法,之后获取到表单所有输入域的名称,根据request.getParameter获取输入域名称所对应的值,然后将输入域名称和值以键值对形式放入到map中,之后调用一个比较重要的第三方组件BeanUtils来实现属性的转换和复制到具体的ActionForm中
八、processValidate()
验证表单数据合法性,这个可用可不用的,如果使用了Struts的验证,这里会自动的执行该方法进行验证。
九、processForward()
处理mapping指定的forward 和 include,Struts检查<action>元素的forward和include属性的值,假如有配置,则把forward和include 请求放在配置的页面内,之后转发到具体的ActionServlet中进行具体业务逻辑的处理。
小结
至此,RequestProcessor的核心方法process执行完毕。之后会到具体的Action中执行业务相关的操作。
纵观整个执行流程,其主要的方法无外乎两个,一个是AcionServlet中的process方法,另一个是RequestProcessor的process()方法,至于其他的截取请求路径,填充actionForm,国际化等方法也都被用于外观模式封装在了process方法中。