//公共的日志实例
protected static Log log = LogFactory.getLog(ActionServlet.class);
//默认的配置文件路径
protected String config = "/WEB-INF/struts-config.xml";
//默认的处理流程配置文件
protected String chainConfig = "org/apache/struts/chain/chain-config.xml";
//未知
protected Digester configDigester = null;
//convertNull若为true,则struts中Java包装类的初始值为null
protected boolean convertNull = false;
//未知
protected MessageResources internal = null;
//默认的资源文件
protected String internalName = "org.apache.struts.action.ActionResources";
//用来验证配置文件格式的dtd文件
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"
};
//未知
protected String servletMapping = null; // :FIXME: - multiples?
//未知
protected String servletName = null;
2. ActionServlet中init方法的执行流程
public void init() throws ServletException {
final String configPrefix = "config/";
final int configPrefixLength = configPrefix.length() - 1;
try {
//①内部资源文件的初始化
initInternal();
//②从web.xml文件中加载ActionServlet的初始化参数:config\convetrNull
initOther();
//③从web.xml文件中加载ActionServlet的初始化参数:servlet-name,加载DTD文件并把其放入HashMap缓存,读取并解析web.xml的内容
initServlet();
//④读取web.xml中命令链文件初始值chainConfig
initChain();
//把servlet对象存储到servletContext中,属性名为Globals.ACTION_SERVLET_KEY
getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
//⑤调用 initModuleConfigFactory(); 和 initModuleConfig("", config); 创建ModuleConfig 对象。Struts中的MessageResource、PlugIn、数据源等,都是通过ModuleConfig来实现的
initModuleConfigFactory();
// Initialize modules as needed
ModuleConfig moduleConfig = initModuleConfig("", config);
//⑥用户资源文件的初始化 initModuleMessageResources(moduleConfig);
initModuleMessageResources(moduleConfig);
//⑦用户插件的初始化 initModulePlugIns(moduleConfig);
initModulePlugIns(moduleConfig);
//⑧ 把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();
}
// 初始化其他模块的前缀
/* 把其他模块prefixes存储到servletContext中,属性名为
Globals.MODULE_PREFIXES_KEY ( " org.apache.struts.globals.MODULE_PREFIXES " ) */
this.initModulePrefixes(this.getServletContext());
// 设置configDigester = null,释放内存
this.destroyConfigDigester();
} catch (UnavailableException ex) {
throw ex;
} catch (Throwable t) {
// The follow error message is not retrieved from internal message
// resources as they may not have been able to have been
// initialized
log.error("Unable to initialize Struts ActionServlet due to an "
+ "unexpected exception or error thrown, so marking the "
+ "servlet as unavailable. Most likely, this is due to an "
+ "incorrect or missing library dependency.", t);
throw new UnavailableException(t.getMessage());
}
}
3.ActionServlet中init()方法调用的各个方法详解
①内部资源文件的初始化 initInternal()方法;
// initInternal 方法中通过下面得到一个MessageResources对象
internal = MessageResources.getMessageResources(internalName);
此资源文件主要包括一些消息信息的定义,具体可参考org.apache.struts.action下的ActionResources.properties文件
②从web.xml文件中加载ActionServlet的初始化参数:config\convetrNull initOther();
protected void initOther()
throws ServletException {
String value;
//得到web.xml文件中的"config"参数
value = getServletConfig().getInitParameter("config");
if (value != null) {
config = value;
}
//获得web.xml文件中convertNull的值,如果此参数的值为true,则所有数值型类型(Integer,Double)的Java
包装类的初始值为null,而非0
value = getServletConfig().getInitParameter("convertNull");
if ("true".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value)
|| "on".equalsIgnoreCase(value) || "y".equalsIgnoreCase(value)
|| "1".equalsIgnoreCase(value)) {
convertNull = true;
}
if (convertNull) {
//将所有的转换器注销掉,使所有值初始为null
ConvertUtils.deregister();
//为指定类型的数据注册转换器
ConvertUtils.register(new BigDecimalConverter(null),
BigDecimal.class);
ConvertUtils.register(new BigIntegerConverter(null),
BigInteger.class);
ConvertUtils.register(new BooleanConverter(null), Boolean.class);
ConvertUtils.register(new ByteConverter(null), Byte.class);
ConvertUtils.register(new CharacterConverter(null), Character.class);
ConvertUtils.register(new DoubleConverter(null), Double.class);
ConvertUtils.register(new FloatConverter(null), Float.class);
ConvertUtils.register(new IntegerConverter(null), Integer.class);
ConvertUtils.register(new LongConverter(null), Long.class);
ConvertUtils.register(new ShortConverter(null), Short.class);
}
}
③initServlet();从web.xml文件中加载ActionServlet的初始化参数:servlet-name,加载DTD文件并把其放入HashMap缓存,读取并解析
web.xml的内容
protected void initServlet()
throws ServletException {
//获取当前Servlet的名称,即web.xml文件中定义的<servlet-name>
this.servletName = getServletConfig().getServletName();
Digester digester = new Digester();
// 把当前的 ActionServlet 对象放入到解析堆栈中
digester.push(this);
//需要考虑命名空间
digester.setNamespaceAware(true);
// 缺省值[false] ,解析器只是检查XML是否格式良好(well formed)
digester.setValidating(false);
for (int i = 0; i < registrations.length; i += 2) {
URL url = this.getClass().getResource(registrations[i + 1]);
if (url != null) {
digester.register(registrations[i], url.toString());
}
}
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);
// Process the web application deployment descriptor
if (log.isDebugEnabled()) {
log.debug("Scanning web.xml for controller servlet mapping");
}
InputStream input =
getServletContext().getResourceAsStream("/WEB-INF/web.xml");
if (input == null) {
log.error(internal.getMessage("configWebXml"));
throw new ServletException(internal.getMessage("configWebXml"));
}
try {
digester.parse(input);
} catch (IOException e) {
log.error(internal.getMessage("configWebXml"), e);
throw new ServletException(e);
} catch (SAXException e) {
log.error(internal.getMessage("configWebXml"), e);
throw new ServletException(e);
} finally {
try {
input.close();
} catch (IOException e) {
log.error(internal.getMessage("configWebXml"), e);
throw new ServletException(e);
}
}
// Record a servlet context attribute (if appropriate)
if (log.isDebugEnabled()) {
log.debug("Mapping for servlet '" + servletName + "' = '"
+ servletMapping + "'");
}
if (servletMapping != null) {
getServletContext().setAttribute(Globals.SERVLET_KEY, servletMapping);
}
}
④读取web.xml中命令链文件初始值chainConfig initChain();
protected void initChain()
throws ServletException {
// Parse the configuration file specified by path or resource
try {
String value;
//读取web.xml文件中的chainConfig参数值,如没有chainConfig参数,则使用默认 "org/apache/struts/chain/chain-config.xml"
value = getServletConfig().getInitParameter("chainConfig");
if (value != null) {
chainConfig = value;
}
ConfigParser parser = new ConfigParser();
List urls = splitAndResolvePaths(chainConfig);
URL resource;
for (Iterator i = urls.iterator(); i.hasNext();) {
resource = (URL) i.next();
log.info("Loading chain catalog from " + resource);
// chainConfig 替换了原来传统的在 RequestProcessor 类中执行的 HTTP 请求处理
parser.parse(resource);
}
} catch (Exception e) {
log.error("Exception loading resources", e);
throw new ServletException(e);
}
}
⑤调用 initModuleConfigFactory(); 和 initModuleConfig("", config); 创建ModuleConfig 对象。Struts中的MessageResource、
PlugIn、数据源等,都是通过ModuleConfig来实现的
protected void initModuleConfigFactory() {
// 得到web.xml中"configFactory"参数,如果找不到,则使用 默认工厂
String configFactory =
getServletConfig().getInitParameter("configFactory");
if (configFactory != null) {
ModuleConfigFactory.setFactoryClass(configFactory);
}
}
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
ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();
ModuleConfig config = factoryObject.createModuleConfig(prefix);
// Configure the Digester instance we will use
Digester digester = initConfigDigester();
/* 循环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
getServletContext().setAttribute(Globals.MODULE_KEY
+ config.getPrefix(), config);
return config;
}
⑥用户资源文件的初始化 initModuleMessageResources(moduleConfig);
protected void initModuleMessageResources(ModuleConfig config)
throws ServletException {
//从moduleConfig中读取所有的资源文件
protected void initModuleMessageResources(ModuleConfig config)
throws ServletException {
MessageResourcesConfig[] mrcs = config.findMessageResourcesConfigs();
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);
}
}
MessageResourcesConfig[] mrcs = config.findMessageResourcesConfigs();
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);
protected void initModulePlugIns(ModuleConfig config)
throws ServletException {
if (log.isDebugEnabled()) {
log.debug("Initializing module path '" + config.getPrefix()
+ "' plug ins");
}
// 从moduleConfig中读取所有的插件文件
PlugInConfig[] plugInConfigs = config.findPlugInConfigs();
PlugIn[] plugIns = new PlugIn[plugInConfigs.length];
// 把所有plugIns存储到servletContext中
getServletContext().setAttribute(Globals.PLUG_INS_KEY
+ config.getPrefix(), plugIns);
for (int i = 0; i < plugIns.length; i++) {
try {
plugIns[i] =
(PlugIn) RequestUtils.applicationInstance(plugInConfigs[i]
.getClassName());
BeanUtils.populate(plugIns[i], plugInConfigs[i].getProperties());
// Pass the current plugIn config object to the PlugIn.
// The property is set only if the plugin declares it.
// This plugin config object is needed by Tiles
try {
PropertyUtils.setProperty(plugIns[i],
"currentPlugInConfigObject", plugInConfigs[i]);
} catch (Exception e) {
;
// FIXME Whenever we fail silently, we must document a valid
// reason for doing so. Why should we fail silently if a
// property can't be set on the plugin?
/**
* Between version 1.138-1.140 cedric made these changes.
* The exceptions are caught to deal with containers
* applying strict security. This was in response to bug
* #15736
*
* Recommend that we make the currentPlugInConfigObject part
* of the PlugIn Interface if we can, Rob
*/
}
plugIns[i].init(this, config);
} catch (ServletException e) {
throw e;
} catch (Exception e) {
String errMsg =
internal.getMessage("plugIn.init",
plugInConfigs[i].getClassName());
log(errMsg, e);
throw new UnavailableException(errMsg);
}
}
}
至此,ActionServlet的初始化工作完成!