0
点赞
收藏
分享

微信扫一扫

Spring MVC中的IoC容器初始化


 Spring Framework本身没有Web功能,Spring MVC使用WebApplicationContext类扩展ApplicationContext,使得拥有web功能。那么,Spring MVC是如何在web环境中创建IoC容器呢?web环境中的IoC容器的结构又是什么结构呢?web环境中,spring IoC容器是怎么启动呢?

      先看一下WebApplicationContext是如何扩展ApplicationContext来添加对Web环境的支持的。WebApplicationContext接口定义如下:

 



[java] view plain copy print ?



1. public interface WebApplicationContext extends
2. //根上下文在ServletContext中的名称
3. class.getName() + ".ROOT";  
4. //取得web容器的ServletContext
5.     ServletContext getServletContext();  
6. }


 

      对于web容器中创建IoC容器的过程,我们从web.xml配置文件讲起。看一下Spring MVC的web.xml中的相关配置:

 



[xhtml] view plain copy print ?



1. <context-param>
2. <param-name>contextConfigLocation</param-name>
3. <param-value>/WEB-INF/applicationContext.xml</param-value>
4. </context-param>
5.       
6. <listener>
7. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
8. </listener>
9.   
10. <!-- Handles all requests into the application -->
11. <servlet>
12. <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
13. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
14. <init-param>
15. <param-name>contextConfigLocation</param-name>
16. <param-value>
17.                 /WEB-INF/spring/*.xml  
18. </param-value>
19. </init-param>
20. <load-on-startup>1</load-on-startup>
21. </servlet>
22.           
23. <!-- Maps all /app requests to the DispatcherServlet for handling -->
24. <servlet-mapping>
25. <servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
26. <url-pattern>/app/*</url-pattern>
27. </servlet-mapping>


 

      在web.xml配置文件中,有两个主要的配置:ContextLoaderListener和DispatcherServlet。同样的关于spring配置文件的相关配置也有两部分:context-param和DispatcherServlet中的init-param。那么,这两部分的配置有什么区别呢?它们都担任什么样的职责呢?

      在Spring MVC中,Spring Context是以父子的继承结构存在的。Web环境中存在一个ROOT Context,这个Context是整个应用的根上下文,是其他context的双亲Context。同时Spring MVC也对应的持有一个独立的Context,它是ROOT Context的子上下文。

      对于这样的Context结构在Spring MVC中是如何实现的呢?下面就先从ROOT Context入手,ROOT Context是在ContextLoaderListener中配置的,ContextLoaderListener读取context-param中的contextConfigLocation指定的配置文件,创建ROOT Context。下面看一下ContextLoaderListener中创建context的源码:

 



[java] view plain copy print ?



1. /**
2.      * Initialize Spring's web application context for the given servlet context,
3.      * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
4.      * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
5.      * @param servletContext current servlet context
6.      * @return the new WebApplicationContext
7.      * @see #CONTEXT_CLASS_PARAM
8.      * @see #CONFIG_LOCATION_PARAM
9.      */
10. public
11. //PS : ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE=WebApplicationContext.class.getName() + ".ROOT" 根上下文的名称
12. //PS : 默认情况下,配置文件的位置和名称是: DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml"
13. //在整个web应用中,只能有一个根上下文
14. if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {  
15. throw new
16. "Cannot initialize context because there is already a root application context present - "
17. "check whether you have multiple ContextLoader* definitions in your web.xml!");  
18.         }  
19.   
20. class);  
21. "Initializing Spring root WebApplicationContext");  
22. if
23. "Root WebApplicationContext: initialization started");  
24.         }  
25. long
26.   
27. try
28. // Determine parent for root web application context, if any.
29.             ApplicationContext parent = loadParentContext(servletContext);  
30.   
31. // Store context in local instance variable, to guarantee that
32. // it is available on ServletContext shutdown.
33. // 在这里执行了创建WebApplicationContext的操作
34. this.context = createWebApplicationContext(servletContext, parent);  
35.   
36. //PS: 将根上下文放置在servletContext中
37. this.context);  
38.   
39.             ClassLoader ccl = Thread.currentThread().getContextClassLoader();  
40. if (ccl == ContextLoader.class.getClassLoader()) {  
41. this.context;  
42.             }  
43. else if (ccl != null) {  
44. this.context);  
45.             }  
46.   
47. if
48. "Published root WebApplicationContext as ServletContext attribute with name ["
49. "]");  
50.             }  
51. if
52. long
53. "Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");  
54.             }  
55.   
56. return this.context;  
57.         }  
58. catch
59. "Context initialization failed", ex);  
60.             servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);  
61. throw
62.         }  
63. catch
64. "Context initialization failed", err);  
65.             servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);  
66. throw
67.         }  
68.     }



 

        再看一下WebApplicationContext对象是如何创建的:

 



