【Spring高级】Springboot自动配置类原理

目录

  • 如何引入第三方库
  • 第三方库与当前项目Bean重复
  • 自定义自动配置类

自动配置类通常位于Spring Boot的自动配置模块中,并且被标记为 @Configuration类。这些类使用 @Conditional注解来检查某些条件是否满足,如果满足,则创建和配置相关的bean。这些条件可能包括检查类路径上是否存在特定的类、检查应用程序的属性设置、检查是否存在特定的bean等。

自动配置类还可用于自动配置各种常见的Spring组件和第三方库。这些自动配置类大大简化了应用程序的配置过程,使得开发者可以更加专注于实现业务逻辑,而不是花费大量时间在繁琐的配置上。

如何引入第三方库

看如下示例:

package com.cys.spring.chapter16;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.GenericApplicationContext;public class TestAutoConfig {@SuppressWarnings("all")public static void main(String[] args) {GenericApplicationContext context = new GenericApplicationContext();context.registerBean("config", Config.class);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({AutoConfiguration1.class, AutoConfiguration2.class})static class Config {}@Configuration // 第三方的配置类static class AutoConfiguration1 {@Beanpublic Bean1 bean1() {return new Bean1("第三方");}}static class Bean1 {private String name;public Bean1() {}public Bean1(String name) {this.name = name;}@Overridepublic String toString() {return "Bean1{" +"name='" + name + '\'' +'}';}}@Configuration // 第三方的配置类static class AutoConfiguration2 {@Beanpublic Bean2 bean2() {return new Bean2();}}static class Bean2 {}
}

运行:

config
org.springframework.context.annotation.ConfigurationClassPostProcessor
com.cys.spring.chapter16.TestAutoConfig$AutoConfiguration1
bean1
com.cys.spring.chapter16.TestAutoConfig$AutoConfiguration2
bean2
>>>>>>>>>>>>>>>>>>>>>>>>>
Bean1{name='第三方'}

首先在我们当前应用的配置类Config中,使用@Import({AutoConfiguration1.class, AutoConfiguration2.class})将模拟出的第三方的配置类AutoConfiguration1AutoConfiguration2导入到了我们自己的应用程序中,接着就可以使用他们俩的配置类中创建的Bean。

接着我们优化一下,不把导入的类名写死在代码,而是配合ImportSelector,将其写在配置文件中,文件名需要是src/main/resources/META-INF/spring.factories

配置文件内容:

com.cys.spring.chapter16.TestAutoConfig$MyImportSelector=\
com.cys.spring.chapter16.TestAutoConfig.AutoConfiguration1,\
com.cys.spring.chapter16.TestAutoConfig.AutoConfiguration2

然后修改测试类如下:

@Configuration // 本项目的配置类
@Import(MyImportSelector.class)
//    @Import({AutoConfiguration1.class, AutoConfiguration2.class})
static class Config {
}/*** 创建一个ImportSelector,返回配置文件的类名*/
static class MyImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {List<String> names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);return names.toArray(new String[0]);}
}

这样也是没问题的。

第三方库与当前项目Bean重复

如果第三方库与当前项目Bean重复,默认是引入的第三方的先创建,然后自己程序的再创建,且后创建的可以覆盖前面的。

可以设置为不允许覆盖,就会有Bean重复的报错,设置方法如下:

setAllowBeanDefinitionOverriding(false);

修改后如下:

