美文网首页
IOC方法入口refresh()、 prepareRefresh

IOC方法入口refresh()、 prepareRefresh

作者: 就这些吗 | 来源:发表于2020-08-06 23:12 被阅读0次

    本系列大量参考Spring IOC 容器源码分析【死磕 Spring】—– IOC 总结

    文章内容如下:

    1.IOC的入口
    1.1 ApplicationContext的介绍
    1.2 ClassPathXmlApplicationContext
    2. AbstractApplicationContext#refresh,初始化容器和bean
      2.1 AbstractApplicationContext#prepareRefresh 标记“已启动”状态、处理配置文件中的占位符
      2.2 AbstractApplicationContext#obtainFreshBeanFactory() 创建BeanFactory(包括解析得到BeanDefinition)
        2.2.1 AbstractRefreshableApplicationContext#refreshBeanFactory 刷新(重建)BeanFactory
        2.2.2 这里先解决,为什么创建的是DefaultListableBeanFactory?
        2.2.3 来看一下BeanDefinition接口的实现
          //加载 BeanDefinition 们
        (1)AbstractXmlApplicationContext#loadBeanDefinitions(beanFactory) 
        //最后返回的是加载BeanDefinition 数量
        (2)AbstractBeanDefinitionReader#loadBeanDefinitions(Resource... resources) 
        //避免加载自身的死循环,调用加载Bean的真正逻辑
        (3)XmlBeanDefinitionReader#(Resource resource) 
        //获取XML Document实例,注册Bean信息
        (4)XmlBeanDefinitionReader #doLoadBeanDefinitions(InputSource inputSource,   Resource resource)
        //执行注册BeanDefinition,解析文件
        (5)DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions,
        //根据命名空间解析
        (6)DefaultBeanDefinitionDocumentReader#parseBeanDefinitions,
        //解析默认命名空间标签,在调用的类中还会进行 BeanDefinition 的注册
        (7)DefaultBeanDefinitionDocumentReader#parseDefaultElement 
        //解析bean标签,设置beanName
        (8)BeanDefinitionParserDelegate #parseBeanDefinitionElement
        //真正解析标签的方法
        (9)BeanDefinitionParserDelegate#parseBeanDefinitionElement 
      【注,这两个(8)(9)是对(7)中调用的不同方法的分析,(8)(9)可类比为栈深】
        // 通过beanName和alias来执行不同的方法注册bean
        (8)BeanDefinitionReaderUtils#registerBeanDefinition 
        //真正通过beanName注册的方法
        (9)DefaultListableBeanFactory#registerBeanDefinition
    3.总结
    

    1.IOC的入口

    1.1 ApplicationContext的介绍

    IOC 总体来说有两处地方最重要,一个是创建 Bean 容器,一个是初始化 Bean,先从启动Spring容器的例子来开始吧。从前文的组件我们了解到,容器有BeanFactor和ApplicationContext两类(注意是类而不是两种)。这边我们以介绍ApplicationContext为主。

    image.png

    对于Spring项目,一般我们在main方法里会这么写,

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationfile.xml");
    }
    

    1.2 ClassPathXmlApplicationContext

    继续进去看看new的这个ClassPathXmlApplicationContext做了什么吧

        public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
    
            this(new String[] {configLocation}, true, null);
        }
    
        public ClassPathXmlApplicationContext(
                String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
                throws BeansException {
    
            super(parent);
            setConfigLocations(configLocations);
            if (refresh) {
                refresh();
            }
        }
    
    

    这边就进入了refresh方法的逻辑,此方法是核心方法,用以初始化容器,初始化bean,后面有关IOC源码的分析都是围绕这个方法展开的。

    2.AbstractApplicationContext#refresh,初始化容器和bean

    public void refresh() throws BeansException, IllegalStateException {
       // 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
       synchronized (this.startupShutdownMonitor) {
    
          // 准备工作,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符
          prepareRefresh();
    
          // 这步比较关键,这步完成后,配置文件就会解析成一个个 Bean 定义,注册到 BeanFactory 中,
          // 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了,
          // 注册也只是将这些信息都保存到了注册中心(说到底核心是一个 beanName-> beanDefinition 的 map)
          ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
          // 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
          // 这块待会会展开说
          prepareBeanFactory(beanFactory);
    
          try {
             // 【这里需要知道 BeanFactoryPostProcessor 这个知识点,Bean 如果实现了此接口,
             // 那么在容器初始化以后,Spring 会负责调用里面的 postProcessBeanFactory 方法。】
    
             // 这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化
             // 具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事
             postProcessBeanFactory(beanFactory);
             // 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
             invokeBeanFactoryPostProcessors(beanFactory);
    
             // 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别
             // 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
             // 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
             registerBeanPostProcessors(beanFactory);
    
             // 初始化当前 ApplicationContext 的 MessageSource,国际化这里就不展开说了,不然没完没了了
             initMessageSource();
    
             // 初始化当前 ApplicationContext 的事件广播器,这里也不展开了
             initApplicationEventMulticaster();
    
             // 从方法名就可以知道,典型的模板方法(钩子方法),
             // 具体的子类可以在这里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
             onRefresh();
    
             // 注册事件监听器,监听器需要实现 ApplicationListener 接口。这也不是我们的重点,过
             registerListeners();
    
             // 重点,重点,重点
             // 初始化所有的 singleton beans
             //(lazy-init 的除外)
             finishBeanFactoryInitialization(beanFactory);
    
             // 最后,广播事件,ApplicationContext 初始化完成
             finishRefresh();
          }
    
          catch (BeansException ex) {
             if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                      "cancelling refresh attempt: " + ex);
             }
    
             // Destroy already created singletons to avoid dangling resources.
             // 销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源
             destroyBeans();
    
             // Reset 'active' flag.
             cancelRefresh(ex);
    
             // 把异常往外抛
             throw ex;
          }
    
          finally {
             // Reset common introspection caches in Spring's core, since we
             // might not ever need metadata for singleton beans anymore...
             resetCommonCaches();
          }
       }
    }
    

    2.1 AbstractApplicationContext#prepareRefresh 标记“已启动”状态、处理配置文件中的占位符

        protected void prepareRefresh() {
            // 记录启动时间
            this.startupDate = System.currentTimeMillis();
            // 设置关闭状态为 false 、开启状态为 true,这两个都是AtomicBoolean
                //这两个标记为当前Context的状态
            this.closed.set(false);
            this.active.set(true);
    
            if (logger.isDebugEnabled()) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Refreshing " + this);
                } else {
                    logger.debug("Refreshing " + getDisplayName());
                }
            }
    
            // 初始化 PropertySource ,由子类覆盖实现。默认实现为空。
            initPropertySources();
    
            // 验证所需属性,是否已经放到环境中
            getEnvironment().validateRequiredProperties();
    
            // 初始化早期的 ApplicationEvent 集合。
            // 因为此时,ApplicationMulticaster 还没创建好。
            this.earlyApplicationEvents = new LinkedHashSet<>();
        }
    

    这个方法只是简单的创建 Bean 容器前的准备工作

    • 1.负责将spring容器设置相应的状态
    • 2.初始化上下文环境,对系统的环境变量或者系统属性进行准备和校验,比如如环境变量中必须设置某个值才能运行,否则不能运行,这个时候可以在这里加这个校验,重写initPropertySources方法就好了。例子:spring4.1.8扩展实战之一:自定义环境变量验证
    • 3.在initPropertySources方法中设定值后,getEnvironment().validateRequiredProperties()会从集合requiredProperties中取出所有key(这是initPropertySources方法注入进来的值),然后获取这些key的环境变量(包括系统环境变量和进程环境变量),如果有一个key对应的环境变量为空,就会抛出异常,导致spring容器初始化失败;也就是说这两行代码的联动的,在前面一行注入一个必要的值,后面一行对这个值进行校验

    2.2 AbstractApplicationContext#obtainFreshBeanFactory() 创建BeanFactory(包括解析得到BeanDefinition)

    我们回到refresh()方法中的下一行 obtainFreshBeanFactory()

    注意,这个方法是全文最重要的部分之一,这里将会初始化BeanFactory、加载 Bean、注册BeanDefinition 等等。也会在此延伸出去一些概念。

        protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
            // 刷新( 重建 ) BeanFactory
            refreshBeanFactory();
            // 获得 BeanFactory
            return getBeanFactory();
        }
    
    

    这两个方法都是抽象方法,交由子类实现,先来看他的refreshBeanFactory

    在他的子类里有如下代码:

    2.2.1 AbstractRefreshableApplicationContext#refreshBeanFactory 刷新(重建)BeanFactory

        protected final void refreshBeanFactory() throws BeansException {
            // 若已有 BeanFactory ,销毁它的 Bean 们,并销毁 BeanFactory
            if (hasBeanFactory()) {
                destroyBeans();
                closeBeanFactory();
            }
            try {
                // 创建 BeanFactory 对象
                DefaultListableBeanFactory beanFactory = createBeanFactory();
                // 指定序列化编号
                beanFactory.setSerializationId(getId());
          // 设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用(后面再讲)
                customizeBeanFactory(beanFactory);
                // 加载 BeanDefinition 们
                loadBeanDefinitions(beanFactory);
                // 设置 Context 的 BeanFactory
                synchronized (this.beanFactoryMonitor) {
                    this.beanFactory = beanFactory;
                }
            } catch (IOException ex) {
                throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
            }
        }
    
      1. 判断当前容器是否存在一个 BeanFactory,如果存在则对其进行销毁和关闭
      1. 调用 createBeanFactory()创建一个 BeanFactory 实例,其实就是 DefaultListableBeanFactory
      1. 自定义 BeanFactory的属性
      1. 加载 BeanDefinition
      1. 将创建好的 bean 工厂的引用交给的 ApplicationContext 来管理

    2.2.2 这里先解决,为什么创建的是DefaultListableBeanFactory?

    image.png

    看这链路就很明显了,他一个人把这几个接口、抽象类都继承了,拥有所有的功能。

    而设置完BeanFactory的属性后调用了loadBeanDefinitions(beanFactory)来加载BeanDefinition,
    在此之前我们仍需先介绍一下
    什么是BeanDefinition?

    BeanDefinition中保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等。
    Bean 在代码层面上可以简单认为是 BeanDefinition 的实例,注意哦,此时Bean还没实例化

    2.2.3 来看一下BeanDefinition接口的实现

    public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
    
       // 我们可以看到,默认只提供 sington 和 prototype 两种,
       // 很多读者可能知道还有 request, session, globalSession, application, websocket 这几种,
       // 不过,它们属于基于 web 的扩展。
       String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
       String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
    
       // 比较不重要,直接跳过吧
       int ROLE_APPLICATION = 0;
       int ROLE_SUPPORT = 1;
       int ROLE_INFRASTRUCTURE = 2;
    
       // 设置父 Bean,这里涉及到 bean 继承,不是 java 继承。请参见附录的详细介绍
       // 一句话就是:继承父 Bean 的配置信息而已
       void setParentName(String parentName);
    
       // 获取父 Bean
       String getParentName();
    
       // 设置 Bean 的类名称,将来是要通过反射来生成实例的
       void setBeanClassName(String beanClassName);
    
       // 获取 Bean 的类名称
       String getBeanClassName();
    
    
       // 设置 bean 的 scope
       void setScope(String scope);
    
       String getScope();
    
       // 设置是否懒加载
       void setLazyInit(boolean lazyInit);
    
       boolean isLazyInit();
    
       // 设置该 Bean 依赖的所有的 Bean,注意,这里的依赖不是指属性依赖(如 @Autowire 标记的),
       // 是 depends-on="" 属性设置的值。
       void setDependsOn(String... dependsOn);
    
       // 返回该 Bean 的所有依赖
       String[] getDependsOn();
    
       // 设置该 Bean 是否可以注入到其他 Bean 中,只对根据类型注入有效,
       // 如果根据名称注入,即使这边设置了 false,也是可以的
       void setAutowireCandidate(boolean autowireCandidate);
    
       // 该 Bean 是否可以注入到其他 Bean 中
       boolean isAutowireCandidate();
    
       // 主要的。同一接口的多个实现,如果不指定名字的话,Spring 会优先选择设置 primary 为 true 的 bean
       void setPrimary(boolean primary);
    
       // 是否是 primary 的
       boolean isPrimary();
    
       // 如果该 Bean 采用工厂方法生成,指定工厂名称。对工厂不熟悉的读者,请参加附录
       // 一句话就是:有些实例不是用反射生成的,而是用工厂模式生成的
       void setFactoryBeanName(String factoryBeanName);
       // 获取工厂名称
       String getFactoryBeanName();
       // 指定工厂类中的 工厂方法名称
       void setFactoryMethodName(String factoryMethodName);
       // 获取工厂类中的 工厂方法名称
       String getFactoryMethodName();
    
       // 构造器参数
       ConstructorArgumentValues getConstructorArgumentValues();
    
       // Bean 中的属性值,后面给 bean 注入属性值的时候会说到
       MutablePropertyValues getPropertyValues();
    
       // 是否 singleton
       boolean isSingleton();
    
       // 是否 prototype
       boolean isPrototype();
    
       // 如果这个 Bean 是被设置为 abstract,那么不能实例化,
       // 常用于作为 父bean 用于继承,其实也很少用......
       boolean isAbstract();
    
       int getRole();
       String getDescription();
       String getResourceDescription();
       BeanDefinition getOriginatingBeanDefinition();
    }
    

    这里接口虽然那么多,但是没有类似 getInstance()这种方法来获取我们定义的类的实例,真正的我们定义的类生成的实例到哪里去了呢?别着急,这个要很后面才能讲到。

    先来看这个 loadBeanDefinitions(beanFactory),同样的这个也是抽象方法,交由子类实现,
    AbstractXmlApplicationContext中会有其实现方法,

    (1) AbstractXmlApplicationContext# loadBeanDefinitions(beanFactory) 加载 BeanDefinition 们

        protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    
            // 创建 XmlBeanDefinitionReader 对象
            XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
            // 对 XmlBeanDefinitionReader 进行环境变量的设置
            beanDefinitionReader.setEnvironment(this.getEnvironment());
            beanDefinitionReader.setResourceLoader(this);
            beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
            // 对 XmlBeanDefinitionReader 进行设置,可以进行覆盖
            initBeanDefinitionReader(beanDefinitionReader);
    
            // 从 Resource 们中,加载 BeanDefinition 们
            loadBeanDefinitions(beanDefinitionReader);
        }
    
        protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
            // 从配置文件 Resource 中,加载 BeanDefinition 们
            Resource[] configResources = getConfigResources();
            if (configResources != null) {
                reader.loadBeanDefinitions(configResources);
            }
            // 从配置文件地址中,加载 BeanDefinition 们
            String[] configLocations = getConfigLocations();
            if (configLocations != null) {
                reader.loadBeanDefinitions(configLocations);
            }
        }
    

    至此我们知道了,在这一步就会加载BeanDefinition了,本篇接下来的内容就是简单分析是如何读取XML文件来加载成一个BeanDefinition的。
    上面虽有两个分支,属于重载方法,但是最后都是会调用到

    AbstractBeanDefinitionReader#loadBeanDefinitions(Resource... resources)的,接下来就来分析这个方法

    (2) AbstractBeanDefinitionReader#loadBeanDefinitions(Resource... resources) 最后返回的是加载BeanDefinition 数量

    public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {
    
        public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
            Assert.notNull(resources, "Resource array must not be null");
            int count = 0;
    // 注意这里是个 for 循环,也就是每个文件是一个 resource
            for (Resource resource : resources) {
                count += loadBeanDefinitions(resource);
            }
    // 最后返回 counter,表示总共加载了多少的 BeanDefinition
            return count;
        }
    //省略其他代码
    }
    

    会接着调用

    (3) XmlBeanDefinitionReader#loadBeanDefinitions(Resource resource) 避免加载自身的死循环,调用加载Bean的真正逻辑

    public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
        public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
            return loadBeanDefinitions(new EncodedResource(resource));
        }
    
        public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
            Assert.notNull(encodedResource, "EncodedResource must not be null");
            if (logger.isTraceEnabled()) {
                logger.trace("Loading XML bean definitions from " + encodedResource);
            }
    
            // 获取已经加载过的资源
            Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
            if (currentResources == null) {
                currentResources = new HashSet<>(4);
                this.resourcesCurrentlyBeingLoaded.set(currentResources);
            }
    // 将当前资源加入记录中。如果已存在,抛出异常
            if (!currentResources.add(encodedResource)) { 
                throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
            }
            try {
                // 从 EncodedResource 获取封装的 Resource ,并从 Resource 中获取其中的 InputStream
                InputStream inputStream = encodedResource.getResource().getInputStream();
                try {
                    InputSource inputSource = new InputSource(inputStream);
                    if (encodedResource.getEncoding() != null) { // 设置编码
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }
                    // 核心逻辑部分,执行加载 BeanDefinition
                    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                } finally {
                    inputStream.close();
                }
            } catch (IOException ex) {
                throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex);
            } finally {
                // 从缓存中剔除该资源
                currentResources.remove(encodedResource);
                if (currentResources.isEmpty()) {
                    this.resourcesCurrentlyBeingLoaded.remove();
                }
            }
        }
    //省略其他代码
    }
    

    这边解释下为什么当资源加入记录,已存在的时候回抛出异常,这是为了避免一个 EncodedResource 在加载时,还没加载完成,又加载自身,从而导致死循环。
    也因此,当一个EncodedResource 加载完成后,需要从缓存中剔除。

    这个方法从encodedResource获取封装的Resource 资源,并从Resource中获取相应的 InputStream ,然后将InputStream封装为 InputSource ,最后调用 XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource inputSource, Resource resource) 方法,执行加载 Bean Definition的真正逻辑

    (4) XmlBeanDefinitionReader #doLoadBeanDefinitions(InputSource inputSource, Resource resource),获取XML Document实例,注册Bean信息

    public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    
        protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
                throws BeanDefinitionStoreException {
            try {
                // 获取 XML Document 实例
                Document doc = doLoadDocument(inputSource, resource);
                // 根据 Document 实例,注册 Bean 信息
                int count = registerBeanDefinitions(doc, resource);
                if (logger.isDebugEnabled()) {
                    logger.debug("Loaded " + count + " bean definitions from " + resource);
                }
                return count;
            } catch (BeanDefinitionStoreException ex) {
                throw ex;
            } catch (SAXParseException ex) {
                throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                        "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
            } catch (SAXException ex) {
                throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                        "XML document from " + resource + " is invalid", ex);
            } catch (ParserConfigurationException ex) {
                throw new BeanDefinitionStoreException(resource.getDescription(),
                        "Parser configuration exception parsing XML from " + resource, ex);
            } catch (IOException ex) {
                throw new BeanDefinitionStoreException(resource.getDescription(),
                        "IOException parsing XML document from " + resource, ex);
            } catch (Throwable ex) {
                throw new BeanDefinitionStoreException(resource.getDescription(),
                        "Unexpected exception parsing XML document from " + resource, ex);
            }
        }
    
        protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
            return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                    getValidationModeForResource(resource), isNamespaceAware());
        }
    
        public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
            // 创建 BeanDefinitionDocumentReader 对象
            BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
            // 获取已注册的 BeanDefinition 数量
            int countBefore = getRegistry().getBeanDefinitionCount();
            // 创建 XmlReaderContext 对象
            // 注册 BeanDefinition
            documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
            // 计算新注册的 BeanDefinition 数量
            return getRegistry().getBeanDefinitionCount() - countBefore;
        }
    //省略其他代码
    }
    

    这边就主要是两个方法,

    • doLoadDocument() 根据 xml 文件,获取 Document 实例。
    • registerBeanDefinitions() 一个根据获取的 Document 实例,注册 Bean 信息。

    这边对于doLoadDocument()方法做一个简单的描述:
    其调用的getValidationModeForResource方法主要是为了获取指定资源(xml)的验证模式。XML 文件的验证模式保证了 XML 文件的正确性。常见的有DTD和XSD两种
    而后通过解析器(EntityResolver)来解析InputSource,返回 Document对象。

    在这里多提一点,在XML文件上的约束,就是下图这个,所以我们虽然在SpringXML 配置中看到的约束文件是一个在线地址,实际上约束文件是从本地 jar 中读取的,EntityResolver也是根据这个来解析的。参考链接:Spring 源码第三弹!EntityResolver 是个什么鬼?

    image.png

    说过了doLoadDocument(inputSource, resource),再来聊聊registerBeanDefinitions(doc, resource)是怎么注册bean信息的,这边的注释已经比较清晰了,主要注意这个同名的方法,他会调用
    DefaultBeanDefinitionDocumentReader里的方法

    (5)DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions,执行注册BeanDefinition,解析文件

    public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
    
        public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
            this.readerContext = readerContext;
            // 获得 XML Document Root Element
            // 执行注册 BeanDefinition
            // 从 xml 根节点开始解析文件
            doRegisterBeanDefinitions(doc.getDocumentElement());
        }
    
        protected void doRegisterBeanDefinitions(Element root) {
    
            // 记录老的 BeanDefinitionParserDelegate 对象(为了递归处理)
          //负责解析 XML Element 的各种方法
         // 因为 <beans /> 内部是可以定义 <beans /> 的,所以这个方法的 root 其实不一定就是 xml 的根节点,
      //也可以是嵌套在里面的 <beans /> 节点,从源码分析的角度,我们当做根节点就好了
            BeanDefinitionParserDelegate parent = this.delegate;
            // 创建 BeanDefinitionParserDelegate 对象,并进行设置到 delegate
            this.delegate = createDelegate(getReaderContext(), root, parent);
            //检查 <beans /> 根标签的命名空间是否为空
            if (this.delegate.isDefaultNamespace(root)) {
                // 处理 profile 属性。
                String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
                if (StringUtils.hasText(profileSpec)) {
                    // 使用分隔符切分,可能有多个 profile 。
                    String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                            profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                    // 如果所有 profile 都无效,则不进行注册
                    if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                    "] not matching: " + getReaderContext().getResource());
                        }
                        return;
                    }
                }
            }
    
            // 解析前处理
            preProcessXml(root);
            // 解析
            parseBeanDefinitions(root, this.delegate);
            // 解析后处理
            postProcessXml(root);
    
            // 设置 delegate 回老的 BeanDefinitionParserDelegate 对象
            this.delegate = parent;
        }
    //省略其他代码
    }
    

    对于代码里的profile不熟悉的可以参见这篇博客 Spring3自定义环境配置 <beans profile="">
    这个方法只是对profile进行了处理,真正的执行逻辑还是在parseBeanDefinitions(root, this.delegate);中,另外解析前后的处理是空方法,可以交由子类去自己实现。这边就不看了。

    (6)DefaultBeanDefinitionDocumentReader#parseBeanDefinitions,根据命名空间解析

    public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
    
        protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
            // 如果根节点使用默认命名空间,执行默认解析
            if (delegate.isDefaultNamespace(root)) {
                // 遍历子节点
                NodeList nl = root.getChildNodes();
                for (int i = 0; i < nl.getLength(); i++) {
                    Node node = nl.item(i);
                    if (node instanceof Element) {
                        Element ele = (Element) node;
                        // 如果该节点使用默认命名空间,执行默认解析
                        if (delegate.isDefaultNamespace(ele)) {
                            parseDefaultElement(ele, delegate);
                        // 如果该节点非默认命名空间,执行自定义解析
                        } else {
                            delegate.parseCustomElement(ele);
                        }
                    }
                }
            // 如果根节点非默认命名空间,执行自定义解析
            } else {
                delegate.parseCustomElement(root);
            }
        }
    //省略其他代码
    }
    

    这边有默认命名空间和非默认之分,默认的是<import />、<alias />、<bean />、<beans />这四个节点,这是由于他们处于http://www.springframework.org/schema/beans这个 namespace下。
    如果需要使用"非 default" 标签,那么上面的xml 头部的地方也要引入相应的 namespace 和 .xsd 文件的路径,如下面代码所示,还引入了<mvc />、<task />、<context />、<aop />等标签。

    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns="http://www.springframework.org/schema/beans"
          xmlns:context="http://www.springframework.org/schema/context"
          xmlns:mvc="http://www.springframework.org/schema/mvc"
          xsi:schemaLocation="
               http://www.springframework.org/schema/beans 
               http://www.springframework.org/schema/beans/spring-beans.xsd
               http://www.springframework.org/schema/context
               http://www.springframework.org/schema/context/spring-context.xsd
               http://www.springframework.org/schema/mvc   
               http://www.springframework.org/schema/mvc/spring-mvc.xsd  
           "
          default-autowire="byName">
    

    至此,我们主要来分析下默认命名空间下的bean标签

    (7) DefaultBeanDefinitionDocumentReader#parseDefaultElement 解析默认命名空间标签

    public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
    
        private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
            if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // import
                importBeanDefinitionResource(ele);
            } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // alias
                processAliasRegistration(ele);
            } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { // bean
                processBeanDefinition(ele, delegate);
            } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // beans,需要执行递归
                doRegisterBeanDefinitions(ele);
            }
        }
    
            // 进行 bean 元素解析。
        protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    
            // 如果解析成功,则返回 BeanDefinitionHolder 对象。而 BeanDefinitionHolder 为 name 和 alias 的 BeanDefinition 对象
            // 如果解析失败,则返回 null 。
            BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
            if (bdHolder != null) {
                // 进行自定义标签处理
                bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
                try {
                    // 进行 BeanDefinition 的注册
                    BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
                } catch (BeanDefinitionStoreException ex) {
                    getReaderContext().error("Failed to register bean definition with name '" +
                            bdHolder.getBeanName() + "'", ele, ex);
                }
                // 发出响应事件,通知相关的监听器,已完成该 Bean 标签的解析。
                getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
            }
        }
    //省略其他代码
    }
    

    很明显,这个processBeanDefinition方法也不是真正的解析标签的方法,需要到
    parseBeanDefinitionElement方法里去

    (7) BeanDefinitionParserDelegate #parseBeanDefinitionElement解析bean标签,设置beanName

    public class BeanDefinitionParserDelegate {
        public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
            return parseBeanDefinitionElement(ele, null);
        }
    
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
            // 解析 id 和 name 属性
            String id = ele.getAttribute(ID_ATTRIBUTE);
            String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    
            // 计算别名集合
            // 将 name 属性的定义按照 “逗号、分号、空格” 切分,形成一个 别名列表数组,
            // 当然,如果你不定义 name 属性的话,就是空的了
            List<String> aliases = new ArrayList<>();
            if (StringUtils.hasLength(nameAttr)) {
                String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                aliases.addAll(Arrays.asList(nameArr));
            }
    
            // beanName ,优先,使用 id
            String beanName = id;
           // 如果没有指定id, 那么用别名列表的第一个名字作为beanName
            if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
                beanName = aliases.remove(0); // 移除出别名集合
                if (logger.isTraceEnabled()) {
                    logger.trace("No XML 'id' specified - using '" + beanName +
                            "' as bean name and " + aliases + " as aliases");
                }
            }
    
    
            if (containingBean == null) {
            // 检查 beanName 的唯一性
                checkNameUniqueness(beanName, aliases, ele);
            }
    
            // 根据 <bean ...>...</bean> 中的配置创建 BeanDefinition,然后把配置中的信息都设置到实例中
            AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    // 到这里,整个 <bean /> 标签就算解析结束了,一个 BeanDefinition 就形成了。
            if (beanDefinition != null) {
                // 如果都没有设置 id 和 name,那么此时的 beanName 就会为 null,进入下面这块代码产生
                if (!StringUtils.hasText(beanName)) {
                    try {
                        if (containingBean != null) {
                            // 生成唯一的 beanName
                            beanName = BeanDefinitionReaderUtils.generateBeanName(
                                    beanDefinition, this.readerContext.getRegistry(), true);
                        } else {
                   // 如果我们不定义 id 和 name,那么我们下图里的那个例子:
                   //   1. beanName 为:com.javadoop.example.MessageServiceImpl#0
                   //   2. beanClassName 为:com.javadoop.example.MessageServiceImpl
                            beanName = this.readerContext.generateBeanName(beanDefinition);
    
                            String beanClassName = beanDefinition.getBeanClassName();
                            if (beanClassName != null &&
                                    beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                    !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                      // 把 beanClassName 设置为 Bean 的别名
                                aliases.add(beanClassName);
                            }
                        }
                        if (logger.isTraceEnabled()) {
                            logger.trace("Neither XML 'id' nor 'name' specified - " +
                                    "using generated bean name [" + beanName + "]");
                        }
                    } catch (Exception ex) {
                        error(ex.getMessage(), ele);
                        return null;
                    }
                }
                // 返回 BeanDefinitionHolder 对象
                String[] aliasesArray = StringUtils.toStringArray(aliases);
                return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
            }
            return null;
        }
    //省略其他代码
    }
    

    这边的代码就是对于BeanDefinition设置beanName,配置规则如下图,但是从解析的逻辑仍需我们更进一层,去parseBeanDefinitionElement方法中才能了解到

    image.png

    (9) BeanDefinitionParserDelegate#parseBeanDefinitionElement 真正解析标签的方法

    public class BeanDefinitionParserDelegate {
    
        @Nullable
        public AbstractBeanDefinition parseBeanDefinitionElement(
                Element ele, String beanName, @Nullable BeanDefinition containingBean) {
    
            this.parseState.push(new BeanEntry(beanName));
    
            // 解析 class 属性
            String className = null;
            if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
                className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
            }
            // 解析 parent 属性
            String parent = null;
            if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
                parent = ele.getAttribute(PARENT_ATTRIBUTE);
            }
    
            try {
                 // 创建 BeanDefinition,然后设置类信息
                AbstractBeanDefinition bd = createBeanDefinition(className, parent);
    
                // 解析默认 bean 的各种属性
                parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
                // 提取 description
                bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    
                // tips:
                // 下面的一堆是解析 <bean>......</bean> 内部的子元素,
                // 解析出来以后的信息都放到 bd 的属性中
    
                // 解析元数据 <meta />
                parseMetaElements(ele, bd);
                // 解析 lookup-method 属性 <lookup-method />
                parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
                // 解析 replaced-method 属性 <replaced-method />
                parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
    
                // 解析构造函数参数 <constructor-arg />
                parseConstructorArgElements(ele, bd);
                // 解析 property 子元素 <property />
                parsePropertyElements(ele, bd);
                // 解析 qualifier 子元素 <qualifier />
                parseQualifierElements(ele, bd);
    
                bd.setResource(this.readerContext.getResource());
                bd.setSource(extractSource(ele));
    
                return bd;
            } catch (ClassNotFoundException ex) {
                error("Bean class [" + className + "] not found", ele, ex);
            } catch (NoClassDefFoundError err) {
                error("Class that bean class [" + className + "] depends on not found", ele, err);
            } catch (Throwable ex) {
                error("Unexpected failure during bean definition parsing", ele, ex);
            } finally {
                this.parseState.pop();
            }
    
            return null;
        }
    //省略其他代码
    }
    

    到这里,我们已经完成了根据 <bean /> 配置创建了一个BeanDefinitionHolder 实例。注意,是一个。
    其成员变量也很简单

    image.png

    至此,我们可以回到(7)中的DefaultBeanDefinitionDocumentReader#processBeanDefinition,来进行下一步BeanDefinition的注册了

    (8)BeanDefinitionReaderUtils#registerBeanDefinition 通过beanName和alias注册bean

    public abstract class BeanDefinitionReaderUtils {
    
    public static void registerBeanDefinition(
          BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
          throws BeanDefinitionStoreException {
    
       String beanName = definitionHolder.getBeanName();
       // 注册这个 Bean
       registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
       // 如果还有别名的话,也要根据别名全部注册一遍,不然根据别名就会找不到 Bean 了
       String[] aliases = definitionHolder.getAliases();
       if (aliases != null) {
          for (String alias : aliases) {
             // alias -> beanName 保存它们的别名信息,这个很简单,用一个 map 保存一下就可以了,
             // 获取的时候,会先将 alias 转换为 beanName,然后再查找
             registry.registerAlias(beanName, alias);
          }
       }
    }
    //省略其他代码
    }
    

    这个方法就是通过beanNamealias注册bean,真正的代码逻辑要调用 BeanDefinitionRegistry的实现类,这边选了个DefaultListableBeanFactory来看

    (9)DefaultListableBeanFactory#registerBeanDefinition 真正通过beanName注册的方法

    public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
            implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    
        public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
                throws BeanDefinitionStoreException {
    
            // 校验 beanName 与 beanDefinition 非空
            Assert.hasText(beanName, "Bean name must not be empty");
            Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    
            // 校验 BeanDefinition 。
            // 这是注册前的最后一次校验了,主要是对属性 methodOverrides 进行校验。
            if (beanDefinition instanceof AbstractBeanDefinition) {
                try {
                    ((AbstractBeanDefinition) beanDefinition).validate();
                } catch (BeanDefinitionValidationException ex) {
                    throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                            "Validation of bean definition failed", ex);
                }
            }
    
            // 从缓存中获取指定 beanName 的 BeanDefinition
            BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
            // 如果已经存在
            if (existingDefinition != null) {
                // 如果存在但是不允许覆盖,抛出异常 
                if (!isAllowBeanDefinitionOverriding()) {
                    throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
                // 覆盖 beanDefinition 大于 被覆盖的 beanDefinition 的 ROLE ,打印 info 日志
                } else if (existingDefinition.getRole() < beanDefinition.getRole()) {
                    //打印的意思是用框架定义的 Bean 覆盖用户自定义的 Bean 
                    if (logger.isInfoEnabled()) {
                        logger.info("Overriding user-defined bean definition for bean '" + beanName +
                                "' with a framework-generated bean definition: replacing [" +
                                existingDefinition + "] with [" + beanDefinition + "]");
                    }
                // 覆盖 beanDefinition 与 被覆盖的 beanDefinition 不相同,打印 debug 日志
                } else if (!beanDefinition.equals(existingDefinition)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Overriding bean definition for bean '" + beanName +
                                "' with a different definition: replacing [" + existingDefinition +
                                "] with [" + beanDefinition + "]");
                    }
                // 其它,打印 debug 日志
                } else {
                    if (logger.isTraceEnabled()) {
          //用同等的 Bean 覆盖旧的 Bean,这里指的是 equals 方法返回 true 的 Bean
                        logger.trace("Overriding bean definition for bean '" + beanName +
                                "' with an equivalent definition: replacing [" + existingDefinition +
                                "] with [" + beanDefinition + "]");
                    }
                }
                // 允许覆盖,直接覆盖原有的 BeanDefinition 到 beanDefinitionMap 中。
                this.beanDefinitionMap.put(beanName, beanDefinition);
            // 如果未存在
            } else {
        // 判断是否已经有其他的 Bean 开始初始化了.如果开始了则需要对 beanDefinitionMap 进行并发控制
          // 注意,"注册Bean" 这个动作结束,Bean 依然还没有初始化,我们后面会有大篇幅说初始化过程,
          // 在 Spring 容器启动的最后,会 预初始化 所有的 singleton beans
                if (hasBeanCreationStarted()) {
                    // beanDefinitionMap 为全局变量,避免并发情况
                    synchronized (this.beanDefinitionMap) {
                        // 添加到 BeanDefinition 到 beanDefinitionMap 中。
                        this.beanDefinitionMap.put(beanName, beanDefinition);
                        // 添加 beanName 到 beanDefinitionNames 中
                        List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                        updatedDefinitions.addAll(this.beanDefinitionNames);
                        updatedDefinitions.add(beanName);
                        this.beanDefinitionNames = updatedDefinitions;
                        // 从 manualSingletonNames 移除 beanName
                        if (this.manualSingletonNames.contains(beanName)) {
                            Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                            updatedSingletons.remove(beanName);
                            this.manualSingletonNames = updatedSingletons;
                        }
                    }
                } else {
                    // 添加到 BeanDefinition 到 beanDefinitionMap 中。 
                  //这个 map 保存了所有的 BeanDefinition
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    // 添加 beanName 到 beanDefinitionNames 中
                    //// 这是个 ArrayList,所以会按照 bean 配置的顺序保存每一个注册的 Bean 的名字
                    this.beanDefinitionNames.add(beanName);
                    // 从 manualSingletonNames 移除 beanName
                  //这是个 LinkedHashSet,代表的是手动注册的 singleton bean,
             // 注意这里是 remove 方法,到这里的 Bean 当然不是手动注册的
             // 手动指的是通过调用以下方法注册的 bean :
             //     registerSingleton(String beanName, Object singletonObject)
             // 这不是重点,解释只是为了不让大家疑惑。Spring 会在后面"手动"注册一些 Bean,
           // 如 "environment"、"systemProperties" 等 bean,我们自己也可以在运行时注册 Bean 到容器中的
                    this.manualSingletonNames.remove(beanName);
                }
    
                this.frozenBeanDefinitionNames = null;
            }
    
            // 重新设置 beanName 对应的缓存
            if (existingDefinition != null || containsSingleton(beanName)) {
                resetBeanDefinition(beanName);
            }
        }
    //省略其他代码
    }
    

    总结一下,到这里已经初始化了Bean 容器(DefaultListableBeanFactory),<bean />配置也相应的转换为了一个个 BeanDefinition(先变成Document,根据标签挨个解析成BeanDefinitionHolder),然后注册了各个 BeanDefinition到注册中心(一个ConcurrentHashMap),并且发送了注册事件[(7)中的DefaultBeanDefinitionDocumentReader#processBeanDefinition] 。

    3. 总结

    可以借助一张图来总结一下这个流程:


    image.png

    相关文章

      网友评论

          本文标题:IOC方法入口refresh()、 prepareRefresh

          本文链接:https://www.haomeiwen.com/subject/bqierktx.html