• 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 }

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

  • 相关阅读:
    Go网络文件传输
    Go网络编程
    LNMP环境搭建(PHP7.4.0)
    LNMP环境搭建(PHP7.2.25)
    Please ensure the argon2 header and library are installed
    MySQL权限管理
    nginx ingress controller配置默认SSL证书
    kubernetes pod内抓包,telnet检查网络连接的几种方式
    ansible取出register变量中最长字符串
    kubernetes flannel pod CrashLoopBackoff解决
  • 原文地址:https://www.cnblogs.com/bzfsdr/p/12937006.html
Copyright © 2020-2023  润新知