public static void main(String[] args) {GenericApplicationContext context = new GenericApplicationContext();// 如果有同名的Bean,是否允许覆盖context.getDefaultListableBeanFactory().setAllowBeanDefinitionOverriding(false);context.registerBean("config", Config.class);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));}

运行后报错:

Exception in thread "main" org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'bean1' defined in com.cys.spring.chapter16.TestAutoConfig$Config: Cannot register bean definition [Root bean: class [null];  defined in com.cys.spring.chapter16.TestAutoConfig$Config] for bean 'bean1': There is already [Root bean: class [null]; defined in class path resource [com/cys/spring/chapter16/TestAutoConfig$AutoConfiguration1.class]] bound.

意思就是无法注册TestAutoConfig C o n f i g 中的 b e a n 1 的 b e a n d e f i n i t i o n ,因为在 T e s t A u t o C o n f i g Config中的bean1的 bean definition,因为在TestAutoConfig Config中的bean1beandefinition,因为在TestAutoConfigAutoConfiguration1.class中已经存在。

实际生产中不建议这么做,建议开启覆盖。

那么如果要调整注册 bean definition的顺序呢,让自己程序的先注册,第三方的后注册呢?也可以,但实际生产也不建议这么做。

真要修改的话可以创建ImportSelector时,实现DeferredImportSelector,达到延迟注册的目的。如下

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

自定义自动配置类

在Springboot中,自动配置通常使用@EnableAutoConfiguration。源码如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.boot.autoconfigure;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
}

他也是使用了@Import注解,去找spring.fatories文件中找AutoConfigurationImportSelector的key。AutoConfigurationImportSelectory中也有个selectImports,其源码如下:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {private static final AutoConfigurationImportSelector.AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationImportSelector.AutoConfigurationEntry();private static final String[] NO_IMPORTS = new String[0];private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";private ConfigurableListableBeanFactory beanFactory;private Environment environment;private ClassLoader beanClassLoader;private ResourceLoader resourceLoader;private AutoConfigurationImportSelector.ConfigurationClassFilter configurationClassFilter;public AutoConfigurationImportSelector() {}public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}}
}

主要方法getAutoConfigurationEntry

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} else {AnnotationAttributes attributes = this.getAttributes(annotationMetadata);List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);configurations = this.removeDuplicates(configurations);Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);this.checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = this.getConfigurationClassFilter().filter(configurations);this.fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);}
}

进入方法getCandidateConfigurations

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");return configurations;
}

他也是使用方法SpringFactoriesLoader.loadFactoryNames找配置类名称,其中key是this.getSpringFactoriesLoaderFactoryClass()方法返回值,

protected Class<?> getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class;
}

进入方法看到,他最后找的类名为EnableAutoConfiguration的key。

根据这个,我们在spring.factories中添加这个key:

com.cys.spring.chapter16.TestAutoConfig$MyImportSelector=\
com.cys.spring.chapter16.TestAutoConfig.AutoConfiguration1,\
com.cys.spring.chapter16.TestAutoConfig.AutoConfiguration2org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.cys.spring.chapter16.TestAutoConfig.AutoConfiguration1,\
com.cys.spring.chapter16.TestAutoConfig.AutoConfiguration2

并在配置类上加上@EnableAutoConfiguration:

package com.cys.spring.chapter16;import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.env.SimpleCommandLinePropertySource;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;import java.io.IOException;public class TestSelfAutoConfiguration {@SuppressWarnings("all")public static void main(String[] args) throws IOException {AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext();StandardEnvironment env = new StandardEnvironment();env.getPropertySources().addLast(new SimpleCommandLinePropertySource("--spring.datasource.url=jdbc:mysql://localhost:3306/test","--spring.datasource.username=root","--spring.datasource.password=root"));context.setEnvironment(env);context.registerBean("config", Config.class);context.refresh();for (String name : context.getBeanDefinitionNames()) {String resourceDescription = context.getBeanDefinition(name).getResourceDescription();if (resourceDescription != null)System.out.println(name + " 来源:" + resourceDescription);}context.close();}@Configuration // 本项目的配置类
//    @Import(MyImportSelector.class)
//    @Import(AutoConfigurationImportSelector.class)@EnableAutoConfigurationstatic class Config {@Beanpublic TomcatServletWebServerFactory tomcatServletWebServerFactory() {return new TomcatServletWebServerFactory();}}static class MyImportSelector implements DeferredImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null).toArray(new String[0]);}}@Configuration // 第三方的配置类static class AutoConfiguration1 {@Beanpublic Bean1 bean1() {return new Bean1();}}@Configuration // 第三方的配置类static class AutoConfiguration2 {@Beanpublic Bean2 bean2() {return new Bean2();}}static class Bean1 {}static class Bean2 {}
}

