Spring的核心在于其IOC和AOP机制,而IOC机制的关键在于Spring中的容器,而Spring中的容器是并非单单指一个独立的对象.根据Spring中组件的职能,做用的不一样,咱们将不一样的组件对象分别注入到两类Context容器中,而且这两类Context容器存在着继承与被继承的关系.前端
在阅读Spring源码或相关文献时,会常常遇到这几个名词:
WebApplicationContext
---ApplicationContext
---ServletContext
---ServletConfig
.这些名词很相近但适用范围有所不一样,容易形成spring
内部实现的理解混淆,因此首先大体解释这几个名词.java
ServletContext
:这个是来自Servlet规范里的概念,它是Servlet
用来与容器间进行交互的接口的组合.也就是,这个接口定义了一系列的方法,Servlet
经过这些方法能够很方便地与所在的容器进行一些交互.从它的定义中也能够看出在一个应用中(一个JVM
)只有一个ServletContext
.也就是说,容器中全部的Servlet
都共享一个ServletContext
.ServletConfig
:它与ServletContext
的区别在于,ServletConfig
是针对servlet
而言的,每一个servlet
都有它独特的ServletConfig
信息,相互之间不共享.ApplicationContext
:这个类是Spring
容器功能的核心接口,它是Spring
实现IOC
功能最重要的接口.从它的名字能够看出,它维护了整个程序运行期所须要的上下文信息,注意这里的应用程序并不必定是web
程序.在Spring
中容许存在多个ApplicationContext
,这些ApplicationContext
相互之间造成父子,继承与被继承的关系,这也是一般咱们所说的:在Spring
中存在两个context
,一个Root Application Context
,一个是Servlet Application Context
,这一点在后续会详细阐述.WebApplicaitonContext
:这个接口只是ApplicationContext
接口的一个子接口,只不过它的应用形式是web
,它在ApplicaitonContext
的基础上,添加了对ServletContext
的引用.
在SpringMVC
配置文件中,咱们一般会配置一个前端控制器DispatcherServlet
和监听器ContextLoaderListener
来进行Spring
应用上下文的初始化
- 在以前的阐述中可知,
ServletContext
是容器中全部Servlet
共享的配置,它是应用于全局的.根据Servlet
规范的规定,根据以上监听器的配置,其中context-param
指定了配置文件的未知.在容器启动后初始化ServletContext
时,监听器会自动加载配置文件,来初始化Spring
的根容器Root Application Context
.- 一样
ServletConfig
是针对每一个Servlet
进步配置的,所以它的配置是在servlet
的配置中,根据以上DispatcherServlet
的配置,配置中init-param
一样指定了在Servlet
初始化调用#init
方法时加载配置信息的xml
文件,并初始化Spring
应用容器Servlet Application Context
.
接下来咱们具体分析Spring容器初始化:web
- 关于
ApplicationContext
的配置,首先,在ServletContext
中配置context-param
参数.经过监听器会生成所谓的Root Application Context
,而每一个DispatcherServlet
中指定的init-param
参数会生成Servlet Application Context
.并且它的parent
就是ServletContext
中生成的Root Application Context
.所以在ServletContext
中定义的全部配置都会继承到DispatcherServlet
中,这在以后代码中会有直观的提现.
1. Root Application Contextspring
public class ContextLoaderListener extends ContextLoader implements ServletContextListener { //................... public void contextInitialized(ServletContextEvent event) { this.initWebApplicationContext(event.getServletContext()); } } -------------------------------------------------------------------------------------------------------------------------------------------------------- public class ContextLoader { //................... public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException("Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!"); } else { //................... try { if(this.context == null) { this.context = this.createWebApplicationContext(servletContext); } //................... servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); return this.context; } } } protected WebApplicationContext createWebApplicationContext(ServletContext sc) { Class<?> contextClass = this.determineContextClass(sc); if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } else { return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); } } protected Class<?> determineContextClass(ServletContext servletContext) { String contextClassName = servletContext.getInitParameter("contextClass"); if(contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } } else { contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } } } static { try { ClassPathResource resource = new ClassPathResource("ContextLoader.properties", ContextLoader.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); } catch (IOException var1) { throw new IllegalStateException("Could not load 'ContextLoader.properties': " + var1.getMessage()); } currentContextPerThread = new ConcurrentHashMap(1); } }
为了方便阅读,这里把一些不是核心的代码过滤segmentfault
- 根据配置文件,首先咱们经过
ContextLoaderListener
对象来监听ServletContext
初始化,在初始化方法中会调用父类ContextLoader#initWebApplicationContext
方法来进行- 在
initWebApplication
中首先判断是否存在Root Application Context
,若是存在则抛出异常.以后经过#createWebApplicationContext
方法来建立容器对象,并会将容器放入ServletContext
中.因此对于ApplicationContext
和ServletContext
的区别就是ApplicationContext
其实就是ServletContext
中的一个属性值而已.这个属性中存有程序运行的全部上下文信息,因为这个ApplicationContext
是全局的应用上下文,因此在Spring
中称它为"Root Application Context"
.- 接下来咱们具体看一下容器是如何建立的:咱们进入到
#createWebApplicationContext
方法中能够看到它是经过实例化容器的class类来建立容器的,而在#determineContextClass
方法中首先经过初始化参数来获取全路径类名,若不存在则经过配置类#defaultStrategies
来获取容器名称.- 咱们能够从当前类的静态代码找到此配置类,它经过读取
ContextLoader.properties
配置文件来获取当前容器全路径类名称
2. Servlet Application Contextapp
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware { public final void init() throws ServletException { //遍历获取servletConfig的全部参数 PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties); //初始化servlet applicationContext this.initServletBean(); } } -------------------------------------------------------------------------------------------------------------------------------------------------------- public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class; public FrameworkServlet() { this.contextClass = DEFAULT_CONTEXT_CLASS; this.contextInitializers = new ArrayList(); ... } protected final void initServletBean() throws ServletException { //................... try { this.webApplicationContext = this.initWebApplicationContext(); } } protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); WebApplicationContext wac = null; if(wac == null) { wac = this.createWebApplicationContext(rootContext); } if(this.publishContext) { String attrName = this.getServletContextAttributeName(); this.getServletContext().setAttribute(attrName, wac); if(this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; } protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { return this.createWebApplicationContext((ApplicationContext)parent); } protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { Class<?> contextClass = this.getContextClass(); if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } else { ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); wac.setEnvironment(this.getEnvironment()); wac.setParent(parent); wac.setConfigLocation(this.getContextConfigLocation()); this.configureAndRefreshWebApplicationContext(wac); return wac; } } protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) { return this.createWebApplicationContext((ApplicationContext)parent); } protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { Class<?> contextClass = this.getContextClass(); if(this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name '" + this.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 '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } else { ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); wac.setEnvironment(this.getEnvironment()); wac.setParent(parent); wac.setConfigLocation(this.getContextConfigLocation()); this.configureAndRefreshWebApplicationContext(wac); return wac; } } }
接下来咱们再看
DispatcherServlet
ui
- 做为
Servlet
,根据规范它的配置信息应该是在#init
方法中完成,咱们首先进入到DispatcherServlet#init
,其方法是继承自父类HttpServletBean
中.在父类的#init
方法中,首先经过遍历获取ServletConfig
的全部参数,而后进行Servlet Application Context
的初始化- 容器初始化方法
#initServletBean
位于父类FrameworkServlet
中,在#initServletBean
方法中调用#initWebApplicationContex
t方法initWebApplicationContext
方法中首先经过ServletContext
获取Root Application Context
,而后经过#createWebApplicationContext
开始初始化Servlet Application Context
,其中获取的#createWebApplicationContext
中获取的class对象在构造方法中进行初始化的.在建立容器的过程会传入Root Application Context
做为它的Parent
,也就是在这里二者创建父子关系,造成以前所说的继承关系,最后一样将新建立的容器放入ContextServlet
中.
因此在
Spring
中存在两类context
,一类Root Application Context
,一类是Servlet Application Context
,前者根容器是惟一的,是其余全部Servlet应用容器的父类.
以上就是关于在Spring
中容器的大体分析,咱们会在项目启动时将不一样的组件实例注入到Spring
容器中,从而实现Spring IOC
机制.this
- 若想要了解如何经过
Spring
注解方式自定义DispatcherServlet
相关内容能够参考:Spring中关于的WebApplicationInitializer及其实现的分析- 若想要了解关于
SpringMVC
自定义配置化相关知识能够参考: