# Spring Bean的生命周期(二) - BeanDefinition

我们开始来研究 bean 完整生命周期的 `BeanDefinition` 阶段了，这一阶段主要发生了以下几件事情：

* 加载配置文件、配置类。
* 解析配置文件、配置类并封装为 `BeanDefinition`。
* 编程式注入额外的 `BeanDefinition`。
* `BeanDefinition` 的后置处理。

## 1. 加载xml配置文件

来，跟着小册来到 `LifecycleSourceXmlApplication` 的测试代码：

```java
public static void main(String[] args) throws Exception {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext();
    ctx.setConfigLocation("lifecycle/bean-source.xml");
}
```

### 1.1 保存配置文件路径

这里 xml 配置文件的加载会使用 `ClassPathXmlApplicationContext` 的 `setConfigLocation` 方法，点进去可以发现它只是在 `AbstractRefreshableConfigApplicationContext` 中，给 `configLocations` 设置了配置文件的路径存放而已。

通过源码，也能看出来最终是将传入的路径切分，并顺序存入 `configLocations` 中：

```java
public void setConfigLocation(String location) {
    // 切分配置文件路径
    setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
}

public void setConfigLocations(@Nullable String... locations) {
    if (locations != null) {
        // assert ......
        this.configLocations = new String[locations.length];
        for (int i = 0; i < locations.length; i++) {
            // 存入ApplicationContext中
            this.configLocations[i] = resolvePath(locations[i]).trim();
        }
    }
    else {
        this.configLocations = null;
    }
}
```

Debug 时，也能发现，它最终把测试代码中设置的 xml 配置文件的路径都保存了。

![img](https://image.ldbmcs.com/rJpBVB.jpg)

### 1.2 加载配置文件并解析

当执行 `ApplicationContext` 的 `refresh` 方法后，会开始刷新（初始化）IOC 容器，这里面有 13 个步骤，前面已经看过不少次了：

```java
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        prepareRefresh();
        // 2. 初始化BeanFactory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        prepareBeanFactory(beanFactory);
        
        // ......
    }
}
```

这里面我把第 2 步 `obtainFreshBeanFactory` 方法标注出来了，加载配置文件并解析的动作就在这里面，我们可以来研究一下。

```java
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    return getBeanFactory();
}
```

这个方法只有两个动作：刷新 `BeanFactory` ，然后获取它。**毫无疑问，刷新的动作中包含配置文件的加载和解析**，我们继续往里看。

`refreshBeanFactory` 方法是 `AbstractApplicationContext` 定义的抽象方法，很明显这里又是**模板方法模式**的体现了。由于当前我们正在研究的是基于 xml 配置文件的 `ApplicationContext` ，所以要进入 `AbstractRefreshableApplicationContext` 中：

```java
protected final void refreshBeanFactory() throws BeansException {
    // 存在BeanFactory则先销毁
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        // 创建BeanFactory
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory);
        // 【1.3】加载配置文件
        loadBeanDefinitions(beanFactory);
        this.beanFactory = beanFactory;
    } // catch ......
}
```

看一下源码的大体流程，在前面 15 章中我们已经了解到，**基于 xml 配置文件的** `**ApplicationContext**` **可以反复刷新加载 IOC 容器**，所以此处有已经存在的判断：如果当前 `ApplicationContext` 中组合的 `BeanFactory` 已经存在，则销毁原来的 `BeanFactory` ，并重新创建。

关注重点，这里面加载配置文件的动作是 `loadBeanDefinitions` 。这个方法相当复杂且困难，小册会选取最重要的部分来解析。

### 1.3 loadBeanDefinitions

点进去，发现 `loadBeanDefinitions` 又是一个抽象方法，在 `AbstractXmlApplicationContext` 中可以找到对应的实现：

```java
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // xml配置文件由XmlBeanDefinitionReader解析
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // 配置上下文环境、资源加载器等
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    initBeanDefinitionReader(beanDefinitionReader);
    // 使用xml解析器 解析xml配置文件
    loadBeanDefinitions(beanDefinitionReader);
}
```

注意这里面，它创建了一个 `XmlBeanDefinitionReader` ，小伙伴们一定不陌生吧，它在第 14 章就出现过，我们说它就是**加载和解析 xml 配置文件的核心 API** 。直接看最底下吧，它把这个 `XmlBeanDefinitionReader` 作为参数传入重载的 `loadBeanDefinitions` 方法：

```java
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
        reader.loadBeanDefinitions(configResources);
    }
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        // 【1.4】加载配置文件资源路径的xml配置文件
        reader.loadBeanDefinitions(configLocations);
    }
}
```

注意这段逻辑分两部分，一个是处理已经加载好的现成的 `Resource` ，一个是处理指定好的配置文件资源路径。由于测试代码中并没有直接指定 Resource ，故此处主要研究第 8 行的 `loadBeanDefinitions(configLocations)` 。

Debug 至此处，同样也能发现 `reader` 与 `configLocations` 都准备好了：

![img](https://image.ldbmcs.com/NQXHeM.jpg)

### 1.4 XmlBeanDefinitionReader加载配置文件

```java
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
    // assert ......
    int count = 0;
    for (String location : locations) {
        count += loadBeanDefinitions(location);
    }
    return count;
}
```

点进来，发现它传入的是一组配置文件，那自然就会循环一个个的加载咯。循环自然不是重点，我们进入内部的 `loadBeanDefinitions(location)` 中：

```java
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(location, null);
}

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
    ResourceLoader resourceLoader = getResourceLoader();
    if (resourceLoader == null) {
        // throw ex ......
    }

    if (resourceLoader instanceof ResourcePatternResolver) {
        try {
            // 根据传入的路径规则，匹配所有符合的xml配置文件
            Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
            int count = loadBeanDefinitions(resources);
            if (actualResources != null) {
                Collections.addAll(actualResources, resources);
            }
            // logger ......
            return count;
        } // catch ......
    }
    else {
        // 每次只能解析一个xml配置文件
        Resource resource = resourceLoader.getResource(location);
        // 【解析】
        int count = loadBeanDefinitions(resource);
        if (actualResources != null) {
            actualResources.add(resource);
        }
        // logger ......
        return count;
    }
}
```

可以发现，这里的核心逻辑只有两个动作：**1) 根据传入的资源路径，获取 xml 配置文件**；**2) 解析 xml 配置文件**。其余的动作都是保证程序正常执行的代码，咱就不研究了，还是核心关注 `loadBeanDefinitions` 的方法实现。