运行后检查,返现bean1和bean2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/817121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【嵌入式 - 输出驱动电路Open Drain (开漏)和Push-Pull (推挽)】

定义 Open drain 和 push-pull 是两种常见的输出驱动电路。它们在数字电子电路中用于控制信号的输出。让我逐一解释它们&#xff1a; 1. Open Drain (开漏): Open drain 输出端通常连接到地 (GND) 或者一个高电阻 (pull-up) 上。当输出信号为逻辑高电平时&#xff0c;输出端…

【CSS】CSS元素的垂直居中案例

CSS元素的垂直居中案例 1. 绝对定位 缺点 必须使用定位&#xff0c;并且是脱标元素必须给元素设置高度 <div class"container"><div class"box"></div> </div>.container {position: relative;width: 300px;height: 300px;bac…

【Python】从零开始学习Python中的随机模块:实现验证码生成功能

欢迎来CILMY23的博客 本篇主题为 从零开始学习Python中的随机模块&#xff1a;实现验证码生成功能 个人主页&#xff1a;CILMY23-CSDN博客 个人专栏系列&#xff1a; Python | C语言 | 数据结构与算法 | C 感谢观看&#xff0c;支持的可以给个一键三连&#xff0c;点赞关注…

【位运算】Leetcode 丢失的数字

题目解析 268. 丢失的数字 本题的意思就是数组的长度为n&#xff0c;在[0,n]区间中寻找缺失的一个数字 算法讲解 直观思路&#xff1a;排序 Hash&#xff0c;顺序查找缺失的数字 优化&#xff1a;使用异或&#xff0c;首先将[0,n]之间所有数字异或在一起&#xff0c;然后将…

4.11 驱动开发

作业 3.1 实验现象 crw------- 1 root root 236, 0 Apr 11 15:55 /dev/myled0 crw------- 1 root root 236, 1 Apr 11 15:55 /dev/myled1 crw------- 1 root root 236, 2 Apr 11 15:55 /dev/myled2在串口工具&#xff0c;输入如下命令实现控制对应的LED灯进行工作echo 1 >…

链表创建的陷阱与细节

链表是线性表的一种&#xff0c;它在逻辑结构上是连续的&#xff0c;在物理结构上是非连续的。 也就是说链表在物理空间上是独立的&#xff0c;可能是东一块西一块的。如下顺序表和链表在内存空间上的对比&#xff1a; 而链表的每一块空间是如何产生联系实现在逻辑结构上是连续…

移动应用安全合规动态:网信办、金管局发文强调数据安全;3月个人信息违规抽查结果出炉!(第五期)

一、监管部门动向&#xff1a;国家互联网信息办公室公布《促进和规范数据跨境流动规定》; 工信部发布《关于网络安全保险典型服务方案目录的公示》 二、安全新闻&#xff1a;恶意软件警报&#xff01;黑客利用软件即服务攻击印度安卓用户&#xff1b;Cerberus银行恶意软件的虚…

第十四届蓝桥杯题解:平方差 ,更小的数,买瓜,网络稳定性(货车运输)

目录 平方差 更小的数 买瓜 网络稳定性&#xff08;货车运输&#xff09; 货车运输 平方差 这道题就是数论的题&#xff0c;不难想到一个数m可以拆成(a-b)(ab)&#xff0c;其实(a-b)和(ab)就是m的一对因子&#xff0c;不妨设为x和y。 则有&#xff1a; abx; a-by; x*ym; 联…

JVM—jps、jstat、jinfo、jmap、jstack的使用

JVM—jps、jstat、jinfo、jmap、jstack的使用 jps jps全称&#xff1a;Java Virtual Machine Process Status Tool 可以查看Java进程&#xff0c;相当于Linux下的ps命令&#xff0c;只不过它只列出Java进程。 jps:列出Jav程序ID和Main函数名称 jps -q:只输出进程ID jps -m …

