Spring IoC容器与应用上下文的设计与实现
SpringBoot系列文章简介
SpringBoot源码阅读辅助篇:
SpringBoot启动流程源码分析:
- SpringBoot启动流程分析(一):SpringApplication类初始化过程
- SpringBoot启动流程分析(二):SpringApplication的run方法
- SpringBoot启动流程分析(三):SpringApplication的run方法之prepareContext()方法
- SpringBoot启动流程分析(四):IoC容器的初始化过程
- SpringBoot启动流程分析(五):SpringBoot自动装配原理实现
- SpringBoot启动流程分析(六):IoC容器依赖注入
笔者注释版Spring Framework与SpringBoot源码git传送门:请不要吝啬小星星
一、前言
写这篇博文的主要目的如下:
- 通过相关类和接口分析IoC容器到底长什么样。
- 阐述笔者对Spring上下文和容器的理解。
- 介绍重要的类辅助理解SpringBoot的启动流程。
二、Spring IoC容器的设计
看看下面这张图(摘自《Spring技术内幕》),IoC容器的设计分为两条线,
- BeanFactory ==> HierarchicalBeanFactory ==>ConfigurableBeanFactory ,这条线可以理解成IoC容器的设计路线。
- BeanFactory ==> ListableBeanFactory ==> ApplicationContext ==> ConfigurableApplicationContext ,这条可以成为Spring应用上下文的设计路线。
为什么这样要分两条线呢,主要是将容器和上下文区分开来。因为在在Spring项目中,上下文对容器不仅是扩展的关系,更重要的是持有的关系,上下文是以属性的形式持有了容器,开发者可以通过上下文对象获取到容器。笔者十分倾向于将二者分开来理解。当然也可以将应用上下文理解成容器的高级表现形式。
2.1,IoC容器的设计线路
BeanFactory定义了IoC容器的基本规范,包括getBean()按类型和按名称的获取Bean的方法。
HierarchicalBeanFactory 在BeanFactory的基础上增加了getParentBeanFactory()方法,使BeanFactory具备了双亲IoC容器管理的功能。
ConfigurableBeanFactory接口提供了配置BeanFactory的各种方法。比如setParentBeanFactory()方法,配置上面提到的双亲IoC容器,addBeanPostProcessor()方法,配置Bean后置处理器等。
到这里先埋个包袱:到ConfigurableBeanFactory接口为止,IoC容器还没有具备作为“容器”最基本的功能,那就是能装东西。
2.2、应用上下文设计路线
上面说了应用上下文是IoC容器的高级表现形式,ListableBeanFactory具备了操作BeanDefinition 的能力,比如getBeanDefinitionCount()方法,可以获取Bean的总数等。
ApplicationContext 类那就厉害了,如下代码所示,实现了一大堆接口
1 public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, 2 MessageSource, ApplicationEventPublisher, ResourcePatternResolver
- MessageSource,支持不同的信息源。具备支持国际化的实现,为开发多语言版本的应用提供服务。
- ResourcePatternResolver,访问数据源。具备了从不同地方得到Bean定义资源的能力,比如:xml,java config,注解等等。
- ApplicationEventPublisher,发布事件。使应用上下文具备了事件机制。事件机制为Bean声明周期的管理提供了便利。
WebApplicationContext扩展了对web应用的支持。
ConfigurableApplicationContext就更重要了,看过Spring源码的都知道一个重要的方法叫refresh,没错就是在这个接口中定义的。最重要的是扩展了配置上下文的功能,和控制上下文生命周期的能力等等。
三、IoC容器的具体实现类 DefaultListableBeanFactory(重点)
首先证明一点,为什么说DefaultListableBeanFactory类是具体的实现类呢?
随便启动一个SpringBoot项目找到第25行代码(在SpringBoot的启动流程系列博文中有介绍)
1 public ConfigurableApplicationContext run(String... args) { 2 //记录程序运行时间 3 StopWatch stopWatch = new StopWatch(); 4 stopWatch.start(); 5 // ConfigurableApplicationContext Spring 的上下文 6 ConfigurableApplicationContext context = null; 7 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); 8 configureHeadlessProperty(); 9 //从META-INF/spring.factories中获取监听器 10 //1、获取并启动监听器 11 SpringApplicationRunListeners listeners = getRunListeners(args); 12 listeners.starting(); 13 try { 14 ApplicationArguments applicationArguments = new DefaultApplicationArguments( 15 args); 16 //2、构造容器环境 17 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); 18 //处理需要忽略的Bean 19 configureIgnoreBeanInfo(environment); 20 //打印banner 21 Banner printedBanner = printBanner(environment); 22 ///3、初始化容器 23 context = createApplicationContext(); 24 //实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误 25 exceptionReporters = getSpringFactoriesInstances( 26 SpringBootExceptionReporter.class, 27 new Class[]{ConfigurableApplicationContext.class}, context); 28 //4、刷新容器前的准备阶段 29 prepareContext(context, environment, listeners, applicationArguments, printedBanner); 30 //5、刷新容器 31 refreshContext(context); 32 //刷新容器后的扩展接口 33 afterRefresh(context, applicationArguments); 34 stopWatch.stop(); 35 if (this.logStartupInfo) { 36 new StartupInfoLogger(this.mainApplicationClass) 37 .logStarted(getApplicationLog(), stopWatch); 38 } 39 listeners.started(context); 40 callRunners(context, applicationArguments); 41 } catch (Throwable ex) { 42 handleRunFailure(context, ex, exceptionReporters, listeners); 43 throw new IllegalStateException(ex); 44 } 45 46 try { 47 listeners.running(context); 48 } catch (Throwable ex) { 49 handleRunFailure(context, ex, exceptionReporters, null); 50 throw new IllegalStateException(ex); 51 } 52 return context; 53 }
debug
如2标注点所示IoC容器的真实面孔就是这个DefaultListableBeanFactory类了。当然他还有一个子类XmlBeanFactory,不过都已经被标注为弃用了(@Deprecated)在《Spring技术内幕》这本书里面也是着重的讲的这个类,可能当时作者是以SpringMVC项目来讲解的吧。XmlBeanFactory顾名思义就是提供了对xml配置方式的支持。呃。。。又勾起了用SpringMVC的痛苦回忆。
言归正传,
如下图,看看他的继承关系
章节二中提到了很多的IoC容器系列,这样总结一下吧,俗话说一流企业做标准,二流企业做产品,章节二中的那一坨就是IoC容器的实现标准,本章节我们要总结的类DefaultListableBeanFactory就是IoC容器的具体产品。
看见上图中那一堆接口和类是不是有点懵,没关系,咱们慢慢梳理一下。
3.1,作为IoC容器的基础设计路线
这条线路在上一章节中已经梳理过了。只是多出了ConfigurableListableBeanFactory接口,ConfigurableListableBeanFactory接口主要是增加指定忽略类型和接口等
3.2、作为IoC容器的高级设计路线
这条设计路线乍一看还是挺复杂的,的确是这样。
1, BeanFactory ==> AutowireCapableBeanFactory ==> AbstractAutowireCapableBeanFactory ==> DefaultListableBeanFactory
在这条线路中,AutowireCapableBeanFactory接口定义了自动注入bean(autowireBean()),创建bean(createBean()),初始化bean(initializeBean())方法等。那么真正实现这些方法的类便是AbstractAutowireCapableBeanFactory。
AbstractAutowireCapableBeanFactory抽象类中实现了AutowireCapableBeanFactory接口定义的方法。在此基础上通过继承AbstractBeanFactory具备了操作Bean的能力。
2, SingletonBeanRegistry ==> DefaultSingletonBeanRegistry ==> FactoryBeanRegistrySupport ==> AbstractBeanFactory ==> AutowireCapableBeanFactory ==> DefaultListableBeanFactory
这条关系链有点长,在这条链中我们要关心的是SingletonBeanRegistry接口,顾名思义,这个接口是单例Bean的注册接口。当然也不止注册这么简单。如下图中所示,除了注册单例之外,还定义获取单例的方法。
注意:为什么只有singleton的注册中心,而没有prototype类型的Bean的注册中心呢?因为单例Bean(singleton)是Spring帮我们创建的并维护的,原型Bean(prototype)是每次获取都会创建出来一个实例。本质是不同的。
3, AliasRegistry ==> SimpleAliasRegistry ==> DefaultSingletonBeanRegistry ==> FactoryBeanRegistrySupport ==> AbstractBeanFactory ==> AutowireCapableBeanFactory ==> DefaultListableBeanFactory
这条路线呢,主要是提供管理别称的能力。因为不是重点,在此就不详细分析了。
4, AliasRegistry ==> BeanDefinitionRegistry ==> DefaultListableBeanFactory
BeanDefinitionRegistry接口要重点说一下,该接口是BeanDefinition的注册中心。使DefaultListableBeanFactory具备操作BeanDefinition的能力。看一下它有什么方法。
包括了注册,删除,获取BeanDefinition的方法。当然这只是个接口,这些方法的具体实现在DefaultListableBeanFactory中。
3.3、DefaultListableBeanFactory几个重要的父类和接口
3.3.1, AbstractBeanFactory 抽象类
如上图所示,AbstractBeanFactory中实现了BeanFactory中定义的几个重要的方法。常用的注解 @Autowired @Resource(name = "xxx") 大家都知道一个是按类查找,一个是按名获取。具体实现这两个注解的方法就是上图中圈出来的几个方法。几个getBean()方法最终都进入了doGetBean()方法。doGetBean()方法是实际获得Bean的地方,也是触发依赖注入发生的地方。在SpringBoot启动流程总会对这个方法进行详细的介绍。
3.3.2, AbstractAutowireCapableBeanFactory 抽象类
AbstractBeanFactory中实现了getBean()方法,AbstractAutowireCapableBeanFactory中实现了Bean的创建方法。
当我们需要定义一个Bean通常会有这样写 @Bean(name = "test", initMethod = "init", destroyMethod = "destroy") 。AbstractAutowireCapableBeanFactory中完成了一个Bean从 create(createBean()) ==> createInstance(createBeanInstance()) ==> init(invokeInitMethods()) 的所有工作。所以这个抽象类的作用不言而喻。具体的创建过程,会在SpringBoot的启动流程中详细介绍。
3.3.3, DefaultSingletonBeanRegistry 让IoC容器拥有作为“容器”的能力
其实我们经常说的Spring 容器,这个容器其实更多的是BeanFactory所代表的意义:Bean生产工厂。是滴,通常我们理解的容器应该是能装东西的,但是spring 容器不是代表代表水桶一样的东西,而是像富士康一样,生产东西的地方。比如我们需要一个Bean,我们只需要告诉spring,spring就会给我们。所以到目前为止我们还没有看到IoC作为“容器”的能力。以上言论纯属自己理解,不喜勿喷。
DefaultSingletonBeanRegistry属性先贴出来
1 public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { 2 /** 3 * Cache of singleton objects: bean name --> bean instance 4 * 缓存 单例对象 5 */ 6 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);//好习惯,创建ConcurrentHashMap,指定初始化因子,纵观Spring源码,创建HashMap,都有初始化因子。get 7 8 /** 9 * Cache of singleton factories: bean name --> ObjectFactory 10 * 缓存 单例工厂 11 */ 12 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); 13 14 /** 15 * Cache of early singleton objects: bean name --> bean instance 16 * 缓存 提前暴露的对象 17 */ 18 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); 19 20 /** 21 * Set of registered singletons, containing the bean names in registration order 22 * 已经注册的单例对象集合,按照注册顺序排序的,并且是不可重复的。 23 */ 24 private final Set<String> registeredSingletons = new LinkedHashSet<>(256); 25 26 /** 27 * Names of beans that are currently in creation 28 * 29 */ 30 private final Set<String> singletonsCurrentlyInCreation = 31 Collections.newSetFromMap(new ConcurrentHashMap<>(16)); 32 33 /** 34 * Names of beans currently excluded from in creation checks 35 */ 36 private final Set<String> inCreationCheckExclusions = 37 Collections.newSetFromMap(new ConcurrentHashMap<>(16)); 38 39 /** 40 * List of suppressed Exceptions, available for associating related causes 41 */ 42 @Nullable 43 private Set<Exception> suppressedExceptions; 44 45 /** 46 * Flag that indicates whether we're currently within destroySingletons 47 */ 48 private boolean singletonsCurrentlyInDestruction = false; 49 50 /** 51 * Disposable bean instances: bean name --> disposable instance 52 * spring是作为一个注册中心的样子,在容器shutdown的时候,直接从这里面找到需要执行destory钩子的Bean 53 */ 54 private final Map<String, Object> disposableBeans = new LinkedHashMap<>(); 55 56 /** 57 * Map between containing bean names: bean name --> Set of bean names that the bean contains 58 * 名称为name的bean,所持有的beans 的映射关系 59 */ 60 private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16); 61 62 /** 63 * Map between dependent bean names: bean name --> Set of dependent bean names 64 * 名称为name的bean与其所依赖的bean的映射关系 65 */ 66 private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64); 67 68 /** 69 * Map between depending bean names: bean name --> Set of bean names for the bean's dependencies 70 */ 71 private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64); 72 }
属性singletonObjects ,没错,就是这个东东,最终存储单例(singleton)Bean的地方,在SpringBoot启动流程中,会详细介绍存取的过程。上面说了原型(prototype)Bean是不需要缓存的,不解释了。到这里我们初步看到了IoC作为“容器”该有的样子。
DefaultSingletonBeanRegistry上面提到的SingletonBeanRegistry接口的相关方法,并且增加了很多对单例的操作的方法。
3.3.4, DefaultListableBeanFactory (重点)
上面我们从IoC容器的宏观设计角度阐述了DefaultListableBeanFactory作为IoC容器的具体实现的设计思想。在这里来分析一下这个类本身的设计。
首先看看该类的属性
1 public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory 2 implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { 3 /** 4 * Map from serialized id to factory instance 5 * 缓存 序列化ID到 DefaultListableBeanFactory 实例的映射 6 */ 7 private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories = 8 new ConcurrentHashMap<>(8); 9 10 /** 11 * Optional id for this factory, for serialization purposes 12 */ 13 @Nullable 14 private String serializationId; 15 16 /** 17 * Whether to allow re-registration of a different definition with the same name 18 */ 19 private boolean allowBeanDefinitionOverriding = true; 20 21 /** 22 * Whether to allow eager class loading even for lazy-init beans 23 */ 24 private boolean allowEagerClassLoading = true; 25 26 /** 27 * Optional OrderComparator for dependency Lists and arrays 28 */ 29 @Nullable 30 private Comparator<Object> dependencyComparator; 31 32 /** 33 * Resolver to use for checking if a bean definition is an autowire candidate 34 * 被用来解决去校验一个BeanDefinition是不是自动装载的候选人 35 */ 36 private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver(); 37 38 /** 39 * Map from dependency type to corresponding autowired value 40 * 缓存 类型对应的自动装载的Bean 41 */ 42 private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16); 43 44 /** 45 * Map of bean definition objects, keyed by bean name 46 * 缓存 beanName到BeanDefinition的映射关系 47 */ 48 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256); 49 50 /** 51 * Map of singleton and non-singleton bean names, keyed by dependency type 52 * 缓存 类型 和 beanName的映射关系 53 */ 54 private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64); 55 56 /** 57 * Map of singleton-only bean names, keyed by dependency type 58 * 缓存 类型 和 单例Bean names的映射 59 */ 60 private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64); 61 62 /** 63 * List of bean definition names, in registration order 64 * 缓存 beanDefinition name的list 65 */ 66 private volatile List<String> beanDefinitionNames = new ArrayList<>(256); 67 68 /** 69 * List of names of manually registered singletons, in registration order 70 */ 71 private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16); 72 73 /** 74 * Cached array of bean definition names in case of frozen configuration 75 */ 76 @Nullable 77 private volatile String[] frozenBeanDefinitionNames; 78 79 /** 80 * Whether bean definition metadata may be cached for all beans 81 */ 82 private volatile boolean configurationFrozen = false; 83 }
在Spring中,实际上是把DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用。 DefaultListableBeanFactory作为一个功能完整的容器具备了除以上父类所具有功能外,还加入了对BeanDefinition的管理和维护。从上面的代码可以看到一个重要的属性:beanDefinitionMap。beanDefinitionMap缓存了Bean name到 BeanDefinition的映射。到这里是不是发现了IoC容器另外一个作为“容器”的能力。在我的理解范围内,IoC容器作为“容器”真正装的两个最总要的能力算是总结完了,一个是装单例(Singleton)Bean,一个是装BeanDefinition。
3.3.5, BeanDefinition
Spring通过定义BeanDefinition来管理基于Spring的应用中的各种对象以及他们之间的相互依赖关系。BeanDefinition抽象了我们对Bean的定义,是让容器起作用的主要数据类型。我么都知道在计算机世界里,所有的功能都是建立在通过数据对现实进行抽象的基础上的。IoC容器是用来管理对象依赖关系的,对IoC容器来说,BeanDefinition就是对依赖反转模式中管理的对象依赖关系的数据抽象,也是容器实现依赖反转功能的核心数据结构,依赖反转功能都是围绕对这个BeanDefinition的处理来完成的。这些BeanDefinition就像是容器里装的水,有了这些基本数据,容器才能发挥作用。简单一句话来说,BeanDefinition就是Bean的元数据,BeanDefinition存放了对Bean的基本描述,包括Bean拥有什么属性,方法,Bean的位置等等Bean的各种信息。IoC容器可以通过BeanDefinition生成Bean。
BeanDefinition究竟长什么样呢?
在同第三章debug的地方一样,点开beanFactory,然后查看beanDefinitionMap属性。
OK,BeanDefinition就是长这样了。具体怎么通过它生成Bean,在SpringBoot启动流程中会详细介绍。
四、SpringBoot web工程中的上下文 AnnotationConfigServletWebServerApplicationContext
在SpringBoot工程中,应用类型分为三种,如下代码所示。
1 public enum WebApplicationType { 2 /** 3 * 应用程序不是web应用,也不应该用web服务器去启动 4 */ 5 NONE, 6 /** 7 * 应用程序应作为基于servlet的web应用程序运行,并应启动嵌入式servlet web(tomcat)服务器。 8 */ 9 SERVLET, 10 /** 11 * 应用程序应作为 reactive web应用程序运行,并应启动嵌入式 reactive web服务器。 12 */ 13 REACTIVE 14 }
对应三种应用类型,SpringBoot项目有三种对应的应用上下文,我们以web工程为例,即其上下文为AnnotationConfigServletWebServerApplicationContext
1 public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot." 2 + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; 3 public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework." 4 + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"; 5 public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context." 6 + "annotation.AnnotationConfigApplicationContext"; 7 8 protected ConfigurableApplicationContext createApplicationContext() { 9 Class<?> contextClass = this.applicationContextClass; 10 if (contextClass == null) { 11 try { 12 switch (this.webApplicationType) { 13 case SERVLET: 14 contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS); 15 break; 16 case REACTIVE: 17 contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); 18 break; 19 default: 20 contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); 21 } 22 } catch (ClassNotFoundException ex) { 23 throw new IllegalStateException( 24 "Unable create a default ApplicationContext, " 25 + "please specify an ApplicationContextClass", 26 ex); 27 } 28 } 29 return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); 30 }
我们先看一下AnnotationConfigServletWebServerApplicationContext的设计。
在2.2中已经介绍了ApplicationContext的设计
关于AnnotationConfigServletWebServerApplicationContext详细的设计路线在这里就不像DefaultListableBeanFactory容器那么详细的去讲解了。在第二章说过,应用上下文可以理解成IoC容器的高级表现形式,拿上图和DefaultListableBeanFactory的继承关系图,不难发现,应用上下文确实是在IoC容器的基础上丰富了一些高级功能。在第二章中,我们还说过应用上下文对IoC容器是持有的关系。继续看第二章debug的截图,context就是AnnotationConfigServletWebServerApplicationContext的神秘面孔,他的一个属性beanFactory就是IoC容器(DefaultListableBeanFactory)。所以他们之间是持有,和扩展的关系。
接下来看GenericApplicationContext类
1 public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry { 2 private final DefaultListableBeanFactory beanFactory; 3 ... 4 }
第一行赫然定义了beanFactory属性,正是DefaultListableBeanFactory对象。
关于上下文还有另外一个最重要的方法refresh,上文中说道该方法是在ConfigurableApplicationContext接口中定义的,那么在哪实现的该方法呢?
看AbstractApplicationContext类。
1 @Override 2 public void refresh() throws BeansException, IllegalStateException { 3 synchronized (this.startupShutdownMonitor) { 4 // Prepare this context for refreshing. 5 //刷新上下文环境 6 prepareRefresh(); 7 8 // Tell the subclass to refresh the internal bean factory. 9 //这里是在子类中启动 refreshBeanFactory() 的地方 10 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 11 12 // Prepare the bean factory for use in this context. 13 //准备bean工厂,以便在此上下文中使用 14 prepareBeanFactory(beanFactory); 15 16 try { 17 // Allows post-processing of the bean factory in context subclasses. 18 //设置 beanFactory 的后置处理 19 postProcessBeanFactory(beanFactory); 20 21 // Invoke factory processors registered as beans in the context. 22 //调用 BeanFactory 的后处理器,这些处理器是在Bean 定义中向容器注册的 23 invokeBeanFactoryPostProcessors(beanFactory); 24 25 // Register bean processors that intercept bean creation. 26 //注册Bean的后处理器,在Bean创建过程中调用 27 registerBeanPostProcessors(beanFactory); 28 29 // Initialize message source for this context. 30 //对上下文中的消息源进行初始化 31 initMessageSource(); 32 33 // Initialize event multicaster for this context. 34 //初始化上下文中的事件机制 35 initApplicationEventMulticaster(); 36 37 // Initialize other special beans in specific context subclasses. 38 //初始化其他特殊的Bean 39 onRefresh(); 40 41 // Check for listener beans and register them. 42 //检查监听Bean并且将这些监听Bean向容器注册 43 registerListeners(); 44 45 // Instantiate all remaining (non-lazy-init) singletons. 46 //实例化所有的(non-lazy-init)单件 47 finishBeanFactoryInitialization(beanFactory); 48 49 // Last step: publish corresponding event. 50 //发布容器事件,结束Refresh过程 51 finishRefresh(); 52 } catch (BeansException ex) { 53 if (logger.isWarnEnabled()) { 54 logger.warn("Exception encountered during context initialization - " + 55 "cancelling refresh attempt: " + ex); 56 } 57 58 // Destroy already created singletons to avoid dangling resources. 59 destroyBeans(); 60 61 // Reset 'active' flag. 62 cancelRefresh(ex); 63 64 // Propagate exception to caller. 65 throw ex; 66 } finally { 67 // Reset common introspection caches in Spring's core, since we 68 // might not ever need metadata for singleton beans anymore... 69 resetCommonCaches(); 70 } 71 } 72 }
OK,应用上下文就介绍到这里。
五、IoC容器的初始化过程
在这里我们先口述一下IoC容器的初始化过程吧,源码分析,请移步SpringBoot启动流程分析。
简单来说IoC容器的初始化过程是由前面介绍的refresh()方法启动的,这个方法标志着IoC容器的正式启动。具体来说,这个启动包括三个过程
1 BeanDefinition的Resource定位 2 BeanDefinition的载入 3 向IoC容器注册BeanDefinition
1、第一个过程:Resource定位
这个定位指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一接口。对于这些BeanDefinition的存在形式,可以是通过像SpringMVC中的xml定义的Bean,也可以是像在类路径中的Bean定义信息,比如使用@Component等注解定义的。这个过程类似于容器寻找数据的过程,就像用水桶装水先要把水找到一样。
结合SpringBoot说一下这个过程,对于SpringBoot,我们都知道他的包扫描是从主类所在的包开始扫描的,那这个定位的过程在SpringBoot中具体是这样的,在refresh容器之前(prepareContext()方法中),会先将主类解析成BeanDefinition,然后在refresh方法中并且是扫描Bean之前,解析主类的BeanDefinition获取basePackage的路径。这样就完成了定位的过程。(先不讨论SpringBoot中指定扫描包路径和自动装配)
2、第二个过程:BeanDefinition的载入
这个载入过程是把用户定义好的Bean表示成IoC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition。
在SpringBoot中,上面我们说到通过主类找到了basePackage,SpringBoot会将该路径拼接成:classpath*:org/springframework/boot/demo/**/*.class这样的形式,然后一个叫做PathMatchingResourcePatternResolver的类会将该路径下所有的.class文件都加载进来,然后遍历判断是不是有@Component注解,如果有的话,就是我们要装载的BeanDefinition。大致过程就是这样的了。
注意:@Configuration,@Controller,@Service等注解底层都是@Component注解,只不过包装了一层罢了。
3、第三个过程:注册BeanDefinition
这个过程通过调用上文提到的BeanDefinitionRegister接口的实现来完成。这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。通过上文的分析,我们可以看到,在IoC容器中将BeanDefinition注入到一个ConcurrentHashMap中,IoC容器就是通过这个HashMap来持有这些BeanDefinition数据的。比如DefaultListableBeanFactory 中的beanDefinitionMap属性。
六、IoC容器的依赖注入
上面对IoC容器的初始化过程进行了详细的介绍,这个过程完成的主要的工作是在IoC容器中建立BeanDefinition数据映射。在此过程中并没有看到IoC容器对Bean的依赖关系进行注入。依赖注入是Spring实现“控制反转”的重要一环。Spring将依赖关系交给IoC容器来完成。
依赖控制反转的实现有很多种方式。在Spring中,IoC容器是实现这个模式的载体,它可以在对象生成或者初始化时直接将数据注入到对象中,也可以通过将对象注入到对象数据域中的方式来注入对方法调用的依赖。这种依赖注入是可以递归的,对象被逐层注入。
原创不易,转载请注明出处。
如有错误的地方还请留言指正。
参考文献:
《Spring技术内幕--深入解析Spring框架与设计原理(第二版)》