```java
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    return loadBeanDefinitions(new EncodedResource(resource));
}
```

这一步给原有的 xml 配置文件的 `Resource` 封装包装了一层编码而已，没啥需要关注的，继续往里看。

```java
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    // assert logger ......
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    if (!currentResources.add(encodedResource)) {
        // throw ex ......
    }

    try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
        InputSource inputSource = new InputSource(inputStream);
        if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
        }
        // 【真正干活的】
        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    } // catch ......
    finally {
        currentResources.remove(encodedResource);
        if (currentResources.isEmpty()) {
            this.resourcesCurrentlyBeingLoaded.remove();
        }
    }
}
```

方法逻辑看上去很复杂，但是抛开细枝末节，最核心的动作还是那个以 **do** 开头的 `doLoadBeanDefinitions` ，它才是真正干活的，所以看它就完事了。

### 1.5 doLoadBeanDefinitions - 读取配置文件

```java
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {
    try {
        Document doc = doLoadDocument(inputSource, resource);
        int count = registerBeanDefinitions(doc, resource);
        // logger ......
        return count;
    } // catch ......
}
```

一上来又是一个 **do** 开头的方法，得了，我就看你。。。诶等一下，`doLoadDocument` 这很明显是解析文档吧，这我们也没必要看吧（也看不懂），重点是下面的那句 `registerBeanDefinitions` ，这才是我们最关心的吧！

```java
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` ，然后调用它的 `registerBeanDefinitions` 方法（不再是 `XmlBeanDefinitionReader` 的方法了）：

```java
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    doRegisterBeanDefinitions(doc.getDocumentElement());
}
```

又又又是这个套路！**xxx 方法最终调 doXxx 方法干活**，得了那咱继续往里走：

```java
protected void doRegisterBeanDefinitions(Element root) {
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);

    if (this.delegate.isDefaultNamespace(root)) {
        // 取<beans>上的profile属性
        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)) {
                // logger ......
                return;
            }
        }
    }

    preProcessXml(root);
    // 【解析xml】
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);

    this.delegate = parent;
}
```

注意看这段源码，上面它会先把 xml 配置文件中声明的 **profile** 取出来，并根据 `Environment` 中配置好的 **profile** 决定是否继续解析（ profile 的过滤）.

接下来，后面就是 xml 配置文件的解析动作了，在这前后有一个预处理和后处理动作，不过默认情况下这里是没有实现的（模板方法罢了），所以我们只需要看 `parseBeanDefinitions` 就可以。

### 1.6 parseBeanDefinitions - 解析xml

```java
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;
                // 解析<beans>中的元素
                if (delegate.isDefaultNamespace(ele)) {
                    parseDefaultElement(ele, delegate);
                }
                else {
                    // 解析其它命名空间中的元素
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}
```

Spring 如何解析 xml ，我们不关心，但是**如何从解析完的 xml 中获取关键信息，以及封装** `**BeanDefinition**` ，这才是我们要关注的。

这个方法一进来的时候，我们在此 Debug 停一下，可以发现它已经是解析并封装成 `Element` 的形式了，而且根节点是 `<beans>` ，它还有几个默认属性等等，我们都比较熟悉了：