[java] view plain copy print ?



1. protected
2. //根据web.xml中的配置决定使用何种WebApplicationContext。默认情况下使用XmlWebApplicationContext
3. //web.xml中相关的配置context-param的名称“contextClass”
4.         Class<?> contextClass = determineContextClass(sc);  
5. if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {  
6. throw new ApplicationContextException("Custom context class ["
7. "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");  
8.         }  
9.   
10. //实例化WebApplicationContext的实现类
11.         ConfigurableWebApplicationContext wac =  
12.                 (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);  
13.   
14. // Assign the best possible id value.
15. if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {  
16. // Servlet <= 2.4: resort to name specified in web.xml, if any.
17.             String servletContextName = sc.getServletContextName();  
18. if (servletContextName != null) {  
19.                 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName);  
20.             }  
21. else
22.                 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX);  
23.             }  
24.         }  
25. else
26. // Servlet 2.5's getContextPath available!
27.             wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + sc.getContextPath());  
28.         }  
29.   
30.         wac.setParent(parent);  
31.   
32.         wac.setServletContext(sc);  
33. //设置spring的配置文件
34.         wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));  
35.         customizeContext(sc, wac);  
36. //spring容器初始化
37.         wac.refresh();  
38. return
39.     }



 

 

       以上是web容器中根上下文的加载与初始化,下面介绍一下Spring MVC对应的上下文是如何加载的。

     

       Spring MVC中核心的类是DispatcherServlet,在这个类中完成Spring context的加载与创建,并且能够根据Spring Context的内容将请求分发给各个Controller类。DispatcherServlet继承自HttpServlet,关于Spring Context的配置文件加载和创建是在init()方法中进行的,主要的调用顺序是init-->initServletBean-->initWebApplicationContext。

       先来看一下initWebApplicationContext的实现

 



