| |
struts1.1部分源代码分析 一:说明 本文针对struts1.1b3做分析,主要希望通过对源代码的分析阐述struts1.1的工作方式。 本文不适合初学者参考,适合具有一定基于struts开发的程序员参考。 下面的描述;里面将会对actionservlet,requestprocessor,moduleconfig等几个类做一些 说明。以注释源代码的方式,说明取工作流程。 特别申明:struts1.1代码版权属于apache遵循the apache software license, version 1.1. 本文版权属于孤魂一笑个人所有,任何个人或组织希望转载,请与我联系。并获得我的授权 方可转载。
二:actionservlet分析 我们先来看一下使用struts的配置文件。 action org.apache.struts.action.actionservlet org.apache.struts.tiles.actioncomponentservlet --> 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-debug 0 definitions-parser-details 0 definitions-parser-validate true 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 validate true debug 2 detail 2 application org.apache.struts.webapp.tiles.dev1-1.applicationresources 2
action *.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 bundle initinternal(); //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); //初始化message initmodulemessageresources(moduleconfig); //初始化jdbc datasource initmoduledatasources(moduleconfig); //初始化plunin initmoduleplugins(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标记为:prefix moduleconfigfactory 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的digester digester 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 break if (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.module getservletcontext().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)需要使用到formbean formbeanconfig 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 init return (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 { //设置或删除attribute requestutils.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 action string 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 {@link 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 {@link 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()处理完成,返回。
|
|