MATLAB4:数值计算

文章目录 一、实验目的二、实验内容三、仿真结果四、实践中遇到的问题及解决方法 一、实验目的 1. 熟悉根据已知数据进行回归法曲线拟合。   2. 熟悉根据已知数据进行多项式曲线拟合。   3. 熟悉根据已知数据利用指定方法进行数据插值&#xff08;临近插值、线性插值、立方…

小程序视频如何下载到电脑上

小程序视频如何下载到电脑上&#xff0c;很简单 1.利用Fiddler和Charles这些专业的抓包工具 2.利用录屏 3.利用专门抓取资源的工具(集成了抓取下载&#xff0c;而且对资源下载很友好) 工具我已经打包好了 下载高手链接&#xff1a;https://pan.baidu.com/s/1qJ81sNBzzzU0w…

leetcode2924--找到冠军II

1. 题意 给定一个有向无环图&#xff0c;方向表示胜负关系&#xff1b;求最后胜出的人。 2. 题解 将所有人标记为胜者&#xff0c;统计出度去掉对应胜者标记&#xff1b; 最后统计胜者数目&#xff0c;是否大于1&#xff0c;若大于1&#xff0c;则没有胜者&#xff0c;否则…

千云物流 -influxdb了解和应用

简介 InfluxDB Cluster 是一个开源的 时间序列数据库,没有外部依赖。它对于记录指标、事件和执行分析很有用。 InfluxDB Cluster 启发于 InfluxDB Enterprise、InfluxDB v1.8.10 和 InfluxDB v0.11.1,旨在替代 InfluxDB Enterprise。 InfluxDB Cluster 易于维护,可以与上游…

Linux的学习之路:6、Linux编译器-gcc/g++使用

摘要 本文主要是说一些gcc的使用&#xff0c;g和gcc使用一样就没有特殊讲述。 目录 摘要 一、背景知识 二、gcc如何完成 1、预处理(进行宏替换) 2、编译&#xff08;生成汇编&#xff09; 3、汇编&#xff08;生成机器可识别代码 4、链接&#xff08;生成可执行文件或…

【我的代码生成器】生成React页面类

有了数据表的结构信息&#xff0c;就能生成React 的页面类&#xff0c;快捷方便。 生成界面如下&#xff1a; 生成的React FrmUser.js页面如下&#xff1a; 只需再写里面的操作逻辑代码。

银河麒麟之PaddleOCR模型部署

一、PaddleOCR简介 PaddleOCR是一个基于飞桨框架开发的开源OCR工具&#xff0c;提供了一系列强大的文本识别功能。PaddleOCR支持多种文本识别任务&#xff0c;包括文字检测、文字识别、文本方向检测等。它具有高效、准确的特点&#xff0c;适用于多种场景下的文本识别需求&…

Selenium - java - 屏幕截图

文档地址 Selenium 浏览器自动化项目 | Selenium 安装 <dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.19.1</version></dependency>使用 创建WebDriver实例 …

IDP之Backstage - 环境搭建

0. 目录 1. 前言2. 环境准备&#xff08;Windows10下&#xff09;2.1 安装nvm2.2 git和docker安装 3. 创建模板项目3.1 典型错误: fails on the yarn install step3.2 再次启动3.3 验证 4. 相关 1. 前言 本不想写这篇&#xff0c;因为看着官网文档写着挺简单的&#xff0c;但实…

禅道登录/创建版本/编辑版本接口 【禅道版本 20.0.beta2】

登录获取token import requestsurl = "http://192.168.1.234:6061/api.php/v1/tokens" payload = {"account": "jenkins","password": "jenkins123" } headers = {}response = requests.request

UI设计的未来发展

UI 设计的未来发展&#xff0c;实际上是互联网行业未来发展的折射。毕竟&#xff0c;UI 设计始终是互联网行业的一部分&#xff0c;因此在互联网行业未来发展的可能性来看&#xff0c;UI 设计同样会跟随着互联网的部分稳步前进。曾经&#xff0c;在最初的图形化界面出现的时候&…