SpringBoot 自动配置原理详解

2023-10-28

自动配置类原理

一些公用或通用性的类或第三方的配置类,不需要每个项目都重复的编写,将他们抽取成自动配置类,使用的时候只需要引入即可;

代码实现:

public class A14 {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config",Config.class);
        //添加BeanFactory后处理器,解析注解
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.refresh();
        for (String definitionName : context.getBeanDefinitionNames()) {
            System.out.println(definitionName);
        }
    }

    @Configuration //项目的配置,这里将第三方的配置类,引入到本项目中
    @Import({AutoConfiguration1.class, AutoConfiguration2.class})
    static class Config {

    }

    @Configuration //第三方的配置类
    static class AutoConfiguration1 {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
    }

    @Configuration //第三方的配置类
    static class AutoConfiguration2 {
        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean1 {

    }

    static class Bean2 {

    }
}

优化:

我们在引入配置类的时候可以只引入一个类,该类帮我们去引入第三方配置类,代码如下:

@Configuration //项目的配置,这里将第三方的配置类,引入到本项目中
@Import({MyImportSelector.class})
static class Config {

}

static class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{AutoConfiguration1.class.getName(), AutoConfiguration2.class.getName()};
    }
}

再次优化:

我们引入的第三方配置类名最好写在配置文件中,然后通过读取配置文件,引入第三方配置类,代码如下:

先在 resource 目录下新建一个 META-INF 目录,在该目录下新建一个 spring.factories 文件,在该文件中写入要引入的配置类全类名,

需要注意的是,如果配置类是内部类,需要用 $ 符。

com.wang.work.A14$MyImportSelector=\
com.wang.work.A14.AutoConfiguration1,\
com.wang.work.A14.AutoConfiguration2
@Configuration //项目的配置,这里将第三方的配置类,引入到本项目中
@Import({MyImportSelector.class})
static class Config {

}    

static class MyImportSelector implements ImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        List<String> names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
        return names.toArray(new String[0]);
    }
}

注意:

  • 上述实现的 ImportSelector 接口,如果第三方的配置类和本项目的配置类重名,配置类会被覆盖,SpringBoot启动会先加载第三方的配置类,然后加载本项目的配置类,所以最后加载的是本项目的配置类;也可以关闭自动覆盖(SpringBoot 中默认是关闭的),不过有相同的配置类时会抛出异常。

  • SpringBoot启动会先加载第三方的配置类,然后加载本项目的配置类这样并不合理,加载顺序应该先加载本项目的,然后再加载第三方的才合理,这时,只需要将 ImportSelector 接口改为 DeferredImportSelector 接口即可;为了防止配置类冲突,在第三方配置类上加上 @ConditionalOnMissingBean 注解即可(SpringBoot 中就是这样做的),冲突之后就不会加载第三方的配置类。

        static class MyImportSelector implements DeferredImportSelector {
    
            @Override
            public String[] selectImports(AnnotationMetadata importingClassMetadata) {
                List<String> names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
                return names.toArray(new String[0]);
            }
        }
    
        @Configuration //第三方的配置类
        static class AutoConfiguration1 {
            @Bean
            @ConditionalOnMissingBean
            public Bean1 bean1() {
                return new Bean1();
            }
        }
    

