上篇讲了Tomcat实例化一个单例的ActionServlet。依据web.xml配置文件做好对应的初始化工作。
这时client产生一个.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方法中。