本文共 24952 字,大约阅读时间需要 83 分钟。
struts源码分析
一:说明本文针对Struts1.1做分析,主要希望通过对源代码的分析阐述Struts1.1的工作方式。本文不适合初学者参考,适合具有一定基于Struts开发的程序员参考。下面的描述;里面将会对ActionServlet,RequestProcessor,ModuleConfig等几个类做一些说明。以注释源代码的方式,说明取工作流程。特别申明:Struts1.1代码版权属于Apache遵循The Apache Software License, Version 1.1.二:ActionServlet分析
我们先来看一下使用Struts的配置文件。 actionorg.apache.struts.action.ActionServlet definitions-config/WEB-INF/tiles-defs.xml,/WEB-INF/tiles-tests-defs.xml,/WEB-INF/tiles-tutorial-defs.xml,/WEB-INF/tiles-examples-defs.xml definitions-debug0 definitions-parser-details0 definitions-parser-validatetrue
config
/WEB-INF/struts-config.xml
config/examples
/WEB-INF/struts-examples-config.xml
config/test
/WEB-INF/struts-tests-config.xml
config/tutorial
/WEB-INF/struts-tutorial-config.xml validatetrue debug2 detail2
application
org.apache.struts.webapp.tiles.dev1-1.ApplicationResources 2action*.do
接下来我们来看一下ActionServlet的具体使用
javax.servlet.http.HttpServlet||-->org.apache.struts.action.ActionServlet所以本质上ActionServlet是一个普通的servlet,负责处理.do为后缀的Http请求.servlet在执行doGet(),doPost(),之前先调用init(),以下我们先分析一下init()方法/*** Initialize this servlet. Most of the processing has been factored into* support methods so that you can override particular functionality at a* fairly granular level.* servlet初始化操作,注意初始化顺序
* @exception ServletException if we cannot configure ourselves correctly*/public void init() throws ServletException { //注意初始化的顺序//Initialize our internal MessageResources bundleinitInternal();//Initialize other global characteristics of the controller servlet//处理一些全局变量的设置如:debug,detail等initOther();//Initialize the servlet mapping under which our controller servlet//is being accessed. This will be used in the &html:form>//tag to generate correct destination URLs for form submissions//主要是注册DTD文件以及解析web.xml关于ActionServlet的配置。如后缀名等.// Configure the processing rules that we need// digester.addCallMethod("web-app/servlet-mapping",// "addServletMapping", 2);// digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);// digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);//initServlet()的上面一段将把Struts默认的后缀名从web.xml中解析得到//也就是web.xml中的如下配置:////action//*.do//默认以.do结尾的请求都将由Struts来处理,你可以自己修改//initServlet();// Initialize modules as needed
//在Attribute中保存类实例getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);//根据配置文件生成ModuleConfig,这是很重要的一步.下面会专门分析//在tiles的配置中先解析注释为"Mark 0"的一个配置文件:/WEB-INF/struts-config.xml//使用initModuleConfig方法解析XML文件.//参数为prefix:"",paths:"/WEB-INF/struts-config.xml"ModuleConfig moduleConfig = initModuleConfig("", config);//初始化MessageinitModuleMessageResources(moduleConfig);//初始化JDBC DataSourceinitModuleDataSources(moduleConfig);//初始化PlunIninitModulePlugIns(moduleConfig);moduleConfig.freeze();
//在Struts1.1以后可以使用多个配置文件,在解析完默认的配置文件也就是上面提到的//注释为"Mark 0"的一个配置文件:/WEB-INF/struts-config.xml后解析其他的配置文件Enumeration names = getServletConfig().getInitParameterNames();//依次解析注释为"Mark 1"、"Mark 2"、"Mark 3"对应配置文件while (names.hasMoreElements()) { //每一个配置文件的文件名String name = (String) names.nextElement();if (!name.startsWith("config/")) { continue;}//String prefix = name.substring(6);moduleConfig = initModuleConfig(prefix, getServletConfig().getInitParameter(name));initModuleMessageResources(moduleConfig);
initModuleDataSources(moduleConfig);initModulePlugIns(moduleConfig);moduleConfig.freeze();}destroyConfigDigester();}
/*** 此方法使用Digester解析XML,关于使用Digester的介绍参看我的另外一篇文章*Initialize the application configuration information for the
* specified module. ** @param prefix Module prefix for this module* @param paths Comma-separated list of context-relative resource path(s)* for this modules's configuration resource(s)** @exception ServletException if initialization cannot be performed* @since Struts 1.1*/protected ModuleConfig initModuleConfig(String prefix, String paths) throws ServletException {if (log.isDebugEnabled()) {
log.debug("Initializing module path '" + prefix +"' configuration from '" + paths + "'");}// Parse the configuration for this module
ModuleConfig config = null;InputStream input = null;String mapping = null;try { //工厂方法创建ModuleConfig标记为:prefixModuleConfigFactory factoryObject =ModuleConfigFactory.createFactory();config = factoryObject.createModuleConfig(prefix);// Support for module-wide ActionMapping type override
mapping = getServletConfig().getInitParameter("mapping");if (mapping != null) { config.setActionMappingClass(mapping);}// Configure the Digester instance we will use
//得到解析XML的digesterDigester digester = initConfigDigester();// Process each specified resource path
while (paths.length() > 0) { //开始解析指定路径文件名的文件digester.push(config);String path = null;//文件是否为多个并且使用","分割int comma = paths.indexOf(',');//存在多个配置文件if (comma >= 0) { //先解析第一个path = paths.substring(0, comma).trim();//paths存放剩余的文件名下次再处理paths = paths.substring(comma + 1);} else { //当前只存在一个path = paths.trim();//没有剩余,下次循环根据上面一句将会在唯一的循环退出点"point break"//退出循环paths = "";}//全部解析完成跳出循环//point breakif (path.length() < 1) { break;}//根据文件名取文件URL url = getServletContext().getResource(path);InputSource is = new InputSource(url.toExternalForm());input = getServletContext().getResourceAsStream(path);is.setByteStream(input);digester.parse(is);//全局变量的形式把解析生成的ModuleConfig实例保存起来//如"Mark 1"处标记的"config/examples",//key为:"org.apache.struts.action.MODULEexamples"//Globals.MODULE_KEY值为:org.apache.struts.action.MODULEgetServletContext().setAttribute(Globals.MODULE_KEY + prefix, config);input.close();}} catch (Throwable t) {
log.error(internal.getMessage("configParse", paths), t);throw new UnavailableException(internal.getMessage("configParse", paths));} finally { if (input != null) { try { input.close();} catch (IOException e) { ;}}}// Force creation and registration of DynaActionFormClass instances
// for all dynamic form beans we wil be using//根据ModuleConfig实例得到配置的FormBean的配置//注意:因为在Struts整个运行当中FormBean的实例要在Action的实例创建之前先创建//因为Action执行perform(1.1以前),execute(1.1)需要使用到FormBeanFormBeanConfig fbs[] = config.findFormBeanConfigs();for (int i = 0; i < fbs.length; i++) { if (fbs[i].getDynamic()) { DynaActionFormClass.createDynaActionFormClass(fbs[i]);}}// Special handling for the default module (for
// backwards compatibility only, will be removed later)//下面是生成一些实例if (prefix.length() < 1) { defaultControllerConfig(config);defaultMessageResourcesConfig(config);defaultFormBeansConfig(config);defaultForwardsConfig(config);defaultMappingsConfig(config);}// Return the completed configuration object
//config.freeze(); // Now done after plugins initreturn (config);}
到此初始化工作基本结束,下面将处理具体的Http请求。在Servlet协议中所有的post方法将调用
以下方法来处理public void doPost(HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException {}get方法也调用类似的方法来处理在Struts中post,get方法都调用同一个方法
process(request, response);来处理具体的请求如下:/*** 对每一个提交过来的Action进行处理* Perform the standard request processing for this request, and create* the corresponding response.** @param request The servlet request we are processing* @param response The servlet response we are creating** @exception IOException if an input/output error occurs* @exception ServletException if a servlet exception is thrown*/protected void process(HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException { //设置或删除AttributeRequestUtils.selectModule(request, getServletContext());//具体的处理交给RequestProcessor去处理HttpRequest,HttpResponse//这是一个很典型的设计模式//下面我们将详细来分析RequestProcessor,很容易理解Struts的运行方式getRequestProcessor(getModuleConfig(request)).process(request, response);}三:RequestProcessor分析
通过前面的分析我们知道Struts中对HttpRequest,HttpResponse的处理都交给RequestProcessor的process()方法来处理。下面我们来看看process方法/*** Process an HttpServletRequest and create the* corresponding HttpServletResponse. ** @param request The servlet request we are processing* @param response The servlet response we are creating** @exception IOException if an input/output error occurs* @exception ServletException if a processing exception occurs*/public void process(HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException {// Wrap multipart requests with a special wrapper
//如果是upload,返回一个MultipartRequestWrapper()request = processMultipart(request);// Identify the path component we will use to select a mapping
//对request进行分析得到提交过来的Form ActionString path = processPath(request, response);if (path == null) { return;}if (log.isInfoEnabled()) { log.info("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);//设置Cache不保存processNoCache(request, response);// General purpose preprocessing hook
if (!processPreprocess(request, response)) { return;}// Identify the mapping for this request
//得到path以后,根据配置文件(struts-config.xml)的相关配置来得到一个//ActionMapping的实例//ActionMapping继承ActionConfig//仔细看一下ActionMapping的代码就能发现://下面的一段将解析影射一个ActionMapping的实例//在ActionServlet在初始化的时候实际上已经把所有的ActionMapping的实例//都已经创建好了。processMapping方法实际上是从Attribute中去得到已经//保存好的ActionMapping的实例,可以理解为在Tomcat启动的时候已经//把所有的ActionMapping保存在Attribute里面。所以在Tomcat启动的时候//比较慢,如果Struts-config.xml有问题启动就会出错。/***type="org.apache.struts.webapp.tiles.test.TestActionTileAction">
**/
//****注意得到创建实例的顺序//ActionMapping实例已经创建好了,现在从Atribute中取到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
//根据mapping得到ActionForm的实例。//同名ActionForm在系统中只会创建一次。ActionForm form = processActionForm(request, response, mapping);//压栈processPopulate(request, response, form, mapping);//处理校验,调用ActionForm的validate方法//假如出错将会返回到前一页面//也就是说在Action还没有创建之前就将做校验if (!processValidate(request, response, form, mapping)) { return;}// 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
//在得到ActionMapping、ActionForm的实例后接下来得到Action实例//实例如果已经创建从Map里面去取如果没有创建一个并保存在Map里面//对保存Action实例的Map 实现线程同步Action action = processActionCreate(request, response, mapping);if (action == null) { return;}// Call the Action instance itself
//在ActionMapping、ActionForm、Action实例创建好以后//调用Action的execute()方法得到一个ActionForward//因为所有的可执行Action都必须有override Action的execute()/perform()方法ActionForward forward =processActionPerform(request, response,action, form, mapping);// Process the returned ActionForward instance
//Action已经正常执行,执行结束后将返回到另外一个页面processActionForward(request, response, forward);}
仔细阅读上面的代码,要特别注意ActionMapping、ActionForm、Action的实例是依次创建的。
创建完以后才去执行Action的execute()方法。为什么要依次创建ActionMapping、ActionForm、Action?????? 四:ModuleConfig分析现在我们很有必要了解一下ModuleConfig这个类,因为太多地方用到了。实际上ModuleConfig是一个接口有一个实现。org.apache.struts.config.impl.ModuleConfigImpl具体实现我就没有不要去分析了。我们来看看这个接口。package org.apache.struts.config;
/**
* The collection of static configuration information that describes a* Struts-based module. Multiple modules are identified by* a prefix at the beginning of the context* relative portion of the request URI. If no module prefix can be* matched, the default configuration (with a prefix equal to a zero-length* string) is selected, which is elegantly backwards compatible with the* previous Struts behavior that only supported one module. ** @author Rob Leland* @version $Revision: 1.2 $ $Date: 2002/12/22 05:31:14 $* @since Struts 1.1*/public interface ModuleConfig { /*** Has this module been completely configured yet. Once this flag* has been set, any attempt to modify the configuration will return an* IllegalStateException.*/boolean getConfigured();/**
* The controller configuration object for this module.*/ControllerConfig getControllerConfig();/*** The controller configuration object for this module.* @param cc The controller configuration object for this module.*/void setControllerConfig(ControllerConfig cc);
/**
* The prefix of the context-relative portion of the request URI, used to* select this configuration versus others supported by the controller* servlet. A configuration with a prefix of a zero-length String is the* default configuration for this web module.*/String getPrefix();/**
* The prefix of the context-relative portion of the request URI, used to* select this configuration versus others supported by the controller* servlet. A configuration with a prefix of a zero-length String is the* default configuration for this web module.*/public void setPrefix(String prefix);/*** The default class name to be used when creating action mapping* instances.*/String getActionMappingClass();/*** The default class name to be used when creating action mapping* instances.* @param actionMappingClass default class name to be used when creating action mapping* instances.*/void setActionMappingClass(String actionMappingClass);
/**
* Add a new ActionConfig instance to the set associated* with this module.** @param config The new configuration instance to be added** @exception java.lang.IllegalStateException if this module configuration* has been frozen*/void addActionConfig(ActionConfig config);/**
* Add a new DataSourceConfig instance to the set associated* with this module.** @param config The new configuration instance to be added** @exception java.lang.IllegalStateException if this module configuration* has been frozen*/void addDataSourceConfig(DataSourceConfig config);/**
* Add a new ExceptionConfig instance to the set associated* with this module.** @param config The new configuration instance to be added** @exception java.lang.IllegalStateException if this module configuration* has been frozen*/void addExceptionConfig(ExceptionConfig config);/**
* Add a new FormBeanConfig instance to the set associated* with this module.** @param config The new configuration instance to be added** @exception java.lang.IllegalStateException if this module configuration* has been frozen*/void addFormBeanConfig(FormBeanConfig config);/**
* Add a new ForwardConfig instance to the set of global* forwards associated with this module.** @param config The new configuration instance to be added** @exception java.lang.IllegalStateException if this module configuration* has been frozen*/void addForwardConfig(ForwardConfig config);/**
* Add a new MessageResourcesConfig instance to the set* associated with this module.** @param config The new configuration instance to be added** @exception IllegalStateException if this module configuration* has been frozen*/void addMessageResourcesConfig(MessageResourcesConfig config);/**
* Add a newly configured org.apache.struts.config.PlugInConfig} instance to the set of* plug-in Actions for this module.** @param plugInConfig The new configuration instance to be added*/void addPlugInConfig(PlugInConfig plugInConfig);/**
* Return the action configuration for the specified path, if any;* otherwise return null.** @param path Path of the action configuration to return*/ActionConfig findActionConfig(String path);/**
* Return the action configurations for this module. If there are* none, a zero-length array is returned.*/ActionConfig[] findActionConfigs();/**
* Return the data source configuration for the specified key, if any;* otherwise return null.** @param key Key of the data source configuration to return*/DataSourceConfig findDataSourceConfig(String key);/**
* Return the data source configurations for this module. If there* are none, a zero-length array is returned.*/DataSourceConfig[] findDataSourceConfigs();/**
* Return the exception configuration for the specified type, if any;* otherwise return null.** @param type Exception class name to find a configuration for*/ExceptionConfig findExceptionConfig(String type);/**
* Return the exception configurations for this module. If there* are none, a zero-length array is returned.*/ExceptionConfig[] findExceptionConfigs();/**
* Return the form bean configuration for the specified key, if any;* otherwise return null.** @param name Name of the form bean configuration to return*/FormBeanConfig findFormBeanConfig(String name);/**
* Return the form bean configurations for this module. If there* are none, a zero-length array is returned.*/FormBeanConfig[] findFormBeanConfigs();/**
* Return the forward configuration for the specified key, if any;* otherwise return null.** @param name Name of the forward configuration to return*/ForwardConfig findForwardConfig(String name);/**
* Return the form bean configurations for this module. If there* are none, a zero-length array is returned.*/ForwardConfig[] findForwardConfigs();/**
* Return the message resources configuration for the specified key,* if any; otherwise return null.** @param key Key of the data source configuration to return*/MessageResourcesConfig findMessageResourcesConfig(String key);/**
* Return the message resources configurations for this module.* If there are none, a zero-length array is returned.*/MessageResourcesConfig[] findMessageResourcesConfigs();/**
* Return the configured plug-in actions for this module. If there* are none, a zero-length array is returned.*/PlugInConfig[] findPlugInConfigs();/**
* Freeze the configuration of this module. After this method* returns, any attempt to modify the configuration will return* an IllegalStateException.*/void freeze();/**
* Remove the specified action configuration instance.** @param config ActionConfig instance to be removed** @exception java.lang.IllegalStateException if this module configuration* has been frozen*/void removeActionConfig(ActionConfig config);/**
* Remove the specified exception configuration instance.** @param config ActionConfig instance to be removed** @exception java.lang.IllegalStateException if this module configuration* has been frozen*/void removeExceptionConfig(ExceptionConfig config);/**
* Remove the specified data source configuration instance.** @param config DataSourceConfig instance to be removed** @exception java.lang.IllegalStateException if this module configuration* has been frozen*/void removeDataSourceConfig(DataSourceConfig config);/**
* Remove the specified form bean configuration instance.** @param config FormBeanConfig instance to be removed** @exception java.lang.IllegalStateException if this module configuration* has been frozen*/void removeFormBeanConfig(FormBeanConfig config);/**
* Remove the specified forward configuration instance.** @param config ForwardConfig instance to be removed** @exception java.lang.IllegalStateException if this module configuration* has been frozen*/void removeForwardConfig(ForwardConfig config);/**
* Remove the specified message resources configuration instance.** @param config MessageResourcesConfig instance to be removed** @exception java.lang.IllegalStateException if this module configuration* has been frozen*/void removeMessageResourcesConfig(MessageResourcesConfig config);} 上面的注释已经非常清晰了。我就不去浪费大家的时间了,再想仔细看就去看他的实现。其实主要是对HashMap()的处理。五:ActionMapping分析
最后我们实现很有必要看一下ActionMapping,因为你如果想清楚的了解Struts-config.xml这个配置文件的作用,你应该要知道ActionMapping前面已经说过ActionMapping继承ActionConfig以下就是ActionMapping加的四个方法。/*** Find and return the ExceptionConfig instance defining* how exceptions of the specified type should be handled. This is* performed by checking local and then global configurations for the* specified exception's class, and then looking up the superclass chain* (again checking local and then global configurations). If no handler* configuration can be found, return null. ** @param type Exception class for which to find a handler* @since Struts 1.1*/public ExceptionConfig findException(Class type) {}
/*** Find and return the ForwardConfig instance defining* how forwarding to the specified logical name should be handled. This is* performed by checking local and then global configurations for the* specified forwarding configuration. If no forwarding configuration* can be found, return null. ** @param name Logical name of the forwarding instance to be returned*/public ActionForward findForward(String name) {}
/*** Return the logical names of all locally defined forwards for this* mapping. If there are no such forwards, a zero-length array* is returned.*/public String[] findForwards() { } /***Create (if necessary) and return an ActionForward} that
* corresponds to the input property of this Action.** @since Struts 1.1b2*/public ActionForward getInputForward() {}
还是看以下他的基类ActionConfig吧
public class ActionConfig implements Serializable {/**
* Has configuration of this component been completed?*/protected boolean configured = false;/**
* The set of exception handling configurations for this* action, if any, keyed by the type property.*/protected HashMap exceptions = new HashMap();/**
* The set of local forward configurations for this action, if any,* keyed by the name property.*/protected HashMap forwards = new HashMap();/**
* The module configuration with which we are associated.*/protected ModuleConfig moduleConfig = null;/**
* The request-scope or session-scope attribute name under which our* form bean is accessed, if it is different from the form bean's* specified name.*/protected String attribute = null;/**
* Context-relative path of the web application resource that will process* this request via RequestDispatcher.forward(), instead of instantiating* and calling the Action class specified by "type".* Exactly one of forward, include, or* type must be specified.*/protected String forward = null;/**
* Context-relative path of the web application resource that will process* this request via RequestDispatcher.include(), instead of instantiating* and calling the Action class specified by "type".* Exactly one of forward, include, or* type must be specified.*/protected String include = null;/**
* Context-relative path of the input form to which control should be* returned if a validation error is encountered. Required if "name"* is specified and the input bean returns validation errors.*/protected String input = null;/**
* Fully qualified Java class name of the* MultipartRequestHandler implementation class used to* process multi-part request data for this Action.*/protected String multipartClass = null;/**
* Name of the form bean, if any, associated with this Action.*/protected String name = null;/**
* General purpose configuration parameter that can be used to pass* extra iunformation to the Action instance selected by this Action.* Struts does not itself use this value in any way.*/protected String parameter = null;/**
* Context-relative path of the submitted request, starting with a* slash ("/") character, and omitting any filename extension if* extension mapping is being used.*/protected String path = null;/**
* Prefix used to match request parameter names to form ben property* names, if any.*/protected String prefix = null;/**
* Comma-delimited list of security role names allowed to request* this Action.*/protected String roles = null;/**
* Identifier of the scope ("request" or "session") within which* our form bean is accessed, if any.*/protected String scope = "session";/**
* Suffix used to match request parameter names to form bean property* names, if any.*/protected String suffix = null;/**
* Fully qualified Java class name of the Action class* to be used to process requests for this mapping if the* forward and include properties are not set.* Exactly one of forward, include, or* type must be specified.*/protected String type = null;/**
* Should the validate() method of the form bean associated* with this action be called?*/protected boolean validate = true;}其实ActionConfig是一个很典型的ValueObject.所以其他的get/set方法我就不写出来了。
看这个代码一定要和struts-config.xml一起来看,根据struts-config.xml去找找每一段配置文件最终要生成一个ActionConfig,他们之间的对应关系。如果你想扩展Struts,ActionMapping估计你一定要修改。还有ActionServlet你也要修改。六:结束语
分析了一些代码下面做一些概述。先来整体的了解一下Struts的工作流程.在实现一个基于Struts的运用之前我们首先是做环境设置,Struts正常工作需要至少两个配置文件web.xml,struts-config.xml.web.xml告诉App Server所有以.do结尾的请求最终提交给ActionServlet去处理。2就规定ActionServlet是在App Server启动的时候创建的并且一直存在。ActionServlet在创建的时候会做如下的工作:保存一些后面需要使用的实例在Attribute(内存)里面。根据web.xml的配置解析struts-config.xml文件。根据struts-config.xml的配置生成ActionMapping实例并且保存。ActionServlet在生命周期就一直等待Http 请求
每一个.do结尾的Http 请求都由ActionServlet先截获然后根据请求路径得到具体调用那一个Action去处理,在这之前生成、处理ActionForm。具体知道那一个Action去处理请求后调用Action的execute()/perform()处理完成,返回。
本文来自CSDN博客,转载请标明出处: