SpringBoot自动配置原理(二)

1.通过SpringFactoriesLoader.loadFactoryName加载 EnableAutoConfiguration.class,读取SpringBoot自动配置类。

复制代码
@Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");
            for(String name : SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,null)){
                System.out.println(name);
            }
            System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");
            //返回配置类的类名
            List<String> names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class,null);
            return names.toArray(new String[0]);
        }

读取到的是spring.factories文件下配置的类

2.遇到的问题

同名的第三方bean导入失败, 以bean1为例测试。

复制代码
package com.example.springdemo.demos.a04;

/**
 * @author zhou
 * @version 1.0
 * @description TODO
 * @date 2025/8/15 21:32
 */
public class Bean1 {
    private String name;
    public Bean1(String name){
        this.name = name;
    }
    @Override
    public String toString() {
        return "bean1的name:" + name;
    }

}

package com.example.springdemo.demos.a04;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;

import java.util.List;

/**
 * @author zhou
 * @version 1.0
 * @description TODO
 * @date 2025/8/15 21:30
 */
public class TestAutoConfiguration {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        //springBoot默认设置为false,同名的bean不允许第三方覆盖
        context.getDefaultListableBeanFactory().setAllowBeanDefinitionOverriding(false);
        context.registerBean("config",Config.class);
        //添加bean工厂后处理器
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();
        for (String name:context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println(">>>>>>>>>>>>>>>>>>>");
        System.out.println(context.getBean(Bean1.class));
    }
    @Configuration //本项目配置类
    @Import({MyImportSelector.class})
    static class Config{
        @Bean
        public Bean1 bean1(){
            return new Bean1("本项目");
        }
    }
    static class MyImportSelector implements ImportSelector{
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            /*System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");
            for(String name : SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,null)){
                System.out.println(name);
            }
            System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");*/
            //返回配置类的类名
            List<String> names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class,null);
            return names.toArray(new String[0]);
        }
    }
    @Configuration
    static class AutoConfiguration1{
        @Bean
        public Bean1 bean1(){return new Bean1("第三方");}
    }
    @Configuration
    static class AutoConfiguration2{
        @Bean
        public Bean2 bean2(){return new Bean2();}
    }
}

如果本类的bean和自动配置类的bean是同名的话,SpringBoot会做以下设置,不允许第三方覆盖

复制代码
context.getDefaultListableBeanFactory().setAllowBeanDefinitionOverriding(false);

它先是使用了自动配置类里面的bean1,后面报错不能注册本地配置的bean1。如何解决这个问题?(配置类的bean1先使用,再是找本地类,这样是否合理)

3.解决办法

导入类变为延迟加载的类 DeferredImportSelector(调整顺序,先找本地配置的bean,后找自动配置的bean,这样比较合理),延迟加载第三方配置,先让本类生效。

复制代码
在第三方的bean上加上@ConditionalOnMissingBean注解
复制代码
@Configuration
    static class AutoConfiguration1{
        @ConditionalOnMissingBean
        @Bean
        public Bean1 bean1(){return new Bean1("第三方");}
    }

结果如下:

本项目有的bean优选使用本项目配置的,如果缺失了再使用第三方的bean。