- 前言
- 第一部分 核心实现
- 第 1 章 Spring 整体架构和环境搭建
- 第 2 章 容器的基本实现
- 第 3 章 默认标签的解析
- 第 4 章 自定义标签的解析
- 第 5 章 bean 的加载
- 第 6 章 容器的功能扩展
- 第 7 章 AOP
- 第二部分 企业应用
- 第 8 章 数据库连接 JDBC
- 第 9 章 整合 MyBatis
- 第 10 章 事务
- 第 11 章 SpringMVC
- 第 12 章 远程服务
- 第 13 章 Spring 消息
11.3.2 DispatcherServlet 的初始化
通过上面的实例我们了解到,在 servlet 初始化阶段会调用其 init 方法,所以我们首先要查看在 DispatcherServlet 中是否重写了 init 方法。我们在其父类 HttpServletBean 中找到了该方法。
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
try {
//解析 init-param 并封装只 pvs 中
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(),
this.requiredProperties);
//将当前的这个 Servlet 类转化为一个 BeanWrapper,从而能够以 Spring 的方式来对 init-param
的值进行注
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader
(getServletContext());
//注册自定义属性编辑器,一旦遇到 Resource 类型的属性将会使用 ResourceEditor 进行解析
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader,
this.environment));
//空实现,留给子类覆盖
initBeanWrapper(bw);
//属性注入
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName()
+ "'", ex);
throw ex;
}
//留给子类扩展
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
DipatcherServlet 的初始化过程主要是通过将当前的 servlet 类型实例转换为 BeanWrapper 类型实例,以便使用 Spring 中提供的注入功能进行对应属性的注入。这些属性如 contextAttribute、contextClass、nameSpace、contextConfigLocation 等,都可以在 web.xml 文件中以初始化参数的方式配置在 servlet 的声明中。DispatcherServlet 继承自 FrameworkServlet,FrameworkServlet 类上包含对应的同名属性,Spring 会保证这些参数被注入到对应的值中。属性注入主要包含以下几个步骤。
1.封装及验证初始化参数
ServletConfigPropertyValues 除了封装属性外还有对属性验证的功能。
public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
throws ServletException {
Set<String> missingProps = (requiredProperties != null && !requiredProperties.
isEmpty()) ?
new HashSet<String>(requiredProperties) : null;
Enumeration en = config.getInitParameterNames();
while (en.hasMoreElements()) {
String property = (String) en.nextElement();
Object value = config.getInitParameter(property);
addPropertyValue(new PropertyValue(property, value));
if (missingProps != null) {
missingProps.remove(property);
}
}
// Fail if we are still missing properties.
if (missingProps != null && missingProps.size() > 0) {
throw new ServletException(
"Initialization from ServletConfig for servlet '" + config.getServlet
Name() +
"' failed; the following required properties were missing: " +
StringUtils.collectionToDelimitedString(missingProps, ", "));
}
}
从代码中得知,封装属性主要是对初始化的参数进行封装,也就是 servlet 中配置的<init-param>中配置的封装。当然,用户可以通过对 requiredProperties 参数的初始化来强制验证某些属性的必要性,这样,在属性封装的过程中,一旦检测到 requiredProperties 中的属性没有指定初始值,就会抛出异常。
2.将当前 servlet 实例转化成 BeanWrapper 实例
PropertyAccessorFactory.forBeanPropertyAccess 是 Spring 中提供的工具方法,主要用于将指定实例转化为 Spring 中可以处理的 BeanWrapper 类型的实例。
3.注册相对于 Resource 的属性编辑器
属性编辑器,我们在上文中已经介绍并且分析过其原理,这里使用属性编辑器的目的是在对当前实例( DispatcherServlet )属性注入过程中一旦遇到 Resource 类型的属性就会使用 ResourceEditor 去解析。
4.属性注入
BeanWrapper 为 Spring 中的方法,支持 Spring 的自动注入。其实我们最常用的属性注入无非是 contextAttribute、contextClass、nameSpace、contextConfigLocation 等属性。
5.servletBean 的初始化
在 ContextLoaderListener 加载的时候已经创建了 WebApplicationContext 实例,而在这个函数中最重要的就是对这个实例进行进一步的补充初始化。
继续查看 initServletBean()。父类 FrameworkServlet 覆盖了 HttpServletBean 中的 initServlet Bean 函数,如下:
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServlet
Name() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization
started");
}
long startTime = System.currentTimeMillis();
try {
this.webApplicationContext = initWebApplicationContext();
//设计为子类覆盖
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "':
initialization completed in " +
elapsedTime + " ms");
}
}
上面的函数设计了计时器来统计初始化的执行时间,而且提供了一个扩展方法 initFrameworkServlet() 用于子类的覆盖操作,而作为关键的初始化逻辑实现委托给了 initWebApplicationContext()。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论