IOC方法入口refresh()、 prepareRefresh()、obtainFreshBeanFactory()

本系列大量参考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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266