本篇详细介绍struts在初始化之后是如何处理一个请求,并返回数据的。这里最核心的类是 RequestProcessor以及RequestUtils。RequestProcessor类通过RequestDispatcher实现页面的跳转, 而RequestProcessor负责处理request中传来的请求信息,存放到FormBeanConfig中,以及对要跳转的 url进行处理。 struts 在初始化完成之后,会根据请求调用doGet(...)或者doPost(...)方法,这两个方法直接 调用process(request, response)方法。process(...)方法首先判断当前的request属于 哪一个ModuleConfig,然后生成与这个ModuleConifg相对应的RequestProcessor,最后调用这个 RequestProcessor的process(...)方法,执行request的请求。 一、RequestUtils.selectModule(String prefix, HttpServletRequest,ServletContext)方法: 这个方法,根据prefix,从ServletContext中选择相应的ModuleConfig,然后把这个ModuleConfig 保存到request中。ServletContext对应的key值为Globals.MODULE_KEY + prefix,保存到request 中使用的key值为Globals.MODULE_KEY。如果在ServletContext中不存在这样的一个ModuleConfig, 那么调用request.removeAttribute(Globals.MODULE_KEY)方法。然后以同样的方法查找、保存 MessageResources对象。当prefix为空时,会调用下面的方法选择ModuleConfig。 二、RequestUtils.selectModule(HttpServletRequest, ServletContext) 这个方发首先使用getModuleName(HttpServletRequest, ServletContext)获取相应的path,然后 通过调用getModuleName(String matchPath, ServletContext)获取相应的prefix。以这prefix为 参数调用(一)中的selectModule(...)选择ModuleConfig。 获取path的过程为:首先从request中查找名称为 INCLUDE_SERVLET_PATH(javax.servlet.include.servlet_path)的属性,如果为空,就调用 request.getServletPath()方法获取servletPath。 获取prefix的过程为:它首先调用getModulePrefixes(ServletContext),获取所有已存在的 module前缀。 然后通过分析上面获取的path来判断当前的url属于哪一个module,方法是截取 path中最后一个"/"字符前面的字符串,然后与有上面方法中获取的prefixes[]逐个对比,如果 prefixes[]中存在这样的一个值,则返回这个截取的字符串,否则继续截取path最后面的"/"前面 的字符串,然后对比。如果始终找不到,则返回""。 getModulePrefixes(ServletContext)的执行过程是:首先通过 context.getAttribute(PREFIXES_KEY)查找是否存在这样的一个保存所有的prefix的string array, 如果存在,就说明已经解析过一次了,就直接返回这个string array,否则遍历ServletContext中 所有的attribute name,查找以MODULE_KEY(org.apache.struts.action.MODULE)开头的 atrribute name,然后截取这个atrribute name中MODULE_KEY后面的字符串,作为一个prefix. 最 后把通过这个这个方式获取的所有的prefix作为一个ArrayList存储到ServletContext中,属性key 值为PREFIXES_KEY(org.apache.struts.util.PREFIXES),这样下次再次查找的时候就可以直接从这 个attribute中获取了。 三、getModuleConfig(HttpServletRequest) 这个方法就是获取上面的selectModule(...)方法所得到的ModuleConfig。如果找不到这样的 ModuleConfig,那么就把ServletContext中缺省的ModuleConfig返回(调用 getServletContext().getAttribute(Globals.MODULE_KEY)) 四、getRequestProcessor(ModuleConfig config) 这个方法从根据ModuleConfig的prefix作为key,从ServletContext中获取RequestProcessor。这 个key值为Globals.REQUEST_PROCESSOR_KEY + config.getPrefix()。 如果ServletContext中不存在这样的一个RequestProcessor,那么就生成一个新的 RequestProcessor的实例,完成初始化(保存ActionServlet以及ModuleConfig到这个新的实例中)后 将其保存到ServletContext中。 五、RequestProcessor.process(HttpServletRequest, HttpServletResponse) 这是真正执行HttpServletRequst请求的方法。 这个方法首先判断但前的HttpServletRequest是否是Multipart类型的request,也就是说当前 的request是否有字段是"file"类型。这样做的原因是这种类型的request中getParameter()等 相类似的方法都是无法执行的,所以要区分对待。如果是Multipart类型的,那么把这个request包 装成MultipartRequestWrapper类。 MultipartRequestWrapper 与 HttpServletRequest不同的就是重新实现了 setParameter(String name, String value),getParameter(String name),getParameterNames() 以及getParametervalues(String name)方法。 这些方法的思想是所有的变量名、值对都保存到一个HashMap里:key为变量名,value为变量值,但 是注意value都是String[]格式的。当有一个新的值加入的时候,通过setParameter(...) 方法,把值加到数组的最后。在setParameter(...)中有一个比较少见的保存值的方法,记录如下: public void setParameter(String name, String value) { String[] mvalue = (String[]) parameters.get(name); if (mvalue == null) { mvalue = new String[0]; } String[] newvalue = new String[mvalue.length + 1]; System.arraycopy(mvalue, 0, newvalue, 0, mvalue.length); newvalue[mvalue.length] = value; parameters.put(name, newvalue); } 然后是调用processPath(HttpServletRequest, HttpServletResponse)获取地址,以这个地址作为 从ModuleConfig获取ActionMapping的Key。这里首先查询 request.getAttribute(INCLUDE_PATH_INFO)中是否有这样的一个值,如果为空就调用 request.getPathInfo()方法获取path。如果这样还是获取不了,就从 request.getAttribute(INCLUDE_SERVLET_PATH)方法中获取path,找不到就使用 request.getServletPath()得到的path进行分析。分析过程如下: 然后如果这个path不是属于当前的ModuleConfig的话,直接返回null。截取prefix后面的字符串, 如果这个字符串以.XX结尾,那么就截取"."前面的字符,比如 http://localhost:8080/servlet/where/go.do。where为module的名称(prefix),那么我们获取的 path值为/go。 然后是通过processLocale(HttpServletRequest ,HttpServletResponse)为当前用户设定一个Local 对象,它查找的顺序是:moduleConfig.getControllerConfig().getLocale(), session.getAttribute(Globals.LOCALE_KEY),request.getLocale()。你可以在config XML文件 中通过<controller><set-property..../></controller>执行相关的定义。 调用processContent(HttpServletRequest, HttpServletResponse)为response设定contentType。 这个contentType是从moduleConfig.getControllerConfig().getContentType()获取的。你可以 在config XML文件中通过<controller><set-property..../></controller>执行相关的定义。 通过processNoCache(HttpServletRequest, HttpServletResponse)方法设置response的缓存。 你可以在config XML文件中通过<controller><set-property..../></controller>执行相关的定义。 processPreprocess(HttpServletRequest, HttpServletResponse)预处理request,这个方法是预 留的,用户可以根据自己的需要加一些预处理的程序。 通过processMapping(HttpServletRequest, HttpServletResponse, String path)以 processPath(...)的返回值为key从ModuleConfig中查找ActionMapping 对象。如果找到了,那么保 存这个Mapping到request中,key值为Globals.MAPPING_KEY。如果不存在,那么遍历ModuleConfig 中所有的ActionMapping,查找哪一个是缺省的ActionMapping。然后把它保存到request中,key值 为Globals.MAPPING_KEY。 通过processRoles(HttpServletRequest,HttpServletResponse,ActionMapping)检查当前用户是 否有权限执行这个请求。如果request.isUserInRole(roles[i])返回true,则代表有。 通过processActionForm(HttpServletRequest, HttpServletResponse, ActionMapping)生成一个 ActionForm,然后根据ActionMapping所属的scope,保存到request或者session中。key值为这个 ActionMapping中的attribute属性。ActionForm是通过RequestUtils.createActionForm(...)方法 获取的,在下一篇将会对这个方法进行详细的说明。这里只说明ActionForm与FormBeanConfig以及 FormPropertyConfig之间的区别。每个FormPropertyConfig代表Form表单的一个字段,表单中的所 有FormPropertyConfig以一个HashMap保存到FormBeanConfig中。而FormBeanConfig是在 Actionservet初始化的时候生成的: <form-beans> <form-bean name="一个名称,作为action选择的key" type="实际对应的ActionForm类"/> <form-beans> 名称用来在MoudleConfig中的formBeans HashMap中查找相应的FormBeanConfig,而FormBeanConfig 中有一个type,它保存了上面XML文件中定义的值,用来生成ActionForm。 通过processPopulate(HttpServletRequest, HttpServletResponse, ActionForm, ActionMapping)方法初始化ActionForm 以及 FormBean,这个方法首先会设定这个ActionForm所属 于的ActionServlet,然后对这个ActionForm进行初始化。判断当前的reqest是否Multipart类型, 如果是就把相应的MultipartClass类全名保存到request中,key值为Globals.MULTIPART_KEY。调 用RequestUtils.populate(...)对request中的参数进行处理,并保存到相应的FormBean中。在下 一篇将会对这个方法进行详细的说明。最后根据request的parameter判断当前的请求是否是被取 消,然后把相关信息保存到request中: if ((request.getParameter(Constants.CANCEL_PROPERTY) != null) || (request.getParameter(Constants.CANCEL_PROPERTY_X) != null)) { request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE); } 六、下面说明这个request是如何真正被处理的,下面的方法都是在RequestProcessor中定义。 通过processValidate(HttpServletRequest, HttpServletResponse, ActionForm, ActionMapping)判断request中的parameter是否合法。如果合法就返回true,否则取消这次请求, 并且返回到指定的输入页面。它判断合法的过程是:如果这次的请求被取消,那么直接返回true; 如果没有要求对request中的parameter进行合法性检验,也直接返回true;最后通过调用 form.validate(mapping, request)执行检验,如果返回的ActionErrors为null,那么代表通过 检验,返回true,否则取消这次request。取消的过程是:如果这个请求是Multipart类型的,那么 要对这个MultipartRequestHandler进行回滚;而后,获取当前的ActionMapping的input值,即在 config XML 中<action.../>定义的input属性。如果ActionMapping没有这个input值,那么调用 response.sendError(...)方法,然后返回false;如果存在,就保存验证出错信息到request中, key为Globals.ERROR_KEY,跳转到input所指定的地址中。 processForward(HttpServletRequest, HttpServletResponse, ActionMapping)以及 processInclude(HttpServletRequest, HttpServletResponse, ActionMapping)分别通过执行 RequestDispatcher.forward(request, response) 以及 RequestDispatcher.include(request, response)实现对页面的跳转。 但是如果当前的请求还有其它操作要执行,那么ActionMapping中的include或者forward属性值就 会为空。这时需要通过调用processActionCreate(HttpServletRequest, HttpServletResponse, ActionMapping)方法根据config XML文件的配置信息生成一个Action类,然后通过 processActionPerform(...)调用生成的Action中的execute(...)方法。最后根据execute(...) 方法返回的ActionForword类,执行跳转。在整个过程中有一个url的计算方法,这个将在下一篇 中说明。 七、对ActionMaping结构的说明: 在ActionConfig为保存Moudle Action属性的一个javabean,它有以下属性: * boolean configured 这个对象的所有属性是否已经被配置完。如果已经完成,那么在改变其中 任何属性都会抛出IllegalStateException("Configuration is frozen") * ModuleConfig moduleConfig 本ActionConfig类所属于的ModuleConfig。 * String attribute request范围 或者 session范围 的属性名称,我们将通过这个属性名称来 获取相应的form bean,注意,这个属性与form bean中定义的名称是不一样的。注意以下的方 法: public String getAttribute() { if (this.attribute == null) { return (this.name); } else { return (this.attribute); } } * String forward 调用RequestDispatcher.forward()方法时所需要的上下文相关的地址。 * String include 调用RequestDispatcher.include()方法时所需要的上下文相关的地址。 * String type Action类的类全名,如果上面的两个属性都没有定义的话,就会用它来处理 request。 * String input 如果输入数据没有通过验证,将会以这个值为地址,跳转到相应的页面。 * String multipartClass MultipartRequestHandler实现类的全名 * String name 与本类相关的 form bean 的名称。 * String parameter 用于struts的扩展,存储其它的配置信息。struts自己不会用到这个属性。 * String path 上下文相关的地址,这个地址为下一个将会进入的地址,这个地址必须要以"/" 开头。如果使用了extension mapping的话,这个地址将不会带有扩展名。 * String roles 一个以","分隔的角色名称。这些角色将会有权限访问这个request。 * String scope 缺省为session。 * String prefix,String suffix后缀和前缀。 * boolean unknown 标志当前的ActionConfig类是否是为了未知的request path而准备的, ActionMappings将会根据这个标志返回这个ActionConfig。 * boolean validate 是否调用validate()方法进行数据校验。 ActionMapping 继承自 ActionConfig,它增加了从ModuleConfig查找ActionForword的方法。同 时它还提供了从ModuleConfig查找ExceptionConfig的方法,如果找不到,会通过 type.getSuperclass()方法,根据父类的类名称继续查找,直到最后。 八、关于request.getServletPath()的解释: request.getServletPath() 的返回值就是在下面url中定义的值,比如如果按照下面的定义 <url-pattern> *.do </url-pattern> 那么: http://localhost:8080/servlet/go.do ---------->;/go.do http://localhost:8080/servlet/where/go.do ---------->;/where/go.do 这里有一个小常识,如果 <url-pattern> /where/*.do </url-pattern> 那么地址中只有http://localhost:8080/servlet/where/*.do有效(tomcat 4.0) 在ActionConfig为保存Moudle Action属性的一个javabean,它有以下属性: * boolean configured 这个对象的所有属性是否已经被配置完。如果已经完成,那么在改变其中 任何属性都会抛出IllegalStateException("Configuration is frozen") * ModuleConfig moduleConfig 本ActionConfig类所属于的ModuleConfig。 * String attribute request范围 或者 session范围 的属性名称,我们将通过这个属性名称来 获取相应的form bean,注意,这个属性与form bean中定义的名称是不一样的。注意以下的方 法: public String getAttribute() { if (this.attribute == null) { return (this.name); } else { return (this.attribute); } } * String forward 调用RequestDispatcher.forward()方法时所需要的上下文相关的地址。 * String include 调用RequestDispatcher.include()方法时所需要的上下文相关的地址。 * String type Action类的类全名,如果上面的两个属性都没有定义的话,就会用它来处理 request。 * String input * String multipartClass MultipartRequestHandler实现类的全名 * String name 与本类相关的 form bean 的名称。 * String parameter 用于struts的扩展,存储其它的配置信息。struts自己不会用到这个属性。 * String path 上下文相关的地址,这个地址为下一个将会进入的地址,这个地址必须要以"/" 开头。如果使用了extension mapping的话,这个地址将不会带有扩展名。 * String roles 一个以","分隔的角色名称。这些角色将会有权限访问这个request。 * String scope 缺省为session。 * String prefix,String suffix后缀和前缀。 * boolean unknown 标志当前的ActionConfig类是否是为了未知的request path而准备的, ActionMappings将会根据这个标志返回这个ActionConfig。 * boolean validate 是否调用validate()方法进行数据校验。 ActionMapping 继承自 ActionConfig,它增加了从ModuleConfig查找ActionForword的方法。同 时它还提供了从ModuleConfig查找ExceptionConfig的方法,如果找不到,会通过 type.getSuperclass()方法,根据父类的类名称继续查找,直到最后。 |
参与论坛讨论:http://www.matrix.org.cn/forum.asp 更多技术文章:http://www.matrix.org.cn/article.asp Matrix java门户:http://www.matrix.org.cn |
原文地址:http://www.matrix.org.cn/article/1144.html 任何获得许可转载此文章,须在显著位置标明Matrix的原文地址,并做链接至原文页面,查看详细的版权说明 |
struts源代码阅读(struts 的执行)(转)
最新推荐文章于 2019-09-17 20:46:46 发布