Struts的执行相当于分为两个阶段,预加载阶段和执行阶段,预加载阶段是指在Tomcat启动之时就开始执行的内容,而此时我们并未真正进入跳转逻辑,这篇博客我们来分析一下预加载阶段。
配置文件
还记得web.xml中关于Struts的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</param-value>
</init-param>
<init-param>
<param-name>debug</param-name>
<param-value>2</param-value>
</init-param>
<init-param>
<param-name>detail</param-name>
<param-value>2</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>
其中<load-on-startup>2</load-on-startup>指的就是在Tomcat启动之时即执行,实际只要数字大于0,意思就是Tomcat启动即执行,为了进入调试,我们把这里改为0。
<load-on-startup>0</load-on-startup>
主函数init()
经过调试发现,当执行完此函数,Struts的预加载阶段即结束,所以我们主要来看这个函数即可。
/**
* <p>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.</p>
*
* @exception ServletException if we cannot configure ourselves correctly
*/
public void init() throws ServletException {
// Wraps the entire initialization in a try/catch to better handle
// unexpected exceptions and errors to provide better feedback
// to the developer
try {
initInternal();
initOther();
initServlet();
getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
initModuleConfigFactory();
// Initialize modules as needed
ModuleConfig moduleConfig = initModuleConfig("", config);
initModuleMessageResources(moduleConfig);
initModuleDataSources(moduleConfig);
initModulePlugIns(moduleConfig);
moduleConfig.freeze();
Enumeration names = getServletConfig().getInitParameterNames();
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();
}
this.initModulePrefixes(this.getServletContext());
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());
}
}
很明显init函数中执行有其它函数,为了简明扼要,这些函数的代码不再完全照搬,碰到重要或是经典的代码片段我会放进来。
调用函数
initInternal()
此函数的作用是,初始化内部文件ActionResources.properties。
initOther()
此函数的作用是:指定struts-config.xml的路径,默认为/WEB-INF/struts-config.xml;同时注册ConvertUtils的数据类型,例如:
ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
ConvertUtils.register(new BigIntegerConverter(null), BigInteger.class);
initServlet()
此函数的作用是:读取struts在web.xml中的配置信息,例如do/action、url等;获取web程序部署信息。
与ModuleConfig相关
getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
initModuleConfigFactory();
// Initialize modules as needed
ModuleConfig moduleConfig = initModuleConfig("", config);
initModuleMessageResources(moduleConfig);
initModuleDataSources(moduleConfig);
initModulePlugIns(moduleConfig);
moduleConfig.freeze();
Enumeration names = getServletConfig().getInitParameterNames();
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();
}
这段代码的作用是,将web.xml中的Struts配置文件初始化为ModuleConfig对象,将struts-config中的MessageResource、DataSource、PlugIn也都初始化为对象,保存到ServletContext中。不同的是,while循环外操作的是web.xml里 ActionServlet配置中config初始化参数指定的Struts配置文件;while循环内操作的是web.xml里 ActionServlet配置中以"config/"开头的初始化参数指定的Struts配置文件,这样的区别造成前者会产生没有前缀的ModuleConfig对象到ServletConfig中,后者会产生带前缀的ModuleConfig对象放到ServletContext中,前缀为“config/”后的字符串。
initModulePrefixes(this.getServletContext());
此函数的作用是将循环产生的所有前缀生成一个String数组,放到ServletContext中。destroyConfigDigester();
此函数的作用是将ActionServlet类的configDigester的值改为为null。执行顺序图
这篇文章主要是分析了在Tomcat启动时struts的预处理情况,在struts执行逻辑处理时是如何处理的,请看我的博客《层层递进Struts1(五)之处理流程》。
更多相关博客,请至《层层递进Struts1(八)之总结》。