Spring MVC中的IoC容器初始化
来源:互联网 发布:淘宝网动态口令 编辑:程序博客网 时间:2024/06/06 09:00
Spring Framework本身没有Web功能,Spring MVC使用WebApplicationContext类扩展ApplicationContext,使得拥有web功能。那么,Spring MVC是如何在web环境中创建IoC容器呢?web环境中的IoC容器的结构又是什么结构呢?web环境中,spring IoC容器是怎么启动呢?
先看一下WebApplicationContext是如何扩展ApplicationContext来添加对Web环境的支持的。WebApplicationContext接口定义如下:
- public interface WebApplicationContext extends ApplicationContext {
- //根上下文在ServletContext中的名称
- String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
- //取得web容器的ServletContext
- ServletContext getServletContext();
- }
对于web容器中创建IoC容器的过程,我们从web.xml配置文件讲起。看一下Spring MVC的web.xml中的相关配置:
- <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>
在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的源码:
- /**
- * 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;
- }
- }
再看一下WebApplicationContext对象是如何创建的:
- 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;
- }
以上是web容器中根上下文的加载与初始化,下面介绍一下Spring MVC对应的上下文是如何加载的。
Spring MVC中核心的类是DispatcherServlet,在这个类中完成Spring context的加载与创建,并且能够根据Spring Context的内容将请求分发给各个Controller类。DispatcherServlet继承自HttpServlet,关于Spring Context的配置文件加载和创建是在init()方法中进行的,主要的调用顺序是init-->initServletBean-->initWebApplicationContext。
先来看一下initWebApplicationContext的实现
- /**
- * 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;
- }
通过initWebApplicationContext方法的调用,创建了DispatcherServlet对应的context,并将其放置到ServletContext中,这样就完成了在web容器中构建Spring IoC容器的过程。
最后,在分别给出ContextLoaderListener和DispatcherServlet构建context的时序。
ContextLoaderListener构建Root Context时序图:
DispatcherServlet创建context时序图:
- Spring MVC中的IoC容器初始化
- Spring MVC中的IoC容器初始化
- Spring MVC中的IoC容器初始化
- Spring IoC容器初始化
- Spring Ioc容器初始化
- Spring-IoC容器初始化
- Spring源码阅读之IoC容器初始化3 -- BeanDefinition在IoC容器中的注册
- spring-mvc之ioc容器
- spring 技术内幕--IOC初始化之BeanDefinition的在IOC容器中的注册
- Spring源码学习(二):IOC容器在WEB容器中的初始化
- 模拟spring中的IOC容器
- spring IoC容器初始化简单分析
- SPRING原理解析-Ioc容器初始化
- Spring IOC容器bean初始化源码分析
- Spring IOC源码详解之容器初始化
- Spring 容器IOC的初始化过程
- Spring IOC源码详解之容器初始化
- Spring IOC容器初始化过程分析
- MSDN查询技巧
- ZOJ 3543 Number String dp
- SpringMVC加载WebApplicationContext源码分析
- BZOJ1864【Tree DP】
- hdu1272 小希的迷宫 并查集
- Spring MVC中的IoC容器初始化
- C++编程基础题
- Posix消息队列——mq_notify函数
- WM消息
- SendMessage和PostMessage的区别PostThreadMessage
- IOS 开发进阶--多线程和网络--对于耗时操作的代码体验
- 实习一个月记
- 九度 题目1111:单词替换
- 吊车也能用语音操纵了,挖掘机还会远吗?