[java] view plain copy print ?



    1. /**
    2.      * Initialize and publish the WebApplicationContext for this servlet.
    3.      * <p>Delegates to {@link #createWebApplicationContext} for actual creation
    4.      * of the context. Can be overridden in subclasses.
    5.      * @return the WebApplicationContext instance
    6.      * @see #setContextClass
    7.      * @see #setContextConfigLocation
    8.      */
    9. protected
    10. //先从web容器的ServletContext中查找WebApplicationContext
    11.         WebApplicationContext wac = findWebApplicationContext();  
    12. if (wac == null) {  
    13. // No fixed context defined for this servlet - create a local one.
    14. //从ServletContext中取得根上下文
    15.             WebApplicationContext parent =  
    16.                     WebApplicationContextUtils.getWebApplicationContext(getServletContext());  
    17. //创建Spring MVC的上下文,并将根上下文作为起双亲上下文
    18.             wac = createWebApplicationContext(parent);  
    19.         }  
    20.   
    21. if (!this.refreshEventReceived) {  
    22. // Apparently not a ConfigurableApplicationContext with refresh support:
    23. // triggering initial onRefresh manually here.
    24.             onRefresh(wac);  
    25.         }  
    26.   
    27. if (this.publishContext) {  
    28. // Publish the context as a servlet context attribute.
    29. // 取得context在ServletContext中的名称
    30.             String attrName = getServletContextAttributeName();  
    31. //将Spring MVC的Context放置到ServletContext中
    32.             getServletContext().setAttribute(attrName, wac);  
    33. if (this.logger.isDebugEnabled()) {  
    34. this.logger.debug("Published WebApplicationContext of servlet '"
    35. "' as ServletContext attribute with name [" + attrName + "]");  
    36.             }  
    37.         }  
    38.   
    39. return
    40.     }


     

            通过initWebApplicationContext方法的调用,创建了DispatcherServlet对应的context,并将其放置到ServletContext中,这样就完成了在web容器中构建Spring IoC容器的过程。

     

            最后,在分别给出ContextLoaderListener和DispatcherServlet构建context的时序。

     

            ContextLoaderListener构建Root Context时序图:

     

             DispatcherServlet创建context时序图:

     

     

     

     public interface WebApplicationContext extends ApplicationContext { //根上下文在ServletContext中的名称 String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; //取得web容器的ServletContext ServletContext getServletContext(); }<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Handles all requests into the application --> <servlet> <servlet-name>Spring MVC Dispatcher Servlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/*.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Maps all /app requests to the DispatcherServlet for handling --> <servlet-mapping> <servlet-name>Spring MVC Dispatcher Servlet</servlet-name> <url-pattern>/app/*</url-pattern> </servlet-mapping>/** * Initialize Spring's web application context for the given servlet context, * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params. * @param servletContext current servlet context * @return the new WebApplicationContext * @see #CONTEXT_CLASS_PARAM * @see #CONFIG_LOCATION_PARAM */ public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { //PS : ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE=WebApplicationContext.class.getName() + ".ROOT" 根上下文的名称 //PS : 默认情况下,配置文件的位置和名称是: DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml" //在整个web应用中,只能有一个根上下文 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!"); } Log logger = LogFactory.getLog(ContextLoader.class); servletContext.log("Initializing Spring root WebApplicationContext"); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } long startTime = System.currentTimeMillis(); try { // Determine parent for root web application context, if any. ApplicationContext parent = loadParentContext(servletContext); // Store context in local instance variable, to guarantee that // it is available on ServletContext shutdown. // 在这里执行了创建WebApplicationContext的操作 this.context = createWebApplicationContext(servletContext, parent); //PS: 将根上下文放置在servletContext中 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } if (logger.isDebugEnabled()) { logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); } return this.context; } catch (RuntimeException ex) { logger.error("Context initialization failed", ex); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } catch (Error err) { logger.error("Context initialization failed", err); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); throw err; } }protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) { //根据web.xml中的配置决定使用何种WebApplicationContext。默认情况下使用XmlWebApplicationContext //web.xml中相关的配置context-param的名称“contextClass” Class<?> contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } //实例化WebApplicationContext的实现类 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); // Assign the best possible id value. if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) { // Servlet <= 2.4: resort to name specified in web.xml, if any. String servletContextName = sc.getServletContextName(); if (servletContextName != null) { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX); } } else { // Servlet 2.5's getContextPath available! wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + sc.getContextPath()); } wac.setParent(parent); wac.setServletContext(sc); //设置spring的配置文件 wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM)); customizeContext(sc, wac); //spring容器初始化 wac.refresh(); return wac; }/** * Initialize and publish the WebApplicationContext for this servlet. * <p>Delegates to {@link #createWebApplicationContext} for actual creation * of the context. Can be overridden in subclasses. * @return the WebApplicationContext instance * @see #setContextClass * @see #setContextConfigLocation */ protected WebApplicationContext initWebApplicationContext() { //先从web容器的ServletContext中查找WebApplicationContext WebApplicationContext wac = findWebApplicationContext(); if (wac == null) { // No fixed context defined for this servlet - create a local one. //从ServletContext中取得根上下文 WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); //创建Spring MVC的上下文,并将根上下文作为起双亲上下文 wac = createWebApplicationContext(parent); } if (!this.refreshEventReceived) { // Apparently not a ConfigurableApplicationContext with refresh support: // triggering initial onRefresh manually here. onRefresh(wac); } if (this.publishContext) { // Publish the context as a servlet context attribute. // 取得context在ServletContext中的名称 String attrName = getServletContextAttributeName(); //将Spring MVC的Context放置到ServletContext中 getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }


     

    举报

    相关推荐

    0 条评论