SpringBoot 自动配置源码分析:

  1. @SpringBootApplication 注解是一个复合注解,其内部 @EnableAutoConfiguration 注解就是开启自动配置
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
  1. @EnableAutoConfiguration 注解内部 @Import(AutoConfigurationImportSelector.class) 会通过加载AutoConfigurationImportSelector 来读取 spring.factories 配置文件里的配置类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
  1. AutoConfigurationImportSelector 实现了 DeferredImportSelector,重写了 selectImports() 方法,getAutoConfigurationEntry() 方法内部会调用 getCandidateConfigurations() 方法,使用 spring.factories 文件加载器去加载 以AutoConfiguration 为键的所有配置类
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
            
    @Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
       
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}
            
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, 
                                                      AnnotationAttributes attributes) {
		List<String> configurations = new ArrayList<>(
				SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), 
                                                       getBeanClassLoader()));
		ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
		Assert.notEmpty(configurations,
				"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
						+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringBoot 自动配置原理详解 的相关文章

随机推荐

  • android的模拟器闪动处理

    为什么80 的码农都做不了架构师 gt gt gt 今天 建立了一个示例android项目 运行的时候 模拟器每隔几秒便闪一下 开始以为是项目的问题 或者版本 v16 的问题 换了其他 还是闪动 网上查了下 只有一人遇到 http zhid
  • 一些在线图床工具

    以下排列按个人喜爱程度 SM MS https sm ms 和picgo搭配 非常哇塞 普通用户只有5G 不算很大 对我来说够用了 举例 https sm ms image fiNMYZ5pxycUmjX 路过图床 https imgchr
  • C#中字符串判断EndsWith和Contains的效率比较

    关于字符串的判断 EndsWith和Contains下面做了两个简单的例子 运行后测试了一下所耗时间 public void TestContains DateTime starTime DateTime Now string str 20
  • Pip install 和Conda install 的区别和使用场景

    文章目录 一 Conda 和Pip的区别 总结 二 Pip install和Conda install的区别 1 默认安装路径不同 1 python包 2 非python包 相关问题 解决 2 conda list列表数量 gt pip l
  • mysql中替换字段的部分内容

    如果想替换表中所有记录的某一个字段的指定字符串内容 可以使用mysql提供的replace 函数来实现 表记录如下 我想将address字段的湖北这部分内容替换成拼音hubei 那么sql语句如下 update user set addre
  • ConvTranspose2d(反卷积操作)

    nn ConvTranspose2d 反卷积操作 1 公式 class torch nn ConvTranspose2d in channels out channels kernel size stride 1 padding 0 out
  • Python 教程

    一 Python环境下载 百度网盘链接 https pan baidu com s 12MnzyIZZuKBiveebPdtJ3w 提取码 0st4 二 Python安装步骤 1 点击python 3 8 1 amd64 exe 2 选择安
  • Python中模块、包、库、框架的理解

    一 模块 module 以 py 文件开头的都叫做模块 模块中有定义的变量 函数 类 模块的名称为 py文件的名称 作为全局变量 name 的值 如果是模块A自己py A py则 name main 如果是被其他模块import之后使用的话
  • 机器学习实战之决策树最有特征的选取

    在学习了jack cui机器学习博客后 为了给自己留下一个理解的笔记 本人比较笨 以后方便查看 他的博客地址在下方 写得很好 点击打开链接 决策树机器学习的一种分类方法 拿相亲来说 决策树模型就是上面这一个 长方形为这个人的某个特征 决策树
  • sql把逗号分割的字符串转换为可放入in的条件语句的字符数列

    mysql 不能直接使用in子句 会当做一个字符来处理 使用FIND IN SET 字段 变量 orcal在in子句中加入 SELECT REGEXP SUBSTR 变量 1 LEVEL FROM DUAL CONNECT BY REGEX
  • element-ui的分页如何实现

    element ui的分页如何实现 表格需要绑定的属性
  • mysql 中enum用法

    enum最大长度65535 也就是可以存65535个预定义值 enum底层存的是十进制整数 严格按顺序1 2 3 4 5 排列 固千万不要用enum来存数字 用例 一件商品从付款到收货的流程的5个流程 未付款 已付款 已发货 已送达 已收货
  • Prometheus(三)Grafana部署及部署告警

    文章目录 一 Grafana部署及模板展示 1 Grafan部署步骤 二 打标签 1 重新打标定义 在job上定义 2 relabel config 重新打标配置 三 prometheus告警功能 1 告警功能概述 2 告警规则 3 通知告
  • 定时器使用总结

    gd32定时器使用总结 本次项目中较多模块使用了定时器 对定时器的不同使用方法进行总结补充 模块一 回充红外 通过定时器的计数器模块记录红外发射出来的脉冲宽度 void ir timer init uint16 t prescaler ui
  • 数据仓库与数据挖掘课后思考题整理

    数据仓库与数据挖掘课后思考题整理 文章目录 数据仓库与数据挖掘课后思考题整理 1 数据仓库概述 思考题 2 数据仓库及其设计 思考题 实践题 3 OLAP技术 思考题 课后书面作业 4 数据挖掘概述 思考题 5 关联分析 思考题 实践题 7
  • mysql:Error executing row event: ‘Table ‘hk_db.jf_share_task_item‘ doesn‘t exist‘

    场景 在主从同步的时候报错 这种情况 1 确实这个表不存在 2 在hk db这个库的目录下 缺失了这张表的 frm文件 或者 idb文件 cd xx hk db ll grep jf share task item rw r 1 mysql
  • YOLO(You Only Look Once)算法详解+NMS算法

    https blog csdn net u014380165 article details 72616238 NMS算法 https blog csdn net shuzfan article details 52711706
  • USB_HID协议基础

    目录 一 HID类设备相关概念 1 USB HID名词解释 2 HID类设备数据传输特性
  • 深度学习优化方法总结比较(SGD,Adagrad,Adadelta,Adam,Adamax,Nadam)

    作者丨ycszen 来源 https zhuanlan zhihu com p 22252270 编辑丨极市平台 导读 本文仅对一些常见的优化方法进行直观介绍和简单的比较 前言 本文仅对一些常见的优化方法进行直观介绍和简单的比较 各种优化方
  • SpringBoot 自动配置原理详解

    自动配置类原理 一些公用或通用性的类或第三方的配置类 不需要每个项目都重复的编写 将他们抽取成自动配置类 使用的时候只需要引入即可 代码实现 public class A14 public static void main String a