`

【Spring源码--IOC容器的实现】(一)Web容器的启动

 
阅读更多

1.由于大家平常用Spring基本都是Web项目中,那么今天就从Web的角度来看看IOC容器是怎么启动并管理Bean的。
2.本文及后续代码版本:Spring3.0.5。所以如发现代码(或图)不一致请注意Spring版本。
3.还是建议大家在前几遍读源码的时候,先把路走通,再回头研究细节。

Web容器接口体系

为了方便在Web环境中使用IOC容器,spring为Web应用提供了上下文接口WebApplicationContext来满足启动过程的需要。这个WebApplicationContext接口的类层次关系如图所示:

在接口设计中,最后是通过ApplicationContext接口与BeanFactory接口对接的,而对于具体功能的实现,很多都是封装在AbstractRefreshableWebApplicationContext中

那么容器启动的入口也就在这里了,context-param字面意思都可以理解,就是ServletContext的参数,这里我们配置了一个xml的路径。我们重点关注listener,这个Spring的ContextLoaderListener实现了ServletContextListener接口,这个接口是在Servlet API中定义的,提供了与Servlet生命周期结合的回调,比如contextInitialized和contextDestroy。那么,作为ServletContext的监听者,如果ServletContext发生变化,监听器就会做出相应的事件。比如:在服务器(tomcat等)启动时,ServletContextListener的contextInitialized方法被调用,服务器将要关闭时,contextDestroy方法被调用。我们来看下ContextLoaderListener的代码:

 

  1. publicclassContextLoaderListenerextendsContextLoaderimplementsServletContextListener{
  2. privateContextLoader contextLoader;
  3. /**
  4. * 初始化容器
  5. * Initialize the root web application context.
  6. */
  7. publicvoid contextInitialized(ServletContextEvent event){
  8. //这个方法实现的本意是提供一个占位符方法createContextLoader()给子类机会创建客户化的环境加载,但是,后来这个证明不是非常有用的,已经鼓励不再使用了,事实上,子类可以通过重写本方法达到同样的目的
  9. this.contextLoader = createContextLoader();
  10. if(this.contextLoader ==null){
  11. this.contextLoader =this;
  12. }
  13. this.contextLoader.initWebApplicationContext(event.getServletContext());
  14. }
  15. /**
  16. * Create the ContextLoader to use. Can be overridden in subclasses.
  17. * @return the new ContextLoader
  18. * @deprecated in favor of simply subclassing ContextLoaderListener itself
  19. * (which extends ContextLoader, as of Spring 3.0)
  20. */
  21. @Deprecated
  22. protectedContextLoader createContextLoader(){
  23. returnnull;
  24. }
  25. /**
  26. * Return the ContextLoader used by this listener.
  27. * @return the current ContextLoader
  28. * @deprecated in favor of simply subclassing ContextLoaderListener itself
  29. * (which extends ContextLoader, as of Spring 3.0)
  30. */
  31. @Deprecated
  32. publicContextLoader getContextLoader(){
  33. returnthis.contextLoader;
  34. }
  35. /**
  36. * 销毁容器的方法
  37. * Close the root web application context.
  38. */
  39. publicvoid contextDestroyed(ServletContextEvent event){
  40. if(this.contextLoader !=null){
  41. this.contextLoader.closeWebApplicationContext(event.getServletContext());
  42. }
  43. ContextCleanupListener.cleanupAttributes(event.getServletContext());
  44. }
  45. }

我们先重点关注容器的启动,也就是contextInitialized方法,从代码中可以看到它是调用的ContextLoader的initWebApplicationContext方法。我们来看下这个方法的代码:

 

  1. publicWebApplicationContext initWebApplicationContext(ServletContext servletContext){
  2. //判断ServletContext中是否已经有根上下文存在,存在抛出异常
  3. if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)!=null){
  4. thrownewIllegalStateException(
  5. "Cannot initialize context because there is already a root application context present - "+
  6. "check whether you have multiple ContextLoader* definitions in your web.xml!");
  7. }
  8. Log logger =LogFactory.getLog(ContextLoader.class);
  9. servletContext.log("Initializing Spring root WebApplicationContext");
  10. if(logger.isInfoEnabled()){
  11. logger.info("Root WebApplicationContext: initialization started");
  12. }
  13. long startTime =System.currentTimeMillis();
  14. try{
  15. //载入双亲上下文(通常是不存在)
  16. ApplicationContext parent = loadParentContext(servletContext);
  17. // 创建WebApplicationContext
  18. this.context = createWebApplicationContext(servletContext, parent);
  19. //把创建的根context保存到Servlet环境中
  20. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,this.context);
  21. //取得线程的类加载器
  22. ClassLoader ccl =Thread.currentThread().getContextClassLoader();
  23. if(ccl ==ContextLoader.class.getClassLoader()){
  24. //如果线程和本类拥有相同的类加载器,则使用静态变量保存即可,因为同一类加载器加载同一份静态变量
  25. currentContext =this.context;
  26. }
  27. elseif(ccl !=null){
  28. //如果线程和本类拥有不同的类加载器,则使用线程的类加载器作为key在保存在一个映射对象里,保证析构时能拿到Webcontext进行关闭操作
  29. currentContextPerThread.put(ccl,this.context);
  30. }
  31. if(logger.isDebugEnabled()){
  32. logger.debug("Published root WebApplicationContext as ServletContext attribute with name ["+
  33. WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE +"]");
  34. }
  35. if(logger.isInfoEnabled()){
  36. long elapsedTime =System.currentTimeMillis()- startTime;
  37. logger.info("Root WebApplicationContext: initialization completed in "+ elapsedTime +" ms");
  38. }
  39. returnthis.context;
  40. }
  41. catch(RuntimeException ex){
  42. logger.error("Context initialization failed", ex);
  43. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
  44. throw ex;
  45. }
  46. catch(Error err){
  47. logger.error("Context initialization failed", err);
  48. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
  49. throw err;
  50. }
  51. }

 

这里我们重点关注createWebApplicationContext(servletContext, parent)这个方法,这里是真正生产容器的地方。至于载入双亲上下文的方法,是根据我们ServletContext的参数取的,很明显,我们在上面的web.xml中只配置了xml的路径,并没有配置双亲上下文。想研究的同学可以去看下代码,这里不再多述。那么我们来看看这个创建容器的方法。
代码1.2:ContextLoader的createWebApplicationContext方法

 

 

  1. protectedWebApplicationContext createWebApplicationContext(ServletContext sc,ApplicationContext parent){
  2. //取得配置的Web应用程序环境类,如果没有配置,则使用缺省的类XmlWebApplicationContext
  3. Class<?> contextClass = determineContextClass(sc);
  4. //如果配置的Web应用程序环境类不是可配置的Web应用程序环境的子类,则抛出异常,停止初始化
  5. if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)){
  6. thrownewApplicationContextException("Custom context class ["+ contextClass.getName()+
  7. "] is not of type ["+ConfigurableWebApplicationContext.class.getName()+"]");
  8. }
  9. //实例化需要产生的IOC容器,并设置容器的各种参数,然后通过refresh启动容器初始化
  10. ConfigurableWebApplicationContext wac =
  11. (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
  12. // 设置IOC容器的的ID
  13. if(sc.getMajorVersion()==2&& sc.getMinorVersion()<5){
  14. // 如果 Servlet规范 <= 2.4,则使用web.xml里定义的应用程序名字定义Web应用程序名
  15. String servletContextName = sc.getServletContextName();
  16. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
  17. ObjectUtils.getDisplayString(servletContextName));
  18. }
  19. else{
  20. // 如果Servlet规范是 2.5, 则使用配置的ContextPath定义Web应用程序名
  21. try{
  22. String contextPath =(String)ServletContext.class.getMethod("getContextPath").invoke(sc);
  23. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
  24. ObjectUtils.getDisplayString(contextPath));
  25. }
  26. catch(Exception ex){
  27. //如果Servlet规范是2.5,但是不能取得ContextPath,抛出异常
  28. thrownewIllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
  29. }
  30. }
  31. //设置双亲上下文
  32. wac.setParent(parent);
  33. //保存Servlet环境
  34. wac.setServletContext(sc);
  35. //这里其实就是设置xml的位置
  36. wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));
  37. //提供子类可互换Web应用程序环境的机会
  38. customizeContext(sc, wac);
  39. //刷新Web应用程序环境以加载Bean定义【重点】
  40. wac.refresh();
  41. return wac;
  42. }

 

 

 

 

 

分享到:
评论

相关推荐

    spring-security-web源码所需jar包

    1. **spring-context-3.1.2.RELEASE.jar**:提供Spring的IoC(Inversion of Control)容器和AOP(Aspect Oriented Programming)支持,这是Spring框架的基础,为Spring Security提供了配置和事件处理能力。...

    官方原版源码spring-framework-5.2.3.RELEASE.zip

    深入学习Spring源码,不仅能够帮助我们理解其工作原理,还能提升我们在实际项目中的问题排查能力。例如,当我们遇到Spring的依赖注入问题时,可以通过查看`BeanFactory`和`ApplicationContext`的相关源码来定位问题...

    spring源码-2022-08-14spring源码spring源码spring源码

    在2022年8月14日更新的Spring源码中,我们可以深入理解其设计理念和实现机制。 Spring框架的主要组成部分包括: 1. **核心容器**:这是Spring的基础,主要包括BeanFactory和ApplicationContext。BeanFactory负责...

    spring-framework-master

    例如,Spring的事件驱动模型、AOP的实现原理、以及IoC容器的内部工作流程等。此外,源码还展示了Spring如何与其他技术(如JDBC、JMS、EJB等)无缝集成,以及如何利用注解简化配置。 通过阅读和分析"spring-...

    Spring源码-interface21

    6. **IoC容器**:Spring的IoC容器是通过`BeanFactory`和`ApplicationContext`接口实现的。这些接口提供了加载配置、创建和管理bean实例的能力。源码中,`DefaultListableBeanFactory`是实际的bean工厂实现,处理bean...

    spring源码spring-framework-4.3.2.RELEASE

    通过对Spring 4.3.2.RELEASE源码的深入研究,我们可以了解其设计理念,学习到如何优雅地管理依赖、实现面向切面编程,以及如何利用Spring构建高效、健壮的Web应用。同时,源码阅读也能帮助我们理解Spring如何与其他...

    官方原版源码spring-framework-5.1.4.RELEASE.zip

    例如,关于IoC(Inversion of Control,控制反转)的实现,Spring使用了XML配置或注解来定义bean的依赖关系,通过反射机制动态加载和管理bean。另外,AOP模块实现了切面编程,允许我们定义横切关注点,如日志、事务...

    官方原版完整包 spring-framework-5.3.1.RELEASE.zip

    2. **spring-beans**: 支持Bean工厂和XML配置,是IoC容器实现的基础。 3. **spring-context**: 扩展了IoC容器,引入了上下文概念,提供了对国际化、事件、资源、缓存等支持。 4. **spring-aop**: 实现了面向切面...

    官方源码 spring-framework-5.3.3.zip

    Spring的IoC容器是其核心功能之一,通过XML或注解配置,它可以管理对象的生命周期和依赖关系。在源码中,`BeanFactory`和`ApplicationContext`接口定义了容器的基本操作,如`getBean`、`refresh`等。`...

    官方源码 spring-framework-5.3.2.zip

    Spring WebFlux模块提供了一个非阻塞的Web堆栈,利用Reactor库实现了反应式编程。 2. **WebFlux增强** 在5.3.2版本中,WebFlux的性能得到了提升,同时增加了对WebSocket和HTTP/2协议的支持。这些改进使得Web应用...

    拿捏面试官-Spring AOP IOC源码笔记

    - **IOC容器**:Spring容器(如BeanFactory或ApplicationContext)负责创建、配置和管理对象,提供了依赖注入的能力。 3. **Spring模块** - Spring分为多个模块,如Core Container(核心容器)、Data Access/...

    Spring-MVC+Spring-IOC+Spring-JdbcTemple

    Spring的IOC容器是其核心特性之一,它负责管理对象的生命周期和依赖关系。开发者定义Bean的配置,而IOC容器负责创建、装配和管理这些Bean。使用XML、注解或者Java配置,可以声明Bean及其依赖。这种方式使得代码更加...

    spring 源码中文注释

    首先,我们来看看Spring的核心组件——IoC容器。IoC容器是Spring的核心,它负责管理对象的生命周期和对象之间的依赖关系。通过XML配置或基于注解的方式,我们可以告诉IoC容器如何创建和装配对象。在源码中,`...

    跟我学spring3-源码.

    4. **Spring容器**:Spring框架的核心是IoC容器,它负责创建、管理和装配对象。Spring容器根据配置文件或Java代码中的定义来实例化对象并管理其生命周期。 5. **Bean的生命周期管理**:Spring容器管理的对象称为...

    Spring3.1.3 Ioc在Web容器中的建立

    标题 "Spring3.1.3 Ioc在Web容器中的建立" 涉及的是Spring框架的一个关键特性——依赖注入(Dependency Injection,简称DI),以及如何在Web应用环境中配置和使用它。Spring是Java开发中最流行的轻量级框架之一,...

    Spring IOC源码解读

    而Spring的IOC通过反转这一过程,将对象的创建和管理交给了IOC容器,从而降低了对象间的耦合度,提高了代码的可测试性和可维护性。 Spring的IOC容器主要有两种类型:BeanFactory和ApplicationContext。BeanFactory...

    官方源码spring-framework-5.0.15.RELEASE.zip

    在源码中,我们可以看到`org.springframework.beans`和`org.springframework.context`包,它们是IoC容器的基础,通过BeanFactory和ApplicationContext实现对象的创建、配置和管理。 2. AOP支持:Spring的AOP模块...

    spring-framework-bom源码

    3. **IoC容器**:Spring的核心是IoC容器,它负责创建对象、管理对象间的依赖关系。在源码中,我们可以找到关于BeanFactory和ApplicationContext的实现,理解它们如何加载配置、实例化和管理Bean。 4. **AOP(面向切...

    spring源码spring-framework-4.2.5.RELEASE

    深入理解Spring源码需要对Java反射、动态代理、设计模式等有扎实的基础。建议从以下几个步骤入手: 1. 了解基本架构和模块划分。 2. 分析核心类如ApplicationContext、BeanFactory和DispatcherServlet的实现。 3. ...

    spring-framework-1.0-m1.zip源码

    本文将深入探讨Spring 1.0源码,帮助读者理解其设计理念和核心机制,以便更好地运用和定制这一经典框架。 一、Spring框架概述 Spring框架是Rod Johnson在2003年推出的,它旨在简化Java企业级应用程序的开发。1.0...

Global site tag (gtag.js) - Google Analytics