Spring MVC容器的初始化
序言
在一个web应用中,我们经常会配置两个Spring容器,一个是applicationContext.xml;另一个是spring-servlet.xml
applicationContext.xml的初始化是由ContextLoaderListener的initWebApplicationContext触发的。
而spring-servlet.xml的初始化时机是在applicationContext.xml初始化完成后,而MVC容器的初始化是在web容器loadServlet时触发GenericServlet的init(ServletConfig),再接着HttpServletBean中的init 中调用FrameworkServlet中的initServletBean。在该方法中,初始化servlet的子web容器,并将applicationContext创建的容器作为父容器。
Spring容器和Spring MVC容器的不同
- 初始化时机不同,Spring容器优先初始化SpringMVC容器
- Spring容器作为SpringMVC的父容器
- Spring容器的触发是在创建完ServletContext时,触发ContextLoaderListener中的contextInitialized的initWebApplicationContext,而SpringMVC容器的触发是在loadServlet中初始化servletBean中创建子容器的
- Spring子容器通常只管里controller层就可以,千万不要与父容器共管。【目前的项目共管的,因为之前搭建框架的人或者是后来在配置文件配置扫描bean包,导致使用我司的rsf,通过注解@Reference注入服务提供方代理对象,没生效。第一次被注入了【applicationContext容器初始化的时候】,在MVC子容器初始化的时候,该service又被创建了一次,新对象注入的为空,而在spring-servlet.xml中又没有消费方的注解声明,也就没有对应的BeanPostProcessor。】
- 共管的话,会造成启动servlet子容器变慢。因为这一过程会有查找ControllerAdviceBean。这个查找过程是从该子容器下所有的beanName中查找。
/**
* Instantiate the WebApplicationContext for this servlet, either a default
* {@link org.springframework.web.context.support.XmlWebApplicationContext}
* or a {@link #setContextClass custom context class}, if set.
* <p>This implementation expects custom contexts to implement the
* {@link org.springframework.web.context.ConfigurableWebApplicationContext}
* interface. Can be overridden in subclasses.
* <p>Do not forget to register this servlet instance as application listener on the
* created context (for triggering its {@link #onRefresh callback}, and to call
* {@link org.springframework.context.ConfigurableApplicationContext#refresh()}
* before returning the context instance.
* @param parent the parent ApplicationContext to use, or {@code null} if none
* @return the WebApplicationContext for this servlet
* @see org.springframework.web.context.support.XmlWebApplicationContext
*/
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
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 + "]");
}
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");
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
wac.setConfigLocation(getContextConfigLocation());
configureAndRefreshWebApplicationContext(wac);
return wac;
}
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
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());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh();
}
Spring MVC 容器的初始化
初始化流程通过wac.refresh跟applicationContext一样。
Spring MVC子容器都做了哪些初始化工作?
- 创建工厂
- 加载Spring MVC的维持运转的核心BeanDefinition
2.1 RequestMappingHandlerMapping的RootBeanDefinition注册
2.2 ConfigurableWebBindingInitializer的RootBeanDefinition注册
2.2.1 FormattingConversionServiceFactoryBean
2.2.2 OptionalValidatorFactoryBean
2.3 ManagedList,一些消息转换的ByteArrayHttpMessageConverter,
StringHttpMessageConverter,ResourceHttpMessageConverter,
SourceHttpMessageConverter,AllEncompassingFormHttpMessageConverter等。
2.4 ManagedList,参数解析的
2.5 ManagedList,返回值处理的
2.6 RuntimeBeanReference,异步执行器
2.7 ManagedList,回调拦截器
2.8 ManagedList,延迟结果拦截器
2.9 RequestMappingHandlerAdapter的RootBeanDefinition注册
2.10 BeanNameUrlHandlerMapping注册
2.11 HttpRequestHandlerAdapter注册
2.12 SimpleControllerHandlerAdapter注册