概述
Struts 源码新版本为 struts-1.3.8-src.zip ( 12-Mar-2007 00:06 )
学习笔记使用struts-1.3.5-src.zip 的源码,
下载地址:http://archive.apache.org/dist/struts/source/
1. 在web.xml中通过下面定义把所有的*.do交给ActionServlet处理
<!-- Standard Action Servlet Configuration (with debugging) -->
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>
/WEB-INF/struts-config.xml,
/WEB-INF/struts-config-Wildcard.xml
</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<!-- Standard Action Servlet Mapping -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
2. 下面研究一下struts的源码,由于servlet设置了load-on-startup,所以tomcat启动时会加载ActionServlet,也就是会执行ActionServlet中的init()方法,Struts 的初始化实现就是在这里实现的。
注: 由于servlet的生命周期为 web容器加载和实例化类/init()初始化/service()请求处理/destroy()四个阶段,而init()方法在tomcat启动后只执行一次,所以如果想在tomcat启动后用debug模式查看ActionServlet中init()方法的执行,可以把上面的<load-on-startup>2</load-on-startup>注释掉就可以了(不过真正开发时还是需要的)。
3. 在ActionServlet中定义了一些常量,如下:
// 默认的struts配置文件为/WEB-INF/struts-config.xml
protected String config = "/WEB-INF/struts-config.xml"; // ② initOther(); ⑤ initModuleConfig ();
// 默认的链(定义了一个按顺序执行的处理流程)配置文件
protected String chainConfig = "org/apache/struts/chain/chain-config.xml";
// ④ initChain();
protected Digester configDigester = null; // ⑤ initModuleConfig ();
// 如convertNull 为true,Java包装类(如java.lang.Integer)的初始值为null
protected boolean convertNull = false; // ② initOther();
protected MessageResources internal = null; // ① initInternal();
// 默认的 struts-core-1.3.5.jar 包 中资源文件为ActionResources.properties
protected String internalName = "org.apache.struts.action.ActionResources";
// ① initInternal();
// 一些文档类型定义,用来验证相应的配置文件如struts-config.xml是否正确
protected String[] registrations =
{
"-//Apache Software Foundation//DTD Struts Configuration 1.0//EN",
"/org/apache/struts/resources/struts-config_1_0.dtd",
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN",
"/org/apache/struts/resources/struts-config_1_1.dtd",
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN",
"/org/apache/struts/resources/struts-config_1_2.dtd",
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN",
"/org/apache/struts/resources/struts-config_1_3.dtd",
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
"/org/apache/struts/resources/web-app_2_3.dtd"
}; // ③ initServlet();
protected String servletMapping = null; // ③ initServlet();
protected String servletName = null; // ③ initServlet();
4. ActionServlet 中的init()方法执行流程如下
① 内部资源文件 ActionResources.properties 的初始化 initInternal();
protected MessageResources internal = null; // ① initInternal();
protected String internalName = "org.apache.struts.action.ActionResources"; // ① initInternal();
// initInternal 方法中通过下面得到一个MessageResources对象
internal = MessageResources.getMessageResources(internalName);
此资源文件主要包括一些消息信息的定义,具体可参考org.apache.struts.action下的ActionResources.properties文件
在MessageResources.java中的getMessageResources方法,
if (defaultFactory == null) {
defaultFactory = MessageResourcesFactory.createFactory(); // ⑴
}
return defaultFactory.createResources(config); // 传入internalName // ⑵
⑴
MessageResourcesFactory.createFactory() 所做的工作:
protected static transient Class clazz = null;
protected static String factoryClass =
"org.apache.struts.util.PropertyMessageResourcesFactory";
clazz = RequestUtils.applicationClass(factoryClass);
而RequestUtils.applicationClass通过classLoader加载一个
org.apache.struts.util.PropertyMessageResourcesFactory
⑵
defaultFactory.createResources(config) 所做的工作:
this.factory = factory;
("org.apache.struts.util.PropertyMessageResourcesFactory")
this.config = config;("org.apache.struts.action.ActionResources")
this.returnNull = returnNull;(true)
PropertyMessageResourcesFactory extends MessageResourcesFactory
返回一个MessageResources对象
② 调用 initOther(); 从web.xml中加载ActionServlet的初始化参数,包括config/ convertNull
protected String config = "/WEB-INF/struts-config.xml"; // ② initOther();
protected boolean convertNull = false; // ② initOther();
// 得到web.xml中"config"参数
String value;
value = getServletConfig().getInitParameter("config");
if (value != null) {
config = value;
}
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name> <!-- 得到"config"参数-->
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>convertNull</param-name> <!-- 得到"convertNull"参数-->
<param-value>true</param-value>
</init-param>
.......
</servlet>
// 获得convertNull的值(true/yes/on/y/1)
getServletConfig().getInitParameter("convertNull");
如果这个参数的值为 true (true/yes/on/y/1) , 数值型(BigDecimal/BigInteger/Boolean/Byte/Character/Double/Float/Integer/Long/Short)的Java 包装类(比如java.lang.Integer)的初始值为null,而非0。缺省值[false]
使其初始值为null的方法如下:
// 将所有的转换器注销掉
ConvertUtils.deregister();
// 为指定类型clazz注册转换器converter
ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
ConvertUtils.register(new BigIntegerConverter(null),BigInteger.class);
.......
注: ConvertUtils 用法如下
deregister () 和 deregister (java.lang.Class clazz)
注销转换器,前者将所有的转换器注销掉,后者只注销对应于clazz的转换器register( Converter converter, java.lang.Class clazz)
为指定类型clazz注册转换器converter。如果clazz已经存在一个对应的转换器,那么converter覆盖原来的转换器。
③ 调用 initServlet(); 从web.xml中加载ActionServlet的初始化参数如servlet-name,加载DTD文件并把其放入HashMap缓存,读取并解析web.xml的内容
// Remember our servlet name
getServletConfig().getServletName();
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name> <!-- 得到"config"参数-->
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>convertNull</param-name> <!-- 得到"convertNull"参数-->
<param-value>true</param-value>
</init-param>
.......
</servlet>
// Prepare a Digester to scan the web application deployment descriptor
Digester digester = new Digester();
// 把当前的 ActionServlet 对象放入到解析堆栈中
digester.push(this);
// 指明要考虑命名空间
digester.setNamespaceAware(true);
// 缺省值[false] ,解析器只是检查XML是否格式良好(well formed)
digester.setValidating(false);
// Register our local copy of the DTDs that we can find
// struts 可使用 struts-core-1.3.5.jar 包 中的DTD中来处理struts配置文件,这样可适用于那些没有连接到internet的应用环境
for (int i = 0; i < registrations.length; i += 2) {
URL url = this.getClass().getResource(registrations[i + 1]);
if (url != null) {
// 读取DTD文件并把其放入 HashMap 缓存
digester.register(registrations[i], url.toString());
}
}
/************************************************************
// 一些文档类型定义,用来验证相应的配置文件如struts-config.xml是否正确
protected String[] registrations =
{
"-//Apache Software Foundation//DTD Struts Configuration 1.0//EN",
"/org/apache/struts/resources/struts-config_1_0.dtd",
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN",
"/org/apache/struts/resources/struts-config_1_1.dtd",
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN",
"/org/apache/struts/resources/struts-config_1_2.dtd",
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN",
"/org/apache/struts/resources/struts-config_1_3.dtd",
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
"/org/apache/struts/resources/web-app_2_3.dtd"
}; // ③ initServlet();
************************************************************/
// Configure the processing rules that we need
// 运行时,digester 就会调用 ActionServlet中的 addServletMapping() 方法,并传入两个参数
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);
得到
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
/************************************************************
// 来判断当前 servlet 名称是否为正在运行的 servlet 名称,如是,就把 url-pattern 作为 servletMapping
public void addServletMapping(String servletName, String urlPattern) {
if (servletName == null) {
return;
}
if (servletName.equals(this.servletName)) {
if (log.isDebugEnabled()) {
log.debug("Process servletName=" + servletName
+ ", urlPattern=" + urlPattern);
}
this.servletMapping = urlPattern;
}
}
************************************************************/
// 读取配置文件web.xml的内容
InputStream input = getServletContext().getResourceAsStream("/WEB-INF/web.xml");
// 如找不到/WEB-INF/web.xml文件,则报错
if (input == null) {
log.error(internal.getMessage("configWebXml"));
throw new ServletException(internal.getMessage("configWebXml"));
}
/************************************************************
// 报错信息定义在orgapachestrutsactionActionResources.properties中
configWebXml=The /WEB-INF/web.xml was not found.
************************************************************/
// 解析input流文件,每读到一个节点元素就触发一个事件
digester.parse(input);
注: Digester 是一个基于 DOM 的 SAX 实现的类,它是事件触发的,可以将XML文件转换为任意的Java对象,支持规则的对任意XML文档的处理。原先是struts项目的一部分,后因其通用性而划归Commons子项目。
// 把servletMapping存储到servletContext中,属性名为Globals.SERVLET_KEY ( " org.apache.struts.action.SERVLET_MAPPING " )
if (servletMapping != null) {
getServletContext().setAttribute(Globals.SERVLET_KEY,servletMapping);
}
④ 调用 initChain(); 读取web.xml中命令链文件初始值chainConfig
protected String chainConfig = "org/apache/struts/chain/chain-config.xml";
// ④ initChain();
// 如没有chainConfig参数,则使用默认 "org/apache/struts/chain/chain-config.xml"
String value;
value = getServletConfig().getInitParameter("chainConfig");
if (value != null) {
chainConfig = value;
}
ConfigParser parser = new ConfigParser();
List urls = splitAndResolvePaths(chainConfig);
URL resource;
// chainConfig 替换了原来传统的在 RequestProcessor 类中执行的 HTTP 请求处理
for (Iterator i = urls.iterator(); i.hasNext();) {
resource = (URL) i.next();
log.info("Loading chain catalog from " + resource);
parser.parse(resource);
}
/************************************************************
// org.apache.struts.action. RequestProcessor .java 的process方法中,一些方法如
processLocale (request, response);
processContent (request, response);
processNoCache (request, response);
.......
被 "org/apache/struts/chain/chain-config.xml" 中下列配置所取代
<command
className="org.apache.struts.chain.commands.servlet.SelectLocale"/>
<command
className="org.apache.struts.chain.commands.servlet.SetContentType"/>
<command
className="org.apache.struts.chain.commands.servlet.RequestNoCache"/>
.......
好处是充分降低了代码内部方法与方法之间的耦合度
************************************************************/
在④/⑤之间
// 把servlet对象存储到servletContext中,属性名为Globals.ACTION_SERVLET_KEY
( " org.apache.struts.action.ACTION_SERVLET " )
getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
⑤ 调用 initModuleConfigFactory(); 和 initModuleConfig("", config); 创建 ModuleConfig 对象。Struts中的MessageResource、PlugIn、数据源等,都是通过ModuleConfig来实现的。
// 初始化ModuleConfig配置工厂
initModuleConfigFactory(); // ⑴
// 由配置工厂实例化一个ModuleConfig的对象
ModuleConfig moduleConfig = initModuleConfig("", config); // ⑵
⑴
initModuleConfigFactory(); 所做的工作:
// 得到web.xml中"configFactory"参数,如果找不到,则使用 默认工厂
String configFactory = getServletConfig().getInitParameter("configFactory");
if (configFactory != null) {
ModuleConfigFactory.setFactoryClass(configFactory);
}
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>configFactory</param-name> <!-- 得到"configFactory"参数-->
<param-value>com.lively.base.webapp.UserModuleConfigFactory</param-value>
</init-param>
.......
</servlet>
在ModuleConfigFactory.java中的setFactoryClass方法,
public static void setFactoryClass(String factoryClass) {
ModuleConfigFactory.factoryClass = factoryClass;
ModuleConfigFactory.clazz = null;
}
其中
protected static Class clazz = null;
protected static String factoryClass =
"org.apache.struts.config.impl.DefaultModuleConfigFactory";
⑵
initModuleConfig ("", config); 所做的工作:
// Parse the configuration for this module
ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory(); // Ⅰ
ModuleConfig config = factoryObject.createModuleConfig(prefix); // Ⅱ
Ⅰ
ModuleConfigFactory.createFactory(); 方法中:
protected static Class clazz = null;
protected static String factoryClass =
"org.apache.struts.config.impl.DefaultModuleConfigFactory";
if (clazz == null) {
clazz = RequestUtils.applicationClass(factoryClass);
}
factory = (ModuleConfigFactory) clazz.newInstance();
而RequestUtils.applicationClass通过classLoader加载一个
org.apache.struts.config.impl.DefaultModuleConfigFactory
Ⅱ
ModuleConfig config = factoryObject.createModuleConfig(prefix); 方法中:
// 通过new ModuleConfigImpl(prefix);设置一些变量的初始值,在 initModuleConfig ("", config); 方法的最后会 把ModuleConfig对象放置到servletContext中 (参见 Ⅴ )
protected String prefix = null;
protected HashMap actionConfigs = null;
protected List actionConfigList = null;
protected String actionFormBeanClass = "org.apache.struts.action.ActionFormBean";
protected String actionMappingClass = "org.apache.struts.action.ActionMapping";
protected String actionForwardClass = "org.apache.struts.action.ActionForward";
protected boolean configured = false;
protected ControllerConfig controllerConfig = null;
protected HashMap exceptions = null;
protected HashMap formBeans = null;
protected HashMap forwards = null;
protected HashMap messageResources = null;
protected ArrayList plugIns = null;
public ModuleConfigImpl(String prefix) {
super();
this.prefix = prefix;
this.actionConfigs = new HashMap();
this.actionConfigList = new ArrayList();
this.actionFormBeanClass = "org.apache.struts.action.ActionFormBean";
this.actionMappingClass = "org.apache.struts.action.ActionMapping";
this.actionForwardClass = "org.apache.struts.action.ActionForward";
this.configured = false;
this.controllerConfig = null;
this.exceptions = new HashMap();
this.formBeans = new HashMap();
this.forwards = new HashMap();
this.messageResources = new HashMap();
this.plugIns = new ArrayList();
}
Ⅲ
protected String config = "/WEB-INF/struts-config.xml"; // ② initOther(); ⑤ initModuleConfig ();
protected Digester configDigester = null; // ⑤ initModuleConfig ();
// 初始化Digester,
Digester digester = initConfigDigester();
initConfigDigester(); 所做的工作:
// Create a new Digester instance with standard capabilities
configDigester = new Digester();
configDigester.setNamespaceAware(true);
configDigester.setValidating(this.isValidating());
configDigester.setUseContextClassLoader(true);
// 解析struts配置文件之前,首先添加默认的解析规则
configDigester.addRuleSet(new ConfigRuleSet());
for (int i = 0; i < registrations.length; i += 2) {
URL url = this.getClass().getResource(registrations[i + 1]);
if (url != null) {
configDigester.register(registrations[i], url.toString());
}
}
// 通过getServletConfig().getInitParameter("rulesets");从web.xml中读取用户自定义的解析规则(用","分开的org.apache.commons.digester.RuleSet列表)
this.addRuleSets();
.......
Ⅳ
/* 循环struts配置文件(用","分开的多个struts配置文件)并解析, parseModuleConfigFile 执行之后可以 从struts-config.xml等配置文件中得到 Ⅱ 中 actionConfigs/actionConfigList 、exceptions 、formBeans、forwards、messageResources、plugIns等的配置,并把得到的所有值封装到对象ModuleConfig对象(config)中 */
List urls = splitAndResolvePaths(paths);
URL url;
for (Iterator i = urls.iterator(); i.hasNext();) {
url = (URL) i.next();
digester.push(config);
this.parseModuleConfigFile(digester, url);
}
Ⅴ
// 把config存储到servletContext中 ,属性名为Globals.MODULE_KEY
( " org.apache.struts.action.MODULE " )
getServletContext().setAttribute(Globals.MODULE_KEY
+ config.getPrefix(), config);
⑥ 用户资源文件的初始化 initModuleMessageResources(moduleConfig);
在上面第⑤步的Ⅱ中我们已经创建了ModuleConfig对象并在Ⅲ和Ⅵ中从struts-config.xml等配置文件中得到得到一些配置且封装到ModuleConfig对象中,所以在下面可以直接使用initModuleMessageResources(moduleConfig);
initModuleMessageResources(moduleConfig); 所做的工作:
⑴
/* 从moduleConfig中读取所有的资源文件(包括 ① 内部资源文件 和 ⑤ 中Ⅵ利用Digester读取的struts配置文件指定的用户资源文件) */
MessageResourcesConfig[] mrcs = config.findMessageResourcesConfigs();
注: 此时moduleConfig中默认只含有① 内部资源文件ActionResources.properties
⑵
// 把resources(包括 ① 内部资源文件 和 ⑥ 用户资源文件 )存储到servletContext中 // 属性名为mrcs[i].getKey() + config.getPrefix()
for (int i = 0; i < mrcs.length; i++) {
if ((mrcs[i].getFactory() == null)
|| (mrcs[i].getParameter() == null)) {
continue;
}
if (log.isDebugEnabled()) {
log.debug("Initializing module path '" + config.getPrefix()
+ "' message resources from '" + mrcs[i].getParameter()
+ "'");
}
String factory = mrcs[i].getFactory();
MessageResourcesFactory.setFactoryClass(factory);
MessageResourcesFactory factoryObject =
MessageResourcesFactory.createFactory();
factoryObject.setConfig(mrcs[i]);
MessageResources resources =
factoryObject.createResources(mrcs[i].getParameter());
resources.setReturnNull(mrcs[i].getNull());
resources.setEscape(mrcs[i].isEscape());
getServletContext().setAttribute(mrcs[i].getKey()
+ config.getPrefix(), resources);
}
⑦ 用户插件的初始化 initModulePlugIns(moduleConfig);
在上面第⑤步的Ⅱ中我们已经创建了ModuleConfig对象并在Ⅲ和Ⅵ中从struts-config.xml等配置文件中得到得到一些配置且封装到ModuleConfig对象中,所以在下面可以直接使用initModulePlugIns(ModuleConfig config);
initModulePlugIns(moduleConfig); 所做的工作:
⑴
// 从moduleConfig中读取所有的插件文件
PlugInConfig[] plugInConfigs = config.findPlugInConfigs();
PlugIn[] plugIns = new PlugIn[plugInConfigs.length];
⑵
// 把所有plugIns存储到servletContext中
// 属性名为Globals.PLUG_INS_KEY + config.getPrefix()
.......
getServletContext().setAttribute(Globals.PLUG_INS_KEY
+ config.getPrefix(), plugIns);
.......
⑧ 把struts配置文件中的其他配置 存储到servletContext中 ,包括
initModuleFormBeans(moduleConfig);
initModuleForwards(moduleConfig);
initModuleExceptionConfigs(moduleConfig);
initModuleActions(moduleConfig);
⑨ 调用 moduleConfig.freeze(); 固定组件配置
/* 使ModuleConfig中的 actionConfigs/actionConfigList 、exceptions 、formBeans、forwards、messageResources、plugIns等的配置等变得不可改变 */
moduleConfig.freeze();
⑩ 解析以"config/"开头的其他struts配置文件
// 遍历web.xml中servletConfig配置的 initParameterNames
// 如发现以" config/ " 开始的parameter,则根据此值初始化其它的ModuleConfig
Enumeration names = getServletConfig().getInitParameterNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
if (!name.startsWith(configPrefix)) {
continue;
}
String prefix = name.substring(configPrefixLength);
moduleConfig =
initModuleConfig(prefix,
getServletConfig().getInitParameter(name));
initModuleMessageResources(moduleConfig);
initModulePlugIns(moduleConfig);
initModuleFormBeans(moduleConfig);
initModuleForwards(moduleConfig);
initModuleExceptionConfigs(moduleConfig);
initModuleActions(moduleConfig);
moduleConfig.freeze();
}
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name> <!-- 得到"config"参数-->
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>config/admin</param-name> <!-- 得到"config/admin"参数-->
<param-value>/WEB-INF/struts-config-admin.xml</param-value>
</init-param>
.......
</servlet>
在⑩ 解析以"config/"开头的其他struts配置文件之后ActionServlet的init()方法还需要做
⑴
// 初始化其他模块的前缀
this.initModulePrefixes(this.getServletContext());
initModulePrefixes(this.getServletContext()); 所做的工作:
/* 把其他模块prefixes存储到servletContext中,属性名为
Globals.MODULE_PREFIXES_KEY ( " org.apache.struts.globals.MODULE_PREFIXES " ) */
context.setAttribute(Globals.MODULE_PREFIXES_KEY, prefixes);
⑵
// 设置configDigester = null,释放内存
this.destroyConfigDigester();
至此struts 的核心类ActionServlet的init()方法完成servlet的初始化工作。
最后
以上就是眯眯眼大山为你收集整理的Struts1 - 源码学习 - ActionServlet的全部内容,希望文章能够帮你解决Struts1 - 源码学习 - ActionServlet所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复