Spring源码学习笔记(四、Spring启动流程解析:概述与准备上下文、获取BeanFactory)

目录

  • Spring启动流程概述
  • 准备上下文刷新
  • 获取BeanFactory

Spring启动流程概述

我们知道Spring容器的核心就是IOC和DI,所以Spring在实现控制反转和依赖注入的过程中可主要分为两个阶段:

  • 容器启动阶段
  • bean的实例化阶段

容器启动阶段:

  • 加载配置
  • 分析配置信息
  • 将Bean信息装配到BeanDefinition
  • 将Bean信息注册到相应的BeanDefinitionRegistry
  • 其它后续处理

容器实例化阶段:

  • 根据策略实例化对象
  • 装配依赖
  • Bean初始化前处理
  • 对象初始化
  • 对象其他处理
  • 注册回调接口

Spring启动流程详解

 

整体浏览:

1 public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
2         throws BeansException {
3 
4     super(parent);
5     setConfigLocations(configLocations);
6     if (refresh) {
7         refresh();
8     }
9 }
 1 @Override
 2 public void refresh() throws BeansException, IllegalStateException {
 3     // 方法加锁避免多线程同时刷新Spring上下文
 4     synchronized (this.startupShutdownMonitor) {
 5         // 准备上下文刷新
 6         prepareRefresh();
 7 
 8         // 告诉子类刷新内部的beanFactory返回新的BeanFactory
 9         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
10 
11         // 在当前上下文中准备要beanFactory
12         prepareBeanFactory(beanFactory);
13 
14         try {
15             // 允许在上下文子类中对beanFactory进行后置处理
16             postProcessBeanFactory(beanFactory);
17 
18             // 在上下文中将BeanFactory处理器注册为Bean
19             invokeBeanFactoryPostProcessors(beanFactory);
20 
21             // 注册Bean处理器用于拦截Bean的创建
22             registerBeanPostProcessors(beanFactory);
23 
24             // 在上下文中初始化国际化信息
25             initMessageSource();
26 
27             // 在上下文中初始化event multicaster(事件多播器)
28             initApplicationEventMulticaster();
29 
30             // 在指定的上下文子类中初始化其他指定的beans
31             onRefresh();
32 
33             // 检查并注册事件监听
34             registerListeners();
35 
36             // 实例化所有剩余的(非延迟初始化)单例
37             finishBeanFactoryInitialization(beanFactory);
38 
39             // 最后一步:发布相应的事件
40             finishRefresh();
41         }
42 
43         catch (BeansException ex) {
44             if (logger.isWarnEnabled()) {
45                 logger.warn("Exception encountered during context initialization - " +
46                         "cancelling refresh attempt: " + ex);
47             }
48 
49             // 如果出现异常则销毁已创建的单例
50             destroyBeans();
51 
52             // 重置活动标志
53             cancelRefresh(ex);
54 
55             // 将异常传递给调用者
56             throw ex;
57         }
58 
59         finally {
60             // Reset common introspection caches in Spring's core, since we
61             // might not ever need metadata for singleton beans anymore...
62             resetCommonCaches();
63         }
64     }
65 }

首先refresh会有一把锁,防止同时刷新Spring上下文;这把锁startupShutdownMonitor只有在refresh和close才会用到,用于同步Application的刷新和销毁。

从代码里可以看到销毁的时候有两个,registerShutdownHook、close;

区别:当close()被调用时会立即关闭或者停止ApplicationContext;而调用registerShutdownHook()将在稍后JVM关闭时关闭或停止ApplicationContext,该方法主要通过JVM ShutdownHook来实现。

ShutdownHook:Java 语言提供一种ShutdownHook(钩子)机制,当JVM接受到系统的关闭通知之后,调用ShutdownHook内的方法,用以完成清理操作,从而实现平滑退出应用

准备上下文刷新

这一步很简单,主要做了一些属性的设置、验证、资源初始化等

 1 protected void prepareRefresh() {
 2     // 设置Spring容器启动时间
 3     this.startupDate = System.currentTimeMillis();
 4     this.closed.set(false);
 5     this.active.set(true);
 6 
 7     if (logger.isInfoEnabled()) {
 8         logger.info("Refreshing " + this);
 9     }
10 
11     // 初始化属性资源
12     initPropertySources();
13 
14     // 验证所有属性是否都是可解析的(为null则不可解析)
15     getEnvironment().validateRequiredProperties();
16 
17     // ApplicationEvent初始化
18     this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
19 }

获取BeanFactory

1 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
2     refreshBeanFactory();
3     ConfigurableListableBeanFactory beanFactory = getBeanFactory();
4     if (logger.isDebugEnabled()) {
5         logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
6     }
7     return beanFactory;
8 }

