美文网首页
Spring IOC的加载过程

Spring IOC的加载过程

作者: 耐得千事烦 | 来源:发表于2019-08-06 09:39 被阅读0次

    先来一段编程式使用BeanFactory来加载bean:

    //创建配置文件的抽象资源,包含了BeanDefinition的定义信息
    ClassPathResource classPathResource = new ClassPathResource("beans.xml");
    //创建一个BeanFactory容器
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    //创建一个载入BeanDefinition的读取器,通过使用读取配置文件的资源然后通过回调配置给BeanFactory
    XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    xmlBeanDefinitionReader.loadBeanDefinitions(classPathResource);
    

    通过以上代码,一个基础的IOC容器就被创建起来了,接下来则是通过调用getBean的方法来完成bean的初始化工作,这个就放在下一篇来讲。

    当然在真正使用spring IOC容器的场景下,是不需要我们使用编码来进行加载容器的工作的,一切都由spring框架帮我们自动完成的。下面将以常用的ClassPathXmlApplicationContext为例子来进行讲解。
    先看一段代码:

    //编码
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    
    //来自于ClassPathXmlApplicationContext
    //ClassPathXmlApplicationContext的构造器方法
    public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
            this(new String[] {configLocation}, true, null);
        }
    
    //来自于ClassPathXmlApplicationContext
    //真正调用的构造器方法
    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
        //如果已经有 ApplicationContext 并需要配置成父子关系,那么调用这个构造方法   
        super(parent);
        //根据提供的路径,处理成配置文件数组(以分号、逗号、空格、tab、换行符分割)
        setConfigLocations(configLocations);
        if (refresh) {
          refresh(); // 核心的方法
        }
    }
    

    接下里就是最核心的方法了,也是整个创建容器的通用步骤:

    //来自于AbstractApplicationContext
    public void refresh() throws BeansException, IllegalStateException {
            //进行加锁处理
        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);
                    }
                        // 销毁已经初始化的 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();
                  }
          }
    }
    

    本章重点部分就是解读这个obtainFreshBeanFactory方法

    //来自于AbstractApplicationContext
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
            // 关闭旧的 BeanFactory (如果有),创建新的 BeanFactory,加载 Bean 定义、注册 Bean 等等
            refreshBeanFactory();
            // 返回刚刚创建的 BeanFactory
            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
            if (logger.isDebugEnabled()) {
                logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
            }
            return beanFactory;
    }
    

    继续看refreshBeanFactory方法

    //来自于AbstractRefreshableApplicationContext
    @Override
    protected final void refreshBeanFactory() throws BeansException {
       // 如果 ApplicationContext 中已经加载过 BeanFactory 了,销毁所有 Bean,关闭 BeanFactory
       // 注意,应用中 BeanFactory 本来就是可以多个的,这里可不是说应用全局是否有 BeanFactory,而是当前
       // ApplicationContext 是否有 BeanFactory
       if (hasBeanFactory()) {
          destroyBeans();
          closeBeanFactory();
       }
       try {
          // 初始化一个 DefaultListableBeanFactory,为什么用这个,我们马上说。
          DefaultListableBeanFactory beanFactory = createBeanFactory();
          // 用于 BeanFactory 的序列化
          beanFactory.setSerializationId(getId());
    
          
          // 设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用
          customizeBeanFactory(beanFactory);
    
          // 加载 Bean 到 BeanFactory 中,重点方法
          loadBeanDefinitions(beanFactory);
          synchronized (this.beanFactoryMonitor) {
             this.beanFactory = beanFactory;
          }
       }
       catch (IOException ex) {
          throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
       }
    }
    

    虽然ApplicationContext是继承了BeanFactory接口,但是个人认为它不应该是被认为是BeanFactory的子类,而是ApplicationContext内部是持有了一个实例化的BeanFctory(其实就是DefaultListableBeanFactory),而有关于BeanFctory的相关操作都是交给这个实例来处理的

    • 问题:为什么ApplicationContext持有的是DefaultListableBeanFactory类呢?
      答:可以先来看下面这张BeanFactory继承图
    BeanFactory继承图

    通过上面这张图可以看到作为最下面的DefaultListableBeanFactory完全实现了BeanFactory相关的所有接口方法,这也是为什么要选择DefaultListableBeanFactory来作为ApplicationContext持有的BeanFactory实例。

    • 问题:bean到底是什么?
      答:Bean是由IOC容器管理的实例,而在代码层面来说Bean就是BeanDefinition的实例。BeanDefinition 中保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等。

    接下来看看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();
    }
    

    这个 BeanDefinition 其实已经包含很多的信息了,我们可以通过这些信息来创建需要管理的实例对象。
    有了 BeanDefinition 的概念以后,我们再往下看 refreshBeanFactory() 方法中的剩余部分:

    // 设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用
    customizeBeanFactory(beanFactory);
    // 加载 Bean 到 BeanFactory 中,重点方法
    loadBeanDefinitions(beanFactory);
    

    customizeBeanFactory(beanFactory) 比较简单,就是配置是否允许 BeanDefinition 覆盖、是否允许循环引用。

    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
       if (this.allowBeanDefinitionOverriding != null) {
          // 是否允许 Bean 定义覆盖
          beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
       }
       if (this.allowCircularReferences != null) {
          // 是否允许 Bean 间的循环依赖
          beanFactory.setAllowCircularReferences(this.allowCircularReferences);
       }
    }
    

    BeanDefinition 的覆盖问题可能会碰到这个坑,就是在配置文件中定义 bean 时使用了相同的 id 或 name,默认情况下,allowBeanDefinitionOverriding属性为 null,如果在同一配置文件中重复了,会抛错,但是如果不是同一配置文件中,会发生覆盖。
    循环引用也很好理解:A 依赖 B,而 B 依赖 A。或 A 依赖 B,B 依赖 C,而 C 依赖 A。
    默认情况下,Spring 允许循环依赖,当然如果你在 A 的构造方法中依赖 B,在 B 的构造方法中依赖 A 是不行的。

    加载 Bean: loadBeanDefinitions
    这个方法将根据配置,加载各个 Bean,然后放到 BeanFactory 中。
    读取配置的操作在 XmlBeanDefinitionReader 中,其负责加载配置、解析。

    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // 为指定的beanFactory创建一个XmlBeanDefinitionReader
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
        // 根据context的环境来配置beanDefinitionReader的环境信息
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
    
        // 初始化 BeanDefinitionReader,其实这个是提供给子类覆写的,
        initBeanDefinitionReader(beanDefinitionReader);
        // 重点
        loadBeanDefinitions(beanDefinitionReader);
    }
    
    
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        // 虽然有两个分支,不过第二个分支很快通过解析路径转换为 Resource 以后也会进到这里
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
    }
    
    
    //来自于AbstractBeanDefinitionReader
    @Override
    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
        Assert.notNull(resources, "Resource array must not be null");
        int counter = 0;
        // 这里是个 for 循环,也就是每个文件是一个 resource
        for (Resource resource : resources) {
            counter += loadBeanDefinitions(resource);
        }
        // 最后返回 counter,表示总共加载了多少的 BeanDefinition
        return counter;
    }
    
    //来自于XmlBeanDefinitionReader
    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
       return loadBeanDefinitions(new EncodedResource(resource));
    }
    

    休息一下,喝个水啥的,我们继续往下看

    // 来自于XmlBeanDefinitionReader 314行
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
       Assert.notNull(encodedResource, "EncodedResource must not be null");
       if (logger.isInfoEnabled()) {
          logger.info("Loading XML bean definitions from " + encodedResource.getResource());
       }
       // 用一个 ThreadLocal 来存放配置文件资源
       Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
       if (currentResources == null) {
          currentResources = new HashSet<EncodedResource>(4);
          this.resourcesCurrentlyBeingLoaded.set(currentResources);
       }
       if (!currentResources.add(encodedResource)) {
          throw new BeanDefinitionStoreException(
                "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
       }
       try {
          // 获取资源的输入流
          InputStream inputStream = encodedResource.getResource().getInputStream();
          try {
             InputSource inputSource = new InputSource(inputStream);
             if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
             }
             // 核心部分是这里,往下面看
             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();
          }
       }
    }
    
    // 来自于XmlBeanDefinitionReader 
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
       try {
          // 这里就不看了,将 xml 文件转换为 Document 对象
          Document doc = doLoadDocument(inputSource, resource);
          // 继续
          return registerBeanDefinitions(doc, resource);
       }
       catch (...
    }
    // 来自于XmlBeanDefinitionReader 第 505 行
    // 返回值:返回从当前配置文件加载了多少数量的 Bean
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
       BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
       int countBefore = getRegistry().getBeanDefinitionCount();
       // 这里
       documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
       return getRegistry().getBeanDefinitionCount() - countBefore;
    }
    
    
    // 来自于DefaultBeanDefinitionDocumentReader 90行
    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
       this.readerContext = readerContext;
       logger.debug("Loading bean definitions");
       Element root = doc.getDocumentElement();
       // 从 xml 根节点开始解析文件
       doRegisterBeanDefinitions(root);
    }
    

    经过漫长的调用,一个配置文件终于转换为一个DOM 树了,注意,这里指的是其中一个配置文件,不是所有的,可以看到上面有个 for 循环的。下面开始从根节点开始解析:

    // 来自于DefaultBeanDefinitionDocumentReader,第116行
    protected void doRegisterBeanDefinitions(Element root) {
       // 我们看名字就知道,BeanDefinitionParserDelegate 必定是一个重要的类,它负责解析 Bean 定义,
       // 这里为什么要定义一个 parent? 是递归问题,
       // 因为 <beans /> 内部是可以定义 <beans /> 的,所以这个方法的 root 其实不一定就是 xml 的根节点,也可以是嵌套在里面的 <beans /> 节点,从源码分析的角度,我们当做根节点就好了
       BeanDefinitionParserDelegate parent = this.delegate;
       this.delegate = createDelegate(getReaderContext(), root, parent);
    
       if (this.delegate.isDefaultNamespace(root)) {
          // 这块说的是根节点 <beans ... profile="dev" /> 中的 profile 是否是当前环境需要的,
          // 如果当前环境配置的 profile 不包含此 profile,那就直接 return 了,不对此 <beans /> 解析
          String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
          if (StringUtils.hasText(profileSpec)) {
             String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                   profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
             if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if (logger.isInfoEnabled()) {
                   logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                         "] not matching: " + getReaderContext().getResource());
                }
                return;
             }
          }
       }
    
       preProcessXml(root); // 钩子
       // 继续看
       parseBeanDefinitions(root, this.delegate);
       postProcessXml(root); // 钩子
    
       this.delegate = parent;
    }
    

    接下来,看核心解析方法 parseBeanDefinitions(root, this.delegate) :

    //来自于DefaultBeanDefinitionDocumentReader 第161行
    // default namespace 涉及到的就四个标签 <import />、<alias />、<bean /> 和 <beans />,
    // 其他的属于 custom 的
    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)) {
                   // 解析 default namespace 下面的几个元素
                   parseDefaultElement(ele, delegate);
                }
                else {
                   // 解析其他 namespace 的元素
                   delegate.parseCustomElement(ele);
                }
             }
          }
       }
       else {
          delegate.parseCustomElement(root);
       }
    }
    

    以上代码有俩个分支分别是 parseDefaultElement(ele, delegate) 和delegate.parseCustomElement(ele)。
    parseDefaultElement(ele, delegate) 代表解析的节点是 <import />、<alias />、<bean />、<beans /> 这几个。
    而对于其他的标签,将进入到 delegate.parseCustomElement(element) 这个分支。如我们经常会使用到的 <mvc />、<task />、<context />、<aop />等。
    这些属于扩展,如果需要使用上面这些 ”非 default“ 标签,那么上面的 xml 头部的地方也要引入相应的 namespace 和 .xsd 文件的路径,如下所示。同时代码中需要提供相应的 parser 来解析,如 MvcNamespaceHandler、TaskNamespaceHandler、ContextNamespaceHandler、AopNamespaceHandler 等。

    继续看处理 default 标签的方法:

    //来自于DefaultBeanDefinitionDocumentReader, 第182行
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
       if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
          // 处理 <import /> 标签
          importBeanDefinitionResource(ele);
       }
       else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
          // 处理 <alias /> 标签定义
          // <alias name="fromName" alias="toName"/>
          processAliasRegistration(ele);
       }
       else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
          // 处理 <bean /> 标签定义,这也算是我们的重点吧
          processBeanDefinition(ele, delegate);
       }
       else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
          // 如果碰到的是嵌套的 <beans /> 标签,需要递归
          doRegisterBeanDefinitions(ele);
       }
    }
    

    下面是 processBeanDefinition 解析 <bean /> 标签:

    // DefaultBeanDefinitionDocumentReader,298行
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
       // 将 <bean /> 节点中的信息提取出来,然后封装到一个 BeanDefinitionHolder 中,细节往下看
       BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    
       if (bdHolder != null) {
          bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
          try {
             // Register the final decorated instance.
             BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
          }
          catch (BeanDefinitionStoreException ex) {
             getReaderContext().error("Failed to register bean definition with name '" +
                   bdHolder.getBeanName() + "'", ele, ex);
          }
          // 发送注册事件
          getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
       }
    }
    

    解析之前先看一下下面这个表格:

    Property 描述
    class 类的全限定名
    name 可指定 id、name(用逗号、分号、空格分隔)
    scope 作用域
    constructor arguments 指定构造参数
    properties 设置属性的值
    autowiring mode no(默认值)、byName、byType、 constructor
    lazy-initialization mode 是否懒加载(如果被非懒加载的bean依赖了那么其实也就不能懒加载了)
    initialization method bean 属性设置完成后,会调用这个方法
    destruction method bean 销毁后的回调方法

    有了以上这些知识以后,我们再继续往里看怎么解析 bean 元素,是怎么转换到 BeanDefinitionHolder 的。

    // 来自于BeanDefinitionParserDelegate 428行
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
        return parseBeanDefinitionElement(ele, null);
    }
    
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
       String id = ele.getAttribute(ID_ATTRIBUTE);
       String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    
       List<String> aliases = new ArrayList<String>();
    
       // 将 name 属性的定义按照 “逗号、分号、空格” 切分,形成一个 别名列表数组,
       // 当然,如果你不定义 name 属性的话,就是空的了
       if (StringUtils.hasLength(nameAttr)) {
          String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
          aliases.addAll(Arrays.asList(nameArr));
       }
    
       String beanName = id;
       // 如果没有指定id, 那么用别名列表的第一个名字作为beanName
       if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
          beanName = aliases.remove(0);
          if (logger.isDebugEnabled()) {
             logger.debug("No XML 'id' specified - using '" + beanName +
                   "' as bean name and " + aliases + " as aliases");
          }
       }
    
       if (containingBean == null) {
          checkNameUniqueness(beanName, aliases, ele);
       }
    
       // 根据 <bean ...>...</bean> 中的配置创建 BeanDefinition,然后把配置中的信息都设置到实例中,
       // 细节后面细说,先知道下面这行结束后,一个 BeanDefinition 实例就出来了。
       AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    
       // 到这里,整个 <bean /> 标签就算解析结束了,一个 BeanDefinition 就形成了。
       if (beanDefinition != null) {
          // 如果都没有设置 id 和 name,那么此时的 beanName 就会为 null,进入下面这块代码产生
          if (!StringUtils.hasText(beanName)) {
             try {
                if (containingBean != null) {// 按照我们的思路,这里 containingBean 是 null 的
                   beanName = BeanDefinitionReaderUtils.generateBeanName(
                         beanDefinition, this.readerContext.getRegistry(), true);
                }
                else {
                   // 如果我们不定义 id 和 name,那么则由下面的这个方法来创建
                   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.isDebugEnabled()) {
                   logger.debug("Neither XML 'id' nor 'name' specified - " +
                         "using generated bean name [" + beanName + "]");
                }
             }
             catch (Exception ex) {
                error(ex.getMessage(), ele);
                return null;
             }
          }
          String[] aliasesArray = StringUtils.toStringArray(aliases);
          // 返回 BeanDefinitionHolder
          return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
       }
       return null;
    }
    

    然后,我们再看看怎么根据配置创建 BeanDefinition 实例的:

    public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) {
    
       this.parseState.push(new BeanEntry(beanName));
    
       String className = null;
       if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
          className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
       }
    
       try {
          String parent = null;
          if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
             parent = ele.getAttribute(PARENT_ATTRIBUTE);
          }
          // 创建 BeanDefinition,然后设置类信息而已,很简单
          AbstractBeanDefinition bd = createBeanDefinition(className, parent);
    
          // 设置 BeanDefinition 的一堆属性,这些属性定义在 AbstractBeanDefinition 中
          parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
          bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    
          /**
           * 下面的一堆是解析 <bean>......</bean> 内部的子元素,
           * 解析出来以后的信息都放到 bd 的属性中
           */
    
          // 解析 <meta />
          parseMetaElements(ele, bd);
          // 解析 <lookup-method />
          parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
          // 解析 <replaced-method />
          parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
        // 解析 <constructor-arg />
          parseConstructorArgElements(ele, bd);
          // 解析 <property />
          parsePropertyElements(ele, bd);
          // 解析 <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 实例。注意,是一个。
    我们回到解析 <bean /> 的入口方法:

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
       // 将 <bean /> 节点转换为 BeanDefinitionHolder,就是上面说的一堆
       BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
       if (bdHolder != null) {
          // 如果有自定义属性的话,进行相应的解析,先忽略
          bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
          try {
             // 我们把这步叫做 注册Bean 吧
             BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
          }
          catch (BeanDefinitionStoreException ex) {
             getReaderContext().error("Failed to register bean definition with name '" +
                   bdHolder.getBeanName() + "'", ele, ex);
          }
          // 注册完成后,发送事件,本文不展开说这个
          getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
       }
    }
    

    这里已经根据一个 <bean /> 标签产生了一个 BeanDefinitionHolder 的实例,这个实例里面也就是一个 BeanDefinition 的实例和它的 beanName、aliases 这三个信息,然后我们准备注册这个 BeanDefinition,最后,把这个注册事件发送出去。

    下面,我们开始说说注册 Bean 吧。
    注册 Bean

    //来自于BeanDefinitionReaderUtils 143行
    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);
          }
       }
    }
    

    别名注册的放一边,我们看看怎么注册 Bean。

    //来自于DefaultListableBeanFactory 793行
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
          throws BeanDefinitionStoreException {
    
       Assert.hasText(beanName, "Bean name must not be empty");
       Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    
       if (beanDefinition instanceof AbstractBeanDefinition) {
          try {
             ((AbstractBeanDefinition) beanDefinition).validate();
          }
          catch (BeanDefinitionValidationException ex) {
             throw new BeanDefinitionStoreException(...);
          }
       }
    
       // old? 还记得 “允许 bean 覆盖” 这个配置吗?allowBeanDefinitionOverriding
       BeanDefinition oldBeanDefinition;
    
       // 之后会看到,所有的 Bean 注册后会放入这个 beanDefinitionMap 中
       oldBeanDefinition = this.beanDefinitionMap.get(beanName);
    
       // 处理重复名称的 Bean 定义的情况
       if (oldBeanDefinition != null) {
          if (!isAllowBeanDefinitionOverriding()) {
             // 如果不允许覆盖的话,抛异常
             throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription()...
          }
          else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
             // log...用框架定义的 Bean 覆盖用户自定义的 Bean 
          }
          else if (!beanDefinition.equals(oldBeanDefinition)) {
             // log...用新的 Bean 覆盖旧的 Bean
          }
          else {
             // log...用同等的 Bean 覆盖旧的 Bean,这里指的是 equals 方法返回 true 的 Bean
          }
          // 覆盖
          this.beanDefinitionMap.put(beanName, beanDefinition);
       }
       else {
          // 判断是否已经有其他的 Bean 开始初始化了.
          // 注意,"注册Bean" 这个动作结束,Bean 依然还没有初始化,我们后面会有大篇幅说初始化过程,
          // 在 Spring 容器启动的最后,会 预初始化 所有的 singleton beans
          if (hasBeanCreationStarted()) {
             // Cannot modify startup-time collection elements anymore (for stable iteration)
             synchronized (this.beanDefinitionMap) {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                this.beanDefinitionNames = updatedDefinitions;
                if (this.manualSingletonNames.contains(beanName)) {
                   Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
                   updatedSingletons.remove(beanName);
                   this.manualSingletonNames = updatedSingletons;
                }
             }
          }
          else {
             // 最正常的应该是进到这个分支。
    
             // 将 BeanDefinition 放到这个 map 中,这个 map 保存了所有的 BeanDefinition
             this.beanDefinitionMap.put(beanName, beanDefinition);
             // 这是个 ArrayList,所以会按照 bean 配置的顺序保存每一个注册的 Bean 的名字
             this.beanDefinitionNames.add(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;
       }
    
       if (oldBeanDefinition != null || containsSingleton(beanName)) {
          resetBeanDefinition(beanName);
       }
    }
    

    总结一下,到这里已经初始化了 Bean 容器,<bean /> 配置也相应的转换为了一个个 BeanDefinition,然后注册了各个 BeanDefinition 到注册中心,并且发送了注册事件。

    至此,IOC容器的加载过程就全部说完了,接下来一个章节专门说说有关于bean的初始化过程

    相关文章

      网友评论

          本文标题:Spring IOC的加载过程

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