SpringMVC中的Servlet一共有三层:HttpServletBean、FrameworkServlet和DispatcherServlet。
HttpServletBean直接继承于Java的HttpServlet,其作用是将Servlet中配置的参数设置到相应的属性。该类实现Servlet生命周期中的init方法,但并未实现destroy方法。
FrameworkServlet初始化了WebApplicationContext。并且实现了Servlet生命周期中的destroy方法,在该方法中调用了Servlet中的IoC容器的close方法。
DispatcherServlet初始化了自身的9个组件,并在运行期负责请求的分发。
HttpServletBean
HttpServletBean是SpringWeb模块中基于Servlet最基础的Bean。其继承于HttpServlet。
在其实现的init方法中,完成SpringWeb模块中IoC容器的初始化,该方法是一个抽象方法,具体的实现是由FrameworkServlet完成的。
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// 根据初始化参数来设置Bean属性
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 根据HttpServletBean来创建BeanWrapper
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
// 创建资源加载器
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
// 注册属性编辑器
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
// 设置Bean属性元数据
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// 该方法在HttpServletBean中并未实现,而是由FrameworkServlet实现.
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
FrameworkServlet
FrameworkServlet最重要的作用便是负责Servlet中的IoC容器的初始化。在其实现的initServletBean方法中,通过调用initWebApplicationContext方法来完成Web容器的初始化工作。
// FrameworkServlet#initWebApplicationContext
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
// 初始化Web应用上下文
this.webApplicationContext = initWebApplicationContext();
// 空方法,留给子类实现,DispatcherServlet并未实现该方法
initFrameworkServlet();
} catch (ServletException | 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");
}
}
在initWebApplicationContext方法中完成了子容器的创建工作。首先从ServletContext中获取到根容器,获取的key为WebApplicationContext的全限定名加上.ROOT。
protected WebApplicationContext initWebApplicationContext() {
// 父容器是由ContextLoaderListener来创建的,所以如果未在web.xml中配置,那么将没有父容器,只有一个子容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
// 第一次执行该方法时 wac 是等于null的。
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
// 尝试从ServletContext中获取WebApplicationContext ,通常获取不到
if (wac == null) {
wac = findWebApplicationContext();
}
if (wac == null) {
// 这里才是真正创建WebApplicationContext
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
onRefresh(wac);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
// WebApplicationContextUtils#getWebApplicationContext
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}
在createWebApplicationContext方法中,首先通过getContextClass方法来获取要创建的上下文容器类型,如果未指定,默认为XmlWebApplicationContext。
// public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
// private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
// 获取要创建的Web容器类型
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
// 如果要创建的Web容器类型不是ConfigurableWebApplicationContext类型则抛出异常
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
// 使用反射来实例化Web容器
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
// 设置环境变量
wac.setEnvironment(getEnvironment());
// 设置父容器
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
// 设置配置文件路径
wac.setConfigLocation(configLocation);
}
// 配置并刷新Web容器
configureAndRefreshWebApplicationContext(wac);
return wac;
}
在configureAndRefreshWebApplicationContext方法中值的注意的就是手动添加的ApplicationListener-SourceFilteringListener。在创建SourceFilterListener时,传入了一个ContextRefreshListener实例。DispatcherServlet的onRefresh方法就是由该监听器的onApplicationEvent方法调用的。
因为在Spring应用上下文中事件机制中事件具有层次传播性(子类的应用上下文事件可以传播到父应用上下文中),因此如果不做任何措施,那么相同的事件可能被重复消费。如何解决事件层次传播问题,无疑SourceFilteringListener极具有参考价值。
// FrameworkServlet#configureAndRefreshWebApplicationContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
// 设置Web应用上下文ID
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
// 将ServletContext设置到Web应用上下文中
wac.setServletContext(getServletContext());
// 将ServletConfig设置到Web应用上下文中
wac.setServletConfig(getServletConfig());
// 将Namespace设置到Web应用上下文中
wac.setNamespace(getNamespace());
// 添加ApplicationContextListener到Web应用上下文中
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// 将ServletContext以及ServletConfig设置到Environment实例中
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
// 扩展方法,留给子类实现
postProcessWebApplicationContext(wac);
// 应用ApplicationContextInitializer
applyInitializers(wac);
// 调用大名鼎鼎的refresh方法
wac.refresh();
}
这里先简单看下SourceFilteringListener的onApplicationEvent方法实现。其处理事件层次性传播很简单,就是通过ApplicationEvent的getSource方法返回的事件源对象(Spring 应用上下文)是否与创建该实例时指定的应用上下文是否相等,如果相对则处理事件,否则不处理。
// SourceFilteringListener#onApplicationEvent
public void onApplicationEvent(ApplicationEvent event) {
if (event.getSource() == this.source) {
onApplicationEventInternal(event);
}
}
// FrameworkServlet#onApplicationEvent
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
onRefresh(event.getApplicationContext());
}
// FrameworkServlet#onRefresh
protected void onRefresh(ApplicationContext context) {
// For subclasses: do nothing by default.
}
在onApplicationEventInternal方法中直接调用了创建SourceFilteringListener时指定的ApplicationListener实例的onApplicationEvent方法,这个实例为ContextRefreshListener(不明白的同学可以看看上面的configureAndRefreshWebApplicationContext方法)。
// SourceFilteringListener#onApplicationEventInternal
protected void onApplicationEventInternal(ApplicationEvent event) {
if (this.delegate == null) {
throw new IllegalStateException(
"Must specify a delegate object or override the onApplicationEventInternal method");
}
this.delegate.onApplicationEvent(event);
}
ContextRefreshListener是FrameworkServlet的一个私有内部类,在其实现的onApplicationEvent方法中,直接调用了FrameworkServlet的onApplicationEvent方法。而DispatcherServlet重写了该方法。
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}
DispatcherServlet
DispatcherServlet通过监听容器刷新完毕的事件来完成SpringMVC九大组件的初始化。在其实现父类FramworkServlet的onRefresh方法中调用了initStrategies方法。
// DispatcherServlet#onRefresh
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
// DispatcherServlet#initStrategies
protected void initStrategies(ApplicationContext context) {
// 初始化多媒体解析器
initMultipartResolver(context);
// 初始化本地化解析器
initLocaleResolver(context);
// 初始化主题解析器
initThemeResolver(context);
// 初始化请求处理器映射器
initHandlerMappings(context);
// 初始化请求处理器适配器
initHandlerAdapters(context);
// 初始化异常解析器
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
// 初始化视图解析器
initViewResolvers(context);
initFlashMapManager(context);
}