首先先刷新BeanFactory,然后就是获取BeanFactory了。

 1 @Override
 2 protected final void refreshBeanFactory() throws BeansException {
 3     // 是否已经存在了BeanFactory
 4     if (hasBeanFactory()) {
 5         // 销毁beans
 6         destroyBeans();
 7         // 关闭已存在的BeanFactory
 8         closeBeanFactory();
 9     }
10     try {
11         // 创建新的BeanFactory对象 -> DefaultListableBeanFactory
12         DefaultListableBeanFactory beanFactory = createBeanFactory();
13         // 这很简单,就是给BeanFactory设置一个全局id
14         beanFactory.setSerializationId(getId());
15         // 该方法主要对2个标志进行设置:allowBeanDefinitionOverriding和allowCircularReferences
16         // allowBeanDefinitionOverriding:是否允许使用相同名称重新注册不同的bean(Spring默认true,SpringBoot默认false)
17         // allowCircularReferences:是否允许循环依赖(默认为true)
18         customizeBeanFactory(beanFactory);
19         // 加载配置
20         loadBeanDefinitions(beanFactory);
21         synchronized (this.beanFactoryMonitor) {
22             this.beanFactory = beanFactory;
23         }
24     }
25     catch (IOException ex) {
26         throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
27     }
28 }

———————————————————————————————————————————————————————

然后我们来看下销毁bean做了些什么处理:destroyBeans()

 1 // 当前这个单例是否在销毁,true=已执行destory方法,或者出现异常时执行了destroySingleton方法
 2 private boolean singletonsCurrentlyInDestruction = false;
 3 // 缓存Bean与Bean的包含关系
 4 private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<String, Set<String>>(16);
 5 // 缓存Bean与其它依赖Bean的关系
 6 private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<String, Set<String>>(64);
 7 // 缓存被依赖Bean与其它依赖Bean的关系
 8 private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<String, Set<String>>(64);
 9 
10 public void destroySingletons() {
11     if (logger.isDebugEnabled()) {
12         logger.debug("Destroying singletons in " + this);
13     }
14     synchronized (this.singletonObjects) {
15         // 设置清理标识
16         this.singletonsCurrentlyInDestruction = true;
17     }
18 
19     // 销毁disposableBeans缓存中所有单例bean
20     String[] disposableBeanNames;
21     synchronized (this.disposableBeans) {
22         disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
23     }
24     for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
25         destroySingleton(disposableBeanNames[i]);
26     }
27 
28     // 清空包含关系
29     this.containedBeanMap.clear();
30     // 清空依赖和被依赖关系
31     this.dependentBeanMap.clear();
32     this.dependenciesForBeanMap.clear();
33 
34     // 清空缓存
35     clearSingletonCache();
36 }

最主要的逻辑也就是清理了一些bean的依赖关系, 29 - 32行。上面的注释你可能有些模糊,没关系,我来举个例子。

1、containedBeanMap:缓存Bean与Bean的包含关系(见org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerContainedBean)。

1 public class A {
2     class B {
3     }
4 }

这就是包含关系,A包含B。

2、dependentBeanMap:缓存Bean与其它依赖Bean的关系(见org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerDependentBean)。

1 public class A {
2     private B b;
3 }
4 public class B {
5 }

上面这段代码就是A依赖于B

3、dependenciesForBeanMap:缓存被依赖Bean与其它依赖Bean的关系见org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#registerDependentBean)。

同dependentBeanMap,B被A依赖。

———————————————————————————————————————————————————————

最后我们来看下获取BeanFactory最复杂也是最重要的部分,loadBeanDefinitions,加载配置。

因为demo使用的是xml解析,所以我们调到AbstractXmlApplicationContext来看:

 1 @Override
 2 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
 3     // Create a new XmlBeanDefinitionReader for the given BeanFactory.
 4     XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
 5 
 6     // Configure the bean definition reader with this context's
 7     // resource loading environment.
 8     beanDefinitionReader.setEnvironment(this.getEnvironment());
 9     beanDefinitionReader.setResourceLoader(this);
10     beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
11 
12     // Allow a subclass to provide custom initialization of the reader,
13     // then proceed with actually loading the bean definitions.
14     initBeanDefinitionReader(beanDefinitionReader);
15     loadBeanDefinitions(beanDefinitionReader);
16 }

前面一段都比较简单,就是一些初始化的动作,我们直接看15行:

 1 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
 2     Resource[] configResources = getConfigResources();
 3     if (configResources != null) {
 4         reader.loadBeanDefinitions(configResources);
 5     }
 6     String[] configLocations = getConfigLocations();
 7     if (configLocations != null) {
 8         reader.loadBeanDefinitions(configLocations);
 9     }
10 }

解析的方式有两种,一种是Resource,一种是String;你可以通过查看代码或debug的方式得出代码会运行到第8行,而configLocations其实也就是我们刚开始定义了xml路径,如beans.xml。

但这两种解析方式实质是一样的,都会进入org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)函数。

