Spring的启动过程
spring中DispatcherServlet、WebApplicationContext、ServletContext之间的关系
- 首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;
-
其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;
-
再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是mlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。
ContextLoaderListener初始化的上下文和DispatcherServlet初始化的上下文关系,如图3-1
图3-1
从图中可以看出:
ContextLoaderListener初始化的上下文加载的Bean是对于整个应用程序共享的,不管是使用什么表现层技术,一般如DAO层、Service层Bean;
DispatcherServlet初始化的上下文加载的Bean是只对Spring Web MVC有效的Bean,如Controller、HandlerMapping、HandlerAdapter等等,该初始化上下文应该只加载Web相关组件。
3.4、DispatcherServlet初始化顺序
继承体系结构如下所示:
1、HttpServletBean继承HttpServlet,因此在Web容器启动时将调用它的init方法,该初始化方法的主要作用
:::将Servlet初始化参数(init-param)设置到该组件上(如contextAttribute、contextClass、namespace、contextConfigLocation),通过BeanWrapper简化设值过程,方便后续使用;
:::提供给子类初始化扩展点,initServletBean(),该方法由FrameworkServlet覆盖。
- public abstract class HttpServletBean extends HttpServlet implements EnvironmentAware{
- @Override
- public final void init() throws ServletException {
- //省略部分代码
- //1、如下代码的作用是将Servlet初始化参数设置到该组件上
- //如contextAttribute、contextClass、namespace、contextConfigLocation;
- try {
- PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
- BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
- ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
- bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.environment));
- initBeanWrapper(bw);
- bw.setPropertyValues(pvs, true);
- }
- catch (BeansException ex) {
- //…………省略其他代码
- }
- //2、提供给子类初始化的扩展点,该方法由FrameworkServlet覆盖
- initServletBean();
- if (logger.isDebugEnabled()) {
- logger.debug("Servlet '" + getServletName() + "' configured successfully");
- }
- }
- //…………省略其他代码
- }
2、FrameworkServlet继承HttpServletBean,通过initServletBean()进行Web上下文初始化,该方法主要覆盖一下两件事情:
初始化web上下文;
提供给子类初始化扩展点;
- public abstract class FrameworkServlet extends HttpServletBean {
- @Override
- protected final void initServletBean() throws ServletException {
- //省略部分代码
- try {
- //1、初始化Web上下文
- this.webApplicationContext = initWebApplicationContext();
- //2、提供给子类初始化的扩展点
- initFrameworkServlet();
- }
- //省略部分代码
- }
- }
- protected WebApplicationContext initWebApplicationContext() {
- //ROOT上下文(ContextLoaderListener加载的)
- WebApplicationContext rootContext =
- WebApplicationContextUtils.getWebApplicationContext(getServletContext());
- WebApplicationContext wac = null;
- if (this.webApplicationContext != null) {
- // 1、在创建该Servlet注入的上下文
- wac = this.webApplicationContext;
- if (wac instanceof ConfigurableWebApplicationContext) {
- ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
- if (!cwac.isActive()) {
- if (cwac.getParent() == null) {
- cwac.setParent(rootContext);
- }
- configureAndRefreshWebApplicationContext(cwac);
- }
- }
- }
- if (wac == null) {
- //2、查找已经绑定的上下文
- wac = findWebApplicationContext();
- }
- if (wac == null) {
- //3、如果没有找到相应的上下文,并指定父亲为ContextLoaderListener
- wac = createWebApplicationContext(rootContext);
- }
- if (!this.refreshEventReceived) {
- //4、刷新上下文(执行一些初始化)
- onRefresh(wac);
- }
- if (this.publishContext) {
- // Publish the context as a servlet context attribute.
- String attrName = getServletContextAttributeName();
- getServletContext().setAttribute(attrName, wac);
- //省略部分代码
- }
- return wac;
- }
从initWebApplicationContext()方法可以看出,基本上如果ContextLoaderListener加载了上下文将作为根上下文(DispatcherServlet的父容器)。
最后调用了onRefresh()方法执行容器的一些初始化,这个方法由子类实现,来进行扩展。
3、DispatcherServlet继承FrameworkServlet,并实现了onRefresh()方法提供一些前端控制器相关的配置:
- public class DispatcherServlet extends FrameworkServlet {
- //实现子类的onRefresh()方法,该方法委托为initStrategies()方法。
- @Override
- protected void onRefresh(ApplicationContext context) {
- initStrategies(context);
- }
- //初始化默认的Spring Web MVC框架使用的策略(如HandlerMapping)
- protected void initStrategies(ApplicationContext context) {
- initMultipartResolver(context);
- initLocaleResolver(context);
- initThemeResolver(context);
- initHandlerMappings(context);
- initHandlerAdapters(context);
- initHandlerExceptionResolvers(context);
- initRequestToViewNameTranslator(context);
- initViewResolvers(context);
- initFlashMapManager(context);
- }
- }
从如上代码可以看出,DispatcherServlet启动时会进行我们需要的Web层Bean的配置,如HandlerMapping、HandlerAdapter等,而且如果我们没有配置,还会给我们提供默认的配置。
从如上代码我们可以看出,整个DispatcherServlet初始化的过程和做了些什么事情,具体主要做了如下两件事情:
1、初始化Spring Web MVC使用的Web上下文,并且可能指定父容器为(ContextLoaderListener加载了根上下文);
2、初始化DispatcherServlet使用的策略,如HandlerMapping、HandlerAdapter等。