![img](https://image.ldbmcs.com/YWoyvm.jpg)

OK ，继续回到源码，源码中可以看到，每次循环出来的 `Node` 都会尝试着转成 `Element` 去解析，而解析的动作主要是 `parseDefaultElement` ，它会解析 `<beans>` 标签下的 xml 元素。马上就要水落石出了，我们点进去看：

```java
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        importBeanDefinitionResource(ele);
    }
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        processAliasRegistration(ele);
    }
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        processBeanDefinition(ele, delegate);
    }
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // recurse
        doRegisterBeanDefinitions(ele);
    }
}
```

终于看到真实的面貌了！！！这里它会解析 `<import>` 标签、`<alias>` 标签、`<bean>` 标签，以及递归解析嵌套的 `<beans>` 标签！

### 1.7 processBeanDefinition - 解析 `<bean>` 标签

既然我们还是在研究 `BeanDefinition` ，那我们就研究 `processBeanDefinition` 方法啦，翻开源码，可以发现逻辑还是比较简单的：

```java
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 解析xml元素为BeanDefinition
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        // 解析<bean>中嵌套的自定义标签
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // BeanDefinition注册到BeanDefinitionRegistry
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        } // catch ......
        // Send registration event.
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}
```

可以发现，整个动作一气呵成，其中第一个步骤会把 xml 元素封装为 `BeanDefinitionHolder` ，且不说 holder 是干嘛用，只从名字上就能知道，它内部肯定组合了一个 `BeanDefinition` 。

Debug 至此，可以发现，这个 `Element` 中已经包含了一个 `<bean>` 标签中定义的那些属性了：

![img](https://image.ldbmcs.com/rTtlaN.jpg)

不过源码是如何将 xml 元素封装为 `BeanDefinitionHolder` 的呢？我们还是进去看看为好。

```java
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
    return parseBeanDefinitionElement(ele, null);
}

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    // 取出bean的id
    String id = ele.getAttribute(ID_ATTRIBUTE);
    // 取出bean的name
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

    // 取出bean的alias
    List<String> aliases = new ArrayList<>();
    if (StringUtils.hasLength(nameAttr)) {
        String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        aliases.addAll(Arrays.asList(nameArr));
    }
    // 如果没有给bean赋name，则第一个alias视为name
    String beanName = id;
    if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
        beanName = aliases.remove(0);
        // logger ......
    }

    // 检查bean的name是否有重复
    if (containingBean == null) {
        checkNameUniqueness(beanName, aliases, ele);
    }

    // 解析其余的bean标签元素属性
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    if (beanDefinition != null) {
        // 如果没有给bean赋name，且没有alias，则生成默认的name
        if (!StringUtils.hasText(beanName)) {
            try {
                if (containingBean != null) {
                    beanName = BeanDefinitionReaderUtils.generateBeanName(
                            beanDefinition, this.readerContext.getRegistry(), true);
                } else {
                    beanName = this.readerContext.generateBeanName(beanDefinition);
                    String beanClassName = beanDefinition.getBeanClassName();
                    if (beanClassName != null &&
                            beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                            !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                        aliases.add(beanClassName);
                    }
                }
                // logger ......
            } // catch ......
        }
        String[] aliasesArray = StringUtils.toStringArray(aliases);
        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }

    return null;
}
```

虽说源码篇幅比较长，但总体还是比较容易理解和接受的，而且每一步干的活也都容易看明白。不过看完这段源码之后，小伙伴们可能会疑惑：只有 `id` 和 `name` ，`class` 呢？`scope` 呢？`lazy-init` 呢？莫慌，看到中间还有一个封装好的 `parseBeanDefinitionElement` 方法了吧，这些属性的封装都在这里可以找得到，我们继续往里进。同样的，这段源码篇幅较长，只需要关注带注释的即可：

```java
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的definition名称
    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);
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

        // 解析property属性
        parseMetaElements(ele, bd);
        // 解析其它的属性 ......

        bd.setResource(this.readerContext.getResource());
        bd.setSource(extractSource(ele));

        return bd;
    } // catch finally ......

    return null;
}
```

从这部分，我们终于看到了最最底层的操作：调用 `createBeanDefinition` 方法创建了一个 `BeanDefinition` （它的底层就是第 24 章提到的 `BeanDefinitionReaderUtils.createBeanDefinition` 方法），之后把 `<bean>` 标签中的其它属性、`<bean>` 的子标签的内容都封装起来，而封装 `<bean>` 其它属性的 `parseBeanDefinitionAttributes` 方法中，已经把这些内容都解析到位了：

```java
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
        @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

    // 解析scope
    if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
        error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
    } else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
        bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
    } else if (containingBean != null) {
        // Take default from containing bean in case of an inner bean definition.
        bd.setScope(containingBean.getScope());
    }

    // 解析abstract属性
    if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
        bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
    }

    // 省略部分相似的封装属性的动作 ......
    return bd;
}
```

到这里，xml 配置文件中的 `<bean>` 标签就可以转换为 `BeanDefinition` 了。

#### 1.7.1 BeanDefinitionHolder的意义

最后补充一下前面略过的 `BeanDefinitionHolder` ，我们说它是持有 `BeanDefinition` 的一个包装而已，不过它除了持有之外，还包含了另外的重要信息：**bean 的名称**。

翻看 `BeanDefinitionHolder` 的源码结构，可以发现这里面还组合了 bean 的 **name** 和 alias ：

```java
public class BeanDefinitionHolder implements BeanMetadataElement {

	private final BeanDefinition beanDefinition;

	private final String beanName;

	@Nullable
	private final String[] aliases;
}
```

是不是突然回过神了，`BeanDefinition` 的结构中是没有 name 的！所以才需要这样一个 holder 帮它持有。

### 1.8 小结

简单总结一下这个流程吧：**首先** `**ClassPathXmlApplicationContext**` **在** `**refresh**` **之前，会指定传入的 xml 配置文件的路径，执行** `**refresh**` **方法时，会初始化** `**BeanFactory**` **，触发 xml 配置文件的读取、加载和解析。其中 xml 的读取需要借助** `**XmlBeanDefinitionReader**` **，解析 xml 配置文件则使用** `**DefaultBeanDefinitionDocumentReader**` **，最终解析 xml 中的元素，封装出** `**BeanDefinition**` **，最后注册到** `**BeanDefinitionRegistry**` **。**

## 2. 加载注解配置类

相比较于 xml 配置文件，注解配置类的加载时机会晚一些，它用到了一个至关重要的 `BeanDefinitionRegistryPostProcessor` ，而且无论如何，这个后置处理器都是最最优先执行的，它就是 `**ConfigurationClassPostProcessor**` 。

下面我们还是通过实例来研究注解配置类的加载机制（注意此处要换用 `LifecycleSourceAnnotationApplication` 了哦）。

### 2.1 BeanDefinitionRegistryPostProcessor的调用时机

还是回到 `AbstractApplicationContext` 的 `refresh` 方法中，这次我们要看的是 `BeanFactoryPostProcessor` 和 `BeanDefinitionRegistryPostProcessor` 的执行时机，而它们的执行都在下面所示的第 5 步 `invokeBeanFactoryPostProcessors` 中执行：

```java
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // ......
        try {
            postProcessBeanFactory(beanFactory);
            // 5. 执行BeanFactoryPostProcessor
            invokeBeanFactoryPostProcessors(beanFactory);
            registerBeanPostProcessors(beanFactory);
            // ......
        }
        // catch finally .....
    }
}
```

进来这个方法，发现核心的方法就一句话：

```java
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // 交给代理执行
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

    // 下面是支持AOP的部分（暂时不读）
}
```

好吧，它又是交给一个 **Delegate** 执行，那就进去看这个方法的实现吧。

### 2.2 PostProcessorRegistrationDelegate的实现

**前方高能**！`invokeBeanFactoryPostProcessors` 方法的篇幅实在太长了！！！小册只截取关键的部分吧。

```java
public static void invokeBeanFactoryPostProcessors(
        ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

    Set<String> processedBeans = new HashSet<>();

    if (beanFactory instanceof BeanDefinitionRegistry) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
        List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

        // 该部分会将BeanFactoryPostProcessor与BeanDefinitionRegistryPostProcessor分离开
        for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
            if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                BeanDefinitionRegistryPostProcessor registryProcessor =
                        (BeanDefinitionRegistryPostProcessor) postProcessor;
                registryProcessor.postProcessBeanDefinitionRegistry(registry);
                registryProcessors.add(registryProcessor);
            }
            else {
                regularPostProcessors.add(postProcessor);
            }
        }
        List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

        // 首先，执行实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessors
        String[] postProcessorNames =
                beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        for (String ppName : postProcessorNames) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        currentRegistryProcessors.clear();

        // 接下来，执行实现了Ordered接口的BeanDefinitionRegistryPostProcessors
        postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        for (String ppName : postProcessorNames) {
            if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        currentRegistryProcessors.clear();

        // 最后，执行所有其他BeanDefinitionRegistryPostProcessor
        boolean reiterate = true;
        while (reiterate) {
            reiterate = false;
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                    reiterate = true;
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();
        }

        invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    }

    else {
        invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
    }

    // 执行BeanFactoryPostProcessor ......
}
```

用简单的语言概括，这个方法的执行机制如下：

1. 执行 `BeanDefinitionRegistryPostProcessor` 的 `postProcessBeanDefinitionRegistry` 方法
2. 1. 执行实现了 `PriorityOrdered` 接口的 `BeanDefinitionRegistryPostProcessor`
   2. 执行实现了 `Ordered` 接口的 `BeanDefinitionRegistryPostProcessor`
   3. 执行普通的 `BeanDefinitionRegistryPostProcessor`
3. 执行 `BeanDefinitionRegistryPostProcessor` 的 `postProcessBeanFactory` 方法 同上
4. 执行`BeanFactoryPostProcessor` 的 `postProcessBeanFactory` 方法 同上

这个方法的更详细解读，可参考[《SpringBoot 小册》第 12 章 5.1 节](https://juejin.im/book/6844733814560784397/section/6844733814615310350)，那里面解释的更加详细呦。

在这个长长的方法中，第一个环节它会执行所有实现了 `PriorityOrdered` 接口的 `BeanDefinitionRegistryPostProcessor` ，这里面第一个执行的处理器就是 `ConfigurationClassPostProcessor` ，咱跟过去看一看。

### 2.3 ConfigurationClassPostProcessor的处理

直接定位到 `postProcessBeanDefinitionRegistry` 方法吧：

```java
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    int registryId = System.identityHashCode(registry);
    // check throw ex ......
    this.registriesPostProcessed.add(registryId);

    // 【解析配置类】
    processConfigBeanDefinitions(registry);
}
```

可以发现，在这个方法的最后一行，就是解析配置类中定义的 bean ，并封装为 `BeanDefinition` 。

进入超难领域前的 “按摩” 提醒：`processConfigBeanDefinitions` 方法的内容超级复杂哦（不亚于上面的 xml 配置文件加载），考虑到前面的 SpringBoot 小册中对该部分流程**没有全部剖析**（ **boot 的小册**中对于 IOC **更注重整体应用和容器的生命周期**），所以我们在这里**完整的研究一遍配置类的加载原理**。

进入 `processConfigBeanDefinitions` 方法，来吧，超级长的源码又出现了：

```java
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();

    // 筛选出所有的配置类
    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        // full configuration的解释可参考boot小册12章5.2.1.1节
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
            // logger ......
        } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

    // Return immediately if no @Configuration classes were found
    if (configCandidates.isEmpty()) {
        return;
    }

    // 配置类排序
    configCandidates.sort((bd1, bd2) -> {
        int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
    });

    // 构造默认的BeanNameGenerator bean的名称生成器
    SingletonBeanRegistry sbr = null;
    if (registry instanceof SingletonBeanRegistry) {
        sbr = (SingletonBeanRegistry) registry;
        if (!this.localBeanNameGeneratorSet) {
            BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
            if (generator != null) {
                this.componentScanBeanNameGenerator = generator;
                this.importBeanNameGenerator = generator;
            }
        }
    }

    if (this.environment == null) {
        this.environment = new StandardEnvironment();
    }

    // 真正解析配置类的组件：ConfigurationClassParser
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory, this.problemReporter, this.environment,
            this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    do {
        // 【解析配置类】
        parser.parse(candidates);
        parser.validate();

        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);

        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                    registry, this.sourceExtractor, this.resourceLoader, this.environment,
                    this.importBeanNameGenerator, parser.getImportRegistry());
        }
        // 【加载配置类的内容】
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);
        
        // 一些额外的处理动作
    }
    while (!candidates.isEmpty());

    // 一些额外的处理 ......
}
```

其实如果小伙伴从上往下看的话，应该可以意识到，其实也没那么难吧（当然没那么难，难的在 `parse` 和 `loadBeanDefinitions` 里头）。不过这里面的前置动作还是要注意一下，它初始化了一个 `ConfigurationClassParser` ，这个家伙是用来解析注解配置类的核心 API 。

一大堆前戏都做足了，下面就可以进入 `ConfigurationClassParser` 的 `parse` 方法了。

### 2.4 ConfigurationClassParser#parse - 解析注解配置类

```java
public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            // 注解配置类
            if (bd instanceof AnnotatedBeanDefinition) {
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            // 编程式注入配置类
            else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            // 其他情况
            else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        } // catch ......
    }
    
    // 回调特殊的ImportSelector
    this.deferredImportSelectorHandler.process();
}
```

先整体看一下这个方法的内容哈。上面的 for 循环中，它会把配置类的全限定名拿出来，扔进重载的 `parse` 方法中（注意无论是执行 if-else-if 的哪个分支，最终都是执行重载的 `parse` 方法）；for 循环调用完成后，最底下会让 `deferredImportSelectorHandler` 执行 `process` 方法，这个东西我们完全没见过，这里有必要说明一下。

#### 2.4.1 ImportSelector的扩展

在 SpringFramework 4.0 中，`ImportSelector` 多了一个子接口：`DeferredImportSelector` ，它的执行时机比 `ImportSelector` 更晚，它会在注解配置类的所有解析工作完成后才执行（其实上面的源码就已经解释了这个原理）。

一般情况下，`DeferredImportSelector` 会跟 `@Conditional` 注解配合使用，完成**条件装配**。

#### 2.4.2 deferredImportSelectorHandler的处理逻辑

进入 `DeferredImportSelectorHandler` 的 `process` 方法：

```java
public void process() {
    List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
    this.deferredImportSelectors = null;
    try {
        if (deferredImports != null) {
            DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
            deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
            deferredImports.forEach(handler::register);
            handler.processGroupImports();
        }
    }
    finally {
        this.deferredImportSelectors = new ArrayList<>();
    }
}
```

可以发现这个处理逻辑还是很简单的，它会取出所有解析中存储好的 `DeferredImportSelector` ，并依次执行。由于 `DeferredImportSelector` 的执行时机比较晚，对于 `@Conditional` 条件装配的处理也会更有利，所以这个设计还是不错的。

### 2.5 parse解析配置类

回到正题上，上面的 `ConfigurationClassParser` 中最终都会把配置类传入重载的 `parse` 方法中，参数类型注意是 `ConfigurationClass` ：

```java
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
        return;
    }

    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    if (existingClass != null) {
        // 如果配置类已经被@Import过了，则跳过
        if (configClass.isImported()) {
            if (existingClass.isImported()) {
                existingClass.mergeImportedBy(configClass);
            }
            return;
        }
        else {
            this.configurationClasses.remove(configClass);
            this.knownSuperclasses.values().removeIf(configClass::equals);
        }
    }

    SourceClass sourceClass = asSourceClass(configClass);
    do {
        // 【真正干活的】
        sourceClass = doProcessConfigurationClass(configClass, sourceClass);
    }
    while (sourceClass != null);

    this.configurationClasses.put(configClass, configClass);
}
```

好吧，底下又是调 `**doXXX**` 方法了，那就废话不多说，直接莽进去就完事了。

### 2.6 doProcessConfigurationClass - 解析配置类

又高能！这个方法又是好长呀！不过这里面的部分几乎都有大用途哦，我们分解着来看吧。

#### 2.6.1 处理@Component注解

```java
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
        throws IOException {

    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        processMemberClasses(configClass, sourceClass);
    }
    // .............
}
```

一上来，它就会判断这个类是否有标注 `@Component` 注解。因为所有的 `@Configuration` 类必定是 `@Component` ，所以该逻辑必进。而内部执行的 `processMemberClasses` 方法如下：

```java
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
        Predicate<String> filter) throws IOException {
    // 获取配置类中的所有内部类
    Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
    if (!memberClasses.isEmpty()) {
        List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
        // 循环解析内部类
        for (SourceClass memberClass : memberClasses) {
            // 如果内部类也是配置类，则它们也会被解析
            if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
                    !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
                candidates.add(memberClass);
            }
        }
        OrderComparator.sort(candidates);
        for (SourceClass candidate : candidates) {
            // 防止循环@Import的处理：如果两个配置类互相@Import，则视为错误
            if (this.importStack.contains(configClass)) {
                this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
            } else {
                this.importStack.push(configClass);
                try {
                    // 递归解析内部的配置类
                    processConfigurationClass(candidate.asConfigClass(configClass), filter);
                }
                finally {
                    this.importStack.pop();
                }
            }
        }
    }
}
```

哦 \~ 合计着这个方法是处理内部类的啊，而且还是递归处理，这个套路很像上面 `<beans>` 的 xml 递归解析哦。那好吧，这里面的逻辑也不复杂，扫一眼过去就得了，不要浪费太多时间。

#### 2.6.2 处理@PropertySource注解

```java
		 // ............
    // Process any @PropertySource annotations
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
        // else logger ......
    }
    // ............
```

接下来是处理 `@PropertySource` 注解了，可以发现借助 `AnnotationConfigUtils` 可以很容易的取出配置类上标注的所有注解信息，然后筛选出指定的注解属性即可。而内部的 `processPropertySource` 方法就在真正的封装 `PropertySource` 导入的资源文件：

```java
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
    // 解析@PropertySource注解的属性
    String name = propertySource.getString("name");
    if (!StringUtils.hasLength(name)) {
        name = null;
    }
    String encoding = propertySource.getString("encoding");
    if (!StringUtils.hasLength(encoding)) {
        encoding = null;
    }
    String[] locations = propertySource.getStringArray("value");
    // ......

    for (String location : locations) {
        try {
            // 处理路径，加载资源文件，并添加进Environment中
            String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
            Resource resource = this.resourceLoader.getResource(resolvedLocation);
            addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
        } // catch ......
    }
}
```

前面的一大堆操作都是拿 `@PropertySource` 的一些属性等等，最后的 for 循环中才是封装资源文件，存放进 `Environment` 的部分，整体逻辑也是比较简单的，咱就不深入研究咯（主要关注 bean 相关的东西哈）。

#### 2.6.3 处理@ComponentScan注解

```java
		 // ............
    // Process any @ComponentScan annotations
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
            !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        // 如果有@ComponentScans，则要取出里面所有的@ComponentScan依次扫描
        for (AnnotationAttributes componentScan : componentScans) {
            // 【复杂】借助ComponentScanAnnotationParser扫描
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            // 是否扫描到了其它的注解配置类
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                if (bdCand == null) {
                    bdCand = holder.getBeanDefinition();
                }
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                    // 如果扫描到了，递归解析
                    parse(bdCand.getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }
    // ............
```

接下来的部分是处理 `@ComponentScan` 注解了，整个流程也不复杂。注意一点：`@ComponentScan` 可以标注多个，并且 Spring 4.3 后多了一个 `@ComponentScans` 注解，它可以组合多个 `@ComponentScan` 注解，所以这里是 for 循环解析 `@ComponentScan` 。

中间的部分，它使用 `ComponentScanAnnotationParser` 来委托处理包扫描的工作，可能会有小伙伴产生疑惑：不是包扫描的组件是 `ClassPathBeanDefinitionScanner` 吗？它是谁？先别着急，我们进到 `ComponentScanAnnotationParser` 的 `parse` 方法中，看一看内部的实现：（嗯，第一行就把 `ClassPathBeanDefinitionScanner` 创建出来了 \~ ）

```java
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    // 构造ClassPathBeanDefinitionScanner
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
            componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

    // 解析@ComponentScan中的属性 ......
    Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");

    // 整理要扫描的basePackages
    Set<String> basePackages = new LinkedHashSet<>();
    String[] basePackagesArray = componentScan.getStringArray("basePackages");
    for (String pkg : basePackagesArray) {
        String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        Collections.addAll(basePackages, tokenized);
    }
    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
        basePackages.add(ClassUtils.getPackageName(clazz));
    }
    // 没有声明basePackages，则当前配置类所在的包即为根包
    if (basePackages.isEmpty()) {
        basePackages.add(ClassUtils.getPackageName(declaringClass));
    }

    // ......
    // 【扫描】执行包扫描动作
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}
```

顺序走下来，大概这个方法只干了三件事情：

1. 构造 `ClassPathBeanDefinitionScanner` ，并封装 `@ComponentScan` 注解中的属性
2. 整理要进行包扫描的 `basePackages` ，以及 include 和 exclude 的过滤器
3. 执行包扫描的动作

前面两个步骤都是准备动作，真正的包扫描那还得看 `ClassPathBeanDefinitionScanner` ，来吧，咱直接进到最底下的 `doScan` 方法中：

```java
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    // assert ......
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        // 【真正的包扫描动作在这里】
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            // 处理scope（默认情况下是singleton）
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            // 生成bean的名称
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            // 处理bean中的@Lazy、@Primary等注解
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                // 设置AOP相关的属性（如果支持的话）
                definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                // 注册进BeanDefinitionRegistry
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}
```

从上往下的逻辑条理还是很清晰的，只要扫描到了符合的类（默认被 `@Component` 注解标注的类），就会包装为 `BeanDefinition` ，然后对这些 `BeanDefinition` 进行一些额外的处理，最终注册进 `BeanDefinitionRegistry` 。不过核心的扫描方法还是封装方法了，咱进入 for 循环的第一句 `findCandidateComponents` 方法中：

```java
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    } else {
        return scanCandidateComponents(basePackage);
    }
}

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        // 此处可处理 [/**/service/*Service.class] 这样的表达式
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
        for (Resource resource : resources) {
            if (resource.isReadable()) {
                try {
                    // 加载.class字节码
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    // 如果符合匹配规则，则封装为ScannedGenericBeanDefinition
                    if (isCandidateComponent(metadataReader)) {
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setSource(resource);
                        if (isCandidateComponent(sbd)) {
                            candidates.add(sbd);
                        }
                    }
                } // catch .......
            }
        }
    } // catch ......
    return candidates;
}
```

整体逻辑还是简单的很：它会将带有通配符的 **Ant** 风格（诸如 `/xxx/**/*.class` ）的路径解析出来，并加载到对应的类，封装为 `ScannedGenericBeanDefinition` ，完事。

到这里，包扫描的处理就完成了，`@ComponentScan` 的处理也就结束了。

#### 2.6.4 处理@Import注解

```java
// ............
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);
// ............
```

嚯，就一句话啊，那咱直接点进去吧：

```java
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
        Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
        boolean checkForCircularImports) {

    if (importCandidates.isEmpty()) {
        return;
    }
    // 防止循环@Import导入
    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
        this.importStack.push(configClass);
        try {
            for (SourceClass candidate : importCandidates) {
                // 处理ImportSelector
                if (candidate.isAssignable(ImportSelector.class)) {
                    Class<?> candidateClass = candidate.loadClass();
                    ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                            this.environment, this.resourceLoader, this.registry);
                    Predicate<String> selectorFilter = selector.getExclusionFilter();
                    if (selectorFilter != null) {
                        exclusionFilter = exclusionFilter.or(selectorFilter);
                    }
                    // DeferredImportSelector的执行时机后延
                    if (selector instanceof DeferredImportSelector) {
                        this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
                    } else {
                        // 执行ImportSelector的selectImports方法，并注册导入的类
                        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
                        processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
                    }
                }
                // 处理ImportBeanDefinitionRegistrar
                else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
                    Class<?> candidateClass = candidate.loadClass();
                    ImportBeanDefinitionRegistrar registrar =
                            ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                    this.environment, this.resourceLoader, this.registry);
                    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                }
                else {
                    // 导入普通类 / 配置类
                    this.importStack.registerImport(
                            currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
                    processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
                }
            }
        } // catch ......
        finally {
            this.importStack.pop();
        }
    }
}
```

可以发现，整体逻辑非常有序，它分别对 `ImportSelector` 、`ImportBeanDefinitionRegistrar` 、普通类 / 配置类都做了处理，并递归解析其中存在的配置类，整体并不复杂，小伙伴们看一看有个印象即可，重要的是记住它们处理的位置，以便日后出现问题时排错定位。

#### 2.6.5 处理@ImportResource注解

```java
    // ............
    // Process any @ImportResource annotations
    AnnotationAttributes importResource =
            AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {
        String[] resources = importResource.getStringArray("locations");
        Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
        for (String resource : resources) {
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }
    // ............
```

前面我们学过了，在注解配置类上标注 `@ImportResource` 可以导入 xml 配置文件，而解析这些 `@ImportResource` 的逻辑就在这里。不过看源码的套路中，怎么只是把配置文件的路径放入了 `configClass` 而已呢？咋不解析呀？莫慌，我们之前看 `ConfigurationClassPostProcessor` 的后置处理 `processConfigBeanDefinitions` 方法中，不是在 `parse` 方法下面还有个 `loadBeanDefinitions` 方法嘛，过会我们看看它就知道了。

#### 2.6.6 处理@Bean注解

```java
    // ............
    // Process individual @Bean methods
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }
    // ............
```

合着 `@Bean` 的处理也只是存起来呗？那它啥时候处理啊？不会跟上面的 `@ImportResource` 放在一块处理吧？哎，想法可以保留，过会我们往下看到了自然就揭晓了。不过它是如何把这些 `@Bean` 方法都拿出来的，我们还得去看看 `retrieveBeanMethodMetadata` 方法：

```java
private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
    AnnotationMetadata original = sourceClass.getMetadata();
    // 获取被@Bean注解标注的方法
    Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
    if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
        try {
            AnnotationMetadata asm =
                    this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
            Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
            if (asmMethods.size() >= beanMethods.size()) {
                Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
                // 筛选每个方法
                for (MethodMetadata asmMethod : asmMethods) {
                    for (MethodMetadata beanMethod : beanMethods) {
                        if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
                            selectedMethods.add(beanMethod);
                            break;
                        }
                    }
                }
                if (selectedMethods.size() == beanMethods.size()) {
                    beanMethods = selectedMethods;
                }
            }
        } // catch ......
    }
    return beanMethods;
}
```

一上来我们就看到 `@Bean` 注解的方法过滤了，经过这个过滤之后，就只会得到配置类中被 `@Bean` 注解标注的方法。

Debug 到这里，可以发现此处确实把配置类中的 `person()` 方法取了出来：

![img](https://image.ldbmcs.com/nsNh4U.jpg)

不过这里可能会有小伙伴产生疑惑：明明反射就可以拿到这些被 `@Bean` 注解标注的方法，但是中间的读取部分都把 ASM （读取字节码的技术）搬出来了，为什么要如此的大动干戈呢？哎，这里我们要多解释一个点了：\*\*使用 JVM 的标准反射，在不同的 JVM 、或者同一个 JVM 上的不同应用中，返回的方法列表顺序可能是不同的。\*\*简言之，**JVM 的标准反射不保证方法列表返回的顺序一致**。所以，想要保证程序在任何 JVM 上、任何应用中，加载同一个 .class 文件的方法列表都返回相同的顺序，那就只能读取字节码了，而读取字节码的技术，Spring 选择了 ASM 。

所以，这里 **Spring 使用 ASM 读取字节码的目的，是为了保证加载配置类中** `**@Bean**` **方法的从上到下的顺序与源文件 .java 中一致**。

#### 2.6.7 处理父接口

```java
// ............
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// ............
```

又是一个一句话方法，咱点进去看一看：

```java
private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
    for (SourceClass ifc : sourceClass.getInterfaces()) {
        // 寻找接口中标注了@Bean的方法
        Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
        for (MethodMetadata methodMetadata : beanMethods) {
            if (!methodMetadata.isAbstract()) {
                configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
            }
        }
        processInterfaces(configClass, ifc);
    }
}
```

注意看这段逻辑，它会把配置类实现的所有接口都拿出来，并且遍历所有标注了 `@Bean` 的方法，并添加到 bean 的注册信息中。跟上面一样，它只是存起来，并没有封装为 `BeanDefinition` ，所以这里只是解析动作而已。

#### 2.6.8 父类的返回

```java
    // ............
    // Process superclass, if any
    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (superclass != null && !superclass.startsWith("java") &&
                !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            return sourceClass.getSuperClass();
        }
    }
    return null;
}
```

终于看到最后一部分了，因为上面的解析处理逻辑都看完了，还剩下最后一个部分。如果配置类存在父类的话，父类也应该一起加载，所以这里会取到配置类的父类，并继续递归处理。逻辑相对的不算复杂，咱就不深入研究了。

### 2.7 loadBeanDefinitions - 加载BeanDefinition

到这里，配置类的解析就完成了，回到 `ConfigurationClassPostProcessor` 中，解析完那些 `@Bean` 后还要注册为 `BeanDefinition` 呢，而这个方法的核心在 `loadBeanDefinitions` 中，我们也来研究。

`this.reader.loadBeanDefinitions(configClasses);` 的执行会来到 `ConfigurationClassBeanDefinitionReader` 中：

```java
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}
```

又是循环，来吧，直接进到 `loadBeanDefinitionsForConfigurationClass` 中：

```java
private void loadBeanDefinitionsForConfigurationClass(
        ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
    // 与条件装配有关
    if (trackedConditionEvaluator.shouldSkip(configClass)) {
        String beanName = configClass.getBeanName();
        if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
            this.registry.removeBeanDefinition(beanName);
        }
        this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
        return;
    }

    // 如果当前配置类是被@Import的，要把自己注册进BeanFactory
    if (configClass.isImported()) {
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    // 注册@Bean注解方法
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }

    // 注册来自xml配置文件的bean
    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    // 注册来自ImportBeanDefinitionRegistrar的bean
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
```

这里面的逻辑分为 4 个部分，咱还是分别来看。

#### 2.7.1 registerBeanDefinitionForImportedConfigurationClass

第一个步骤是将配置类自身注册进 `BeanFactory` 。按照包扫描的原则来看，只要是 `@Configuration` ，那就必然也是 `@Component` ，就应该一起注册到 `BeanFactory` 中。但如果配置类是通过 `@Import` 的方式导入的，那就不会主动将自己注册进 `BeanFactory` ，所以在这里，它需要将那些被 `@Import` 进去的配置类，也全部注册到 `BeanFactory` 中。

从源码实现上看，这就是一个最最普通的 `BeanDefinition` 的注册，没有任何额外的花里胡哨的操作，所以这个我们也没必要深查了，看一下就好咯。

```java
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
    AnnotationMetadata metadata = configClass.getMetadata();
    // 构造BeanDefinition
    AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);

    ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
    configBeanDef.setScope(scopeMetadata.getScopeName());
    String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
    AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);

    // 包装BeanDefinitionHolder
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    // 注册进BeanDefinitionRegistry
    this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
    configClass.setBeanName(configBeanName);
    // logger ......
}
```

#### 2.7.2 loadBeanDefinitionsForBeanMethod

这个步骤就是加载刚才处理过的那些 `@Bean` 方法了，咱来看看它怎么处理。

```java
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
    ConfigurationClass configClass = beanMethod.getConfigurationClass();
    MethodMetadata metadata = beanMethod.getMetadata();
    String methodName = metadata.getMethodName();

    // 如果条件装配将其跳过，则该@Bean标注的方法，对应的BeanDefinition不会注册进BeanDefinitionRegistry
    if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
        configClass.skippedBeanMethods.add(methodName);
        return;
    }
    if (configClass.skippedBeanMethods.contains(methodName)) {
        return;
    }

    // 检查方法上真的有@Bean注解吗
    AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
    // assert ......

    // 如果bean指定了多个name，则第1个为唯一标识，其余的都是alias别名
    List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
    String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
    // Register aliases even when overridden
    for (String alias : names) {
        this.registry.registerAlias(beanName, alias);
    }

    // 注解中配置了@Bean，与xml中的bean撞车了，会抛出异常
    if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
        if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
            // throw ex ......
        }
        return;
    }

    // 构造BeanDefinition
    ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
    beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));

    // 【复杂】解析@Bean所在方法的修饰符
    if (metadata.isStatic()) {
        // static @Bean method
        if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
            beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
        } else {
            beanDef.setBeanClassName(configClass.getMetadata().getClassName());
        }
        beanDef.setUniqueFactoryMethodName(methodName);
    }
    else {
        // instance @Bean method
        beanDef.setFactoryBeanName(configClass.getBeanName());
        beanDef.setUniqueFactoryMethodName(methodName);
    }

    // 处理@Bean的属性(name、initMethod等)、额外的注解(@Lazy、@DependsOn等) ......

    // 注册进BeanDefinitionRegistry
    this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}
```

一样标准的套路：检查 → 构造 `BeanDefinition` → 封装信息 → 注册进 `BeanDefinitionRegistry` 。最下面的部分，那些处理 `@Bean` 的属性、额外的注解解析都比较简单了，小伙伴们可以自行借助 IDE 去看看，小册就不展开解释了。

不过这里面有一个还蛮关键的部分，要给小伙伴们解释一下，就是上面的这个 `metadata.isStatic()` ，它的判断逻辑下面，会给 `BeanDefinition` 封装两个属性：`setBeanClassName` / `setFactoryBeanName` 、`setUniqueFactoryMethodName` ，它们俩分别指定了当前 `@Bean` 方法所在的配置类，以及方法名。小伙伴们可以先猜想一下，封装它的作用是什么呢？

回想一下前面 `@Component` 注解标注的类形成的 `BeanDefinition` ，以及 xml 配置文件转换出来的 `BeanDefinition` 有个什么特点？它们**都指定了 bean 的全限定名、属性注入等**，而且最终创建的对象一定是通过**反射**创建。而在注解配置类中的 `@Bean` 方法是**有实际的代码执行**，属于**编程式创建**，无法使用（也不适合用）反射创建 bean 对象，所以**为了在后面能正常创建出 bean 对象，此处就需要记录该 bean 的定义源（包含注解配置类和方法名），以保证在创建 bean 对象时，能够使用反射调用该注解配置类的方法，生成 bean 对象并返回**。

#### 2.7.3 loadBeanDefinitionsFromImportedResources

这部分是解析从注解配置类上取到的 xml 配置文件的路径，有了前面的分析，我们马上就能猜到，它又要用 XmlBeanDefinitionReader 那一套来搞了，点开源码，发现果然如此：

```java
private void loadBeanDefinitionsFromImportedResources(
        Map<String, Class<? extends BeanDefinitionReader>> importedResources) {

    Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();

    importedResources.forEach((resource, readerClass) -> {
        if (BeanDefinitionReader.class == readerClass) {
            if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
                readerClass = GroovyBeanDefinitionReader.class;
            }
            else {
                // 创建XmlBeanDefinitionReader，以备下面的解析
                readerClass = XmlBeanDefinitionReader.class;
            }
        }

        BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
        // reader的缓存等等

        // 调用XmlBeanDefinitionReader解析资源文件
        reader.loadBeanDefinitions(resource);
    });
}
```

很容易理解了吧，里面的套路想必不用小册多解释，小伙伴们自己就已经有很清楚的认识了吧。

#### 2.7.4 loadBeanDefinitionsFromRegistrars

最后一部分是执行 `ImportBeanDefinitionRegistrar` ，这个就更简单了，既然是接口，那执行它们的话，只需要调用 `registerBeanDefinitions` 方法就可以吧，进到方法内部，发现真就是这么简单：

```java
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
    registrars.forEach((registrar, metadata) ->
            registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}
```

OK ，那到此为止，注解配置类中 `BeanDefinition` 的注册也就全部理清楚了，顺便我们把整个注解配置类的解析流程和逻辑也都研究了一遍，小伙伴们要做好笔记呀。

### 2.8 小结

同样总结一下注解配置类的加载与解析过程：**注解配置类的解析发生在** `**BeanDefinitionRegistryPostProcessor**` **的执行阶段，它对应的核心后置处理器是** `**ConfigurationClassPostProcessor**` **，它主要负责两个步骤三件事情：解析配置类、注册** `**BeanDefinition**` **。三件事情包括：1) 解析** `**@ComponentScan**` **并进行包扫描，实际进行包扫描的组件是** `**ClassPathBeanDefinitionScanner**` **；2) 解析配置类中的注解（如** `**@Import**` **、**`**@ImportResource**` **、**`**@PropertySource**` **等）并处理，工作的核心组件是** `**ConfigurationClassParser**` **；3) 解析配置类中的** `**@Bean**` **并封装** `**BeanDefinition**` **，实际解析的组件是** `**ConfigurationClassBeanDefinitionReader**` 。

## 3. BeanDefinition的后置处理

执行完 `ConfigurationClassPostProcessor` 之后，在 xml 和配置类中定义的 `BeanDefinition` 就都解析和准备好了，但是还没有加载进 `BeanDefinitionRegistry` 中。下面还会有 `BeanDefinitionRegistryPostProcessor` 和 `BeanFactoryPostProcessor` 的执行，而它们的执行时机还是上面的 `PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors` 方法中。

至于这些处理的机制，咱在前面的 28 章 `BeanFactoryPostProcessor` 章节都讲解过了，如果忘记的小伙伴们记得回去复习哦。

执行完这些后置处理器之后，`BeanDefinition` 的后置处理也就算结束了。

## 4. 总结

**首先，bean 的生命周期分为** `**BeanDefinition**` **阶段和 bean 实例阶段。**

`BeanDefinition` 阶段分为加载 xml 配置文件、解析注解配置类、编程式构造 `BeanDefinition` 、`BeanDefinition` 的后置处理，一共四个部分。

1. **加载 xml 配置文件** 发生在基于 xml 配置文件的 `ApplicationContext` 中 `refresh` 方法的 `BeanFactory` 初始化阶段，此时 `BeanFactory` 刚刚构建完成，它会借助 `XmlBeanDefinitionReader` 来加载 xml 配置文件，并使用 `DefaultBeanDefinitionDocumentReader` 解析 xml 配置文件，封装声明的 `<bean>` 标签内容并转换为 `BeanDefinition` 。
2. **解析注解配置类** 发生在 `ApplicationContext` 中 `refresh` 方法的 `BeanDefinitionRegistryPostProcessor` 执行阶段，该阶段首先会执行 `ConfigurationClassPostProcessor` 的 `postProcessBeanDefinitionRegistry` 方法。`ConfigurationClassPostProcessor` 中会找出所有的配置类，排序后依次解析，并借助 `ClassPathBeanDefinitionScanner` 实现包扫描的 `BeanDefinition` 封装，借助 `ConfigurationClassBeanDefinitionReader` 实现 `@Bean` 注解方法的 `BeanDefinition` 解析和封装。
3. **编程式构造** `**BeanDefinition**` 也是发生在 `ApplicationContext` 中 `refresh` 方法的 `BeanDefinitionRegistryPostProcessor` 执行阶段，由于 `BeanDefinitionRegistryPostProcessor` 中包含 `ConfigurationClassPostProcessor` ，而 `ConfigurationClassPostProcessor` 会执行 `ImportBeanDefinitionRegistrar` 的逻辑，从而达到编程式构造 `BeanDefinition` 并注入到 `BeanDefinitionRegistry` 的目的；另外，实现了 `BeanDefinitionRegistryPostProcessor` 的类也可以编程式构造 `BeanDefinition` ，注入 `BeanDefinitionRegistry` 。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ldbmcs.gitbook.io/java/java-frameworks-70/spring/spring-ioc/spring-bean-de-sheng-ming-zhou-qi-er-beandefinition.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