此函数主要用于,从执行的xml加载bean的定义,也就是从beans.xml读取配置。

 1 public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
 2     Assert.notNull(encodedResource, "EncodedResource must not be null");
 3     if (logger.isInfoEnabled()) {
 4         logger.info("Loading XML bean definitions from " + encodedResource);
 5     }
 6 
 7     Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
 8     if (currentResources == null) {
 9         currentResources = new HashSet<EncodedResource>(4);
10         this.resourcesCurrentlyBeingLoaded.set(currentResources);
11     }
12     if (!currentResources.add(encodedResource)) {
13         throw new BeanDefinitionStoreException(
14                 "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
15     }
16     try {
17         // 将Resource资源转换为输出流InputSteam
18         InputStream inputStream = encodedResource.getResource().getInputStream();
19         try {
20             InputSource inputSource = new InputSource(inputStream);
21             if (encodedResource.getEncoding() != null) {
22                 inputSource.setEncoding(encodedResource.getEncoding());
23             }
24             // 执行加载bean的过程
25             return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
26         }
27         finally {
28             inputStream.close();
29         }
30     }
31     catch (IOException ex) {
32         throw new BeanDefinitionStoreException(
33                 "IOException parsing XML document from " + encodedResource.getResource(), ex);
34     }
35     finally {
36         currentResources.remove(encodedResource);
37         if (currentResources.isEmpty()) {
38             this.resourcesCurrentlyBeingLoaded.remove();
39         }
40     }
41 }
42 
43 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
44         throws BeanDefinitionStoreException {
45     try {
46         // 加载XML文件,构造Document对象
47         Document doc = doLoadDocument(inputSource, resource);
48         // 注册Bean
49         return registerBeanDefinitions(doc, resource);
50     }
51     // 抛出各种异常......
52 }

构造Document对象就是解析XML,不是本次重点,有兴趣可以去瞅瞅,我们直接看Spring是怎样注册Bean的。

 1 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
 2     // 使用代理类DefaultBeanDefinitionDocumentReader
 3     BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
 4     int countBefore = getRegistry().getBeanDefinitionCount();
 5     // 读取bean的定义
 6     documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
 7     return getRegistry().getBeanDefinitionCount() - countBefore;
 8 }
 9 
10 @Override
11 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
12     this.readerContext = readerContext;
13     logger.debug("Loading bean definitions");
14     Element root = doc.getDocumentElement();
15     // 注册bean实例
16     doRegisterBeanDefinitions(root);
17 }

好,重点来了,doRegisterBeanDefinitions:

 1 // XML配置文件中beans元素
 2 public static final String NESTED_BEANS_ELEMENT = "beans";
 3 // XML配置文件中alias别名元素
 4 public static final String ALIAS_ELEMENT = "alias";
 5 // XML配置文件中name属性
 6 public static final String NAME_ATTRIBUTE = "name";
 7 // XML配置文件中alias属性
 8 public static final String ALIAS_ATTRIBUTE = "alias";
 9 // XML配置文件中import元素
10 public static final String IMPORT_ELEMENT = "import";
11 // XML配置文件中resource属性
12 public static final String RESOURCE_ATTRIBUTE = "resource";
13 // XML配置文件中profile属性
14 public static final String PROFILE_ATTRIBUTE = "profile";
15 
16 protected void doRegisterBeanDefinitions(Element root) {
17     BeanDefinitionParserDelegate parent = this.delegate;
18     // 创建Bean解析代理工具类
19     this.delegate = createDelegate(getReaderContext(), root, parent);
20 
21     if (this.delegate.isDefaultNamespace(root)) {
22         // 解析profile属性
23         String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
24         if (StringUtils.hasText(profileSpec)) {
25             String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
26                 profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
27             if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
28                 if (logger.isInfoEnabled()) {
29                     logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
30                                 "] not matching: " + getReaderContext().getResource());
31                 }
32                 return;
33             }
34         }
35     }
36 
37     preProcessXml(root);
38     // 解析XML并执行Bean注册
39     parseBeanDefinitions(root, this.delegate);
40     postProcessXml(root);
41 
42     this.delegate = parent;
43 }
44 
45 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
46     // root根节点是默认标签
47     if (delegate.isDefaultNamespace(root)) {
48         NodeList nl = root.getChildNodes();
49         // 遍历XML Document的每个节点
50         for (int i = 0; i < nl.getLength(); i++) {
51             Node node = nl.item(i);
52             if (node instanceof Element) {
53                 Element ele = (Element) node;
54                 if (delegate.isDefaultNamespace(ele)) {
55                     // 解析默认标签
56                     parseDefaultElement(ele, delegate);
57                 }
58                 else {
59                     // 解析自定义标签
60                     delegate.parseCustomElement(ele);
61                 }
62             }
63         }
64     }
65     // root根节点是自定义标签
66     else {
67         delegate.parseCustomElement(root);
68     }
69 }
70 
71 // 解析XML配置文件的节点元素
72 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
73     // 如果是Import元素
74     if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
75         importBeanDefinitionResource(ele);
76     }
77     // 如果是Alias别名元素
78     else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
79         processAliasRegistration(ele);
80     }
81     // 如果是Bean元素
82     else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
83         processBeanDefinition(ele, delegate);
84     }
85     // 如果是嵌套Bean元素(Beans)
86     else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
87         // recurse
88         doRegisterBeanDefinitions(ele);
89     }
90 }

具体实现逻辑有兴趣的可以自行查阅源码。

posted @ 2020-05-24 14:25  被猪附身的人  阅读(513)  评论(0编辑  收藏  举报