springboot系列--自动配置原理

news2024/9/20 22:49:52

一、容器功能

一、组件添加功能

一、@Configuration

@Configuration有两种模式,Full模式与Lite模式。

1、配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断

2、配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式

/**
 * 1、配置类里面使用@Bean标注在方法上给容器注册组件,默认也是单实例的
 * 2、配置类本身也是组件
 * 3、proxyBeanMethods:代理bean的方法
 *      Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
 *      Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
 *      组件依赖必须使用Full模式默认。其他默认是否Lite模式
 *
 *
 *
 */
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {

    /**
     * Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
     * @return
     */
    @Bean //给容器中添加组件。以方法名作为组件的id。返回类型就是组件类型。返回的值,就是组件在容器中的实例
    public User user01(){
        User zhangsan = new User("zhangsan", 18);
        //user组件依赖了Pet组件
        zhangsan.setPet(tomcatPet());
        return zhangsan;
    }

    @Bean("tom")
    public Pet tomcatPet(){
        return new Pet("tomcat");
    }
}

 二、@Bean、@Component、@Controller、@Service、@Repository都可以实现往容器中注入添加组件

三、@ComponentScan、@Import

 * 4、@Import({User.class, DBHelper.class})
 *      给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名
 *
 *
 *
 */

@Import({User.class, DBHelper.class})
@Configuration(proxyBeanMethods = false) //告诉SpringBoot这是一个配置类 == 配置文件
public class MyConfig {
}

四、@Conditional条件装配

 满足Conditional指定的条件,则进行组件注入

1、@ConditionalOnBean:存在指定的bean,则注入

2、@ConditionalOnMissingBean:不存在指定的bean,则注入

3、@ConditionalOnClass:存在指定类,则注入

4、@ConditionalOnMissingClass:不存在指定的类,则注入

二、原生配置文件导入

@ImportResource("classpath:beans.xml")

导入指定的bean.xml文件中的配置进入容器中,形成bean

三、配置绑定

使用Java读取到properties文件中的内容(实际项目中一般会把这里的配置放到nacos中),并且把它封装到JavaBean中,以供随时使用:

public class getProperties {
     public static void main(String[] args) throws FileNotFoundException, IOException {
         Properties pps = new Properties();
         pps.load(new FileInputStream("a.properties"));
         Enumeration enum1 = pps.propertyNames();//得到配置文件的名字
         while(enum1.hasMoreElements()) {
             String strKey = (String) enum1.nextElement();
             String strValue = pps.getProperty(strKey);
             System.out.println(strKey + "=" + strValue);
             //封装到JavaBean。
         }
     }
 }

 一、@ConfigurationProperties

这个注解只是单纯的进行配置绑定,并还没有注入容器,所以如果还想通过spring的注解从容器中获取对象,就需要使用@Component或者其他一些注入容器中的注解,把@ConfigurationProperties修饰的对象也一起注入容器中。

/**
 * 只有在容器中的组件,才会拥有SpringBoot提供的强大功能
 */
@Component
@ConfigurationProperties(prefix = "mycar")
public class Car {

    private String brand;
    private Integer price;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                '}';
    }
}

二、@EnableConfigurationProperties + @ConfigurationProperties

@EnableConfigurationProperties开启指定类的绑定功能,且自动注入到容器中去,也就是那个类使用了@ConfigurationProperties修饰,通过@EnableConfigurationProperties指定那个类,就可以在不使用@Component的情况下注入进容器

@EnableConfigurationProperties(Car.class)
//1、开启Car配置绑定功能
//2、把这个Car这个组件自动注册到容器中
public class MyConfig {
}

二、自动配置原理

一、引导加载自动配置

自动配置主要是同过@SpringBootApplication这个类注解实现的,这个注解是一个合成注解,里面由多个注解组成。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

一、 @SpringBootConfiguration

使用@Configuration修饰这个注解。代表当前这个注解是一个配置类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

二、@ComponentScan

指定扫描哪些,Spring注解;

@ComponentScan(excludeFilters = { 
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

excludeFilters :排除指定的组件
FilterType.CUSTOM:按照自定义规则排除
TypeExcludeFilter.class:自定义规则组件

 三、@EnableAutoConfiguration

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

一、@AutoConfigurationPackage

自动导包配置注解,里面是@Import(AutoConfigurationPackages.Registrar.class)

1、AutoConfigurationPackages.Registrar.class

	@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			register(registry, new PackageImport(metadata).getPackageName());
		}

metadata:注解元信息,也就是这个注解修饰的类的全路径类名
register:扫描注解所在的类的包下的所有组件,并注册进容器,也就是SpringAnnotateApplication所在的包。所以springboot,默认自动导入SpringAnnotateApplication所在的包下的所有组件。

	public static void register(BeanDefinitionRegistry registry, String... packageNames) {
        // BEAN:AutoConfigurationPackages.class.getName()
        // 判断容器中是否有自动配置类,如果该bean已经注册,则将要注册包名称添加进去
		if (registry.containsBeanDefinition(BEAN)) {
			BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
			ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
			constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
		}
		else {
        //  //如果该bean尚未注册,则注册该bean,参数中提供的包名称会被设置到bean定义中去
			GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        // 注册的是BasePackages这个类
			beanDefinition.setBeanClass(BasePackages.class);
        // 这是他的参数
			beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
			beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
			registry.registerBeanDefinition(BEAN, beanDefinition);
		}
	}

AutoConfigurationPackages.Registrar这个类就干一个事,注册一个Bean,这个Bean就是org.springframework.boot.autoconfigure.AutoConfigurationPackages.BasePackages,它有一个参数,这个参数是使用了@AutoConfigurationPackage这个注解的类所在的包路径,保存自动配置类以供之后的使用.

二、@Import({AutoConfigurationImportSelector.class}

主要是AutoConfigurationImportSelector类中getAutoConfigurationEntry方法给容器进行批量导入组件。

1、进入getAutoConfigurationEntry方法,找到List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);这行代码,这里就把指定路径下文件中的配置类加载进入容器了。

2、进入getCandidateConfigurations,只有一行List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());是主要代码,从这里进入可以看到具体加载逻辑

 

3、loadSpringFactories(@Nullable ClassLoader classLoader)方法是主要的加载逻辑

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 先从缓存中获取配置文件
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
        // 如果没有从指定classLoader,则默认加载系统下所有META-INF/spring.factories位置中的文件
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                MultiValueMap<String, String> result = new LinkedMultiValueMap();
                // 循环处理,然后放入缓存
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Map.Entry<?, ?> entry = (Map.Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryImplementationName = var9[var11];
                            result.add(factoryTypeName, factoryImplementationName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

spring-boot-autoconfigure这个包里面也有META-INF/spring.factories文件,文件里面写死了spring-boot启动就要给容器中加载的所有配置类。

 虽然springboot启动时,就默认加载META-INF/spring.factories下的所有配置文件,但是这些配置文件并不是所有都会启动,他们只会按条件装配,只有符合某些条件,才会开启。以下是几个springboot底层按条件开启配置的栗子:

1、springboot底层给容器中加入了文件上传解析器

        @Bean
        @ConditionalOnBean(MultipartResolver.class)  //容器中有这个类型组件
        @ConditionalOnMissingBean(name = DispatcherServlet.) //容器中没有这个名字 multipartResolver 的组件
        public MultipartResolver multipartResolver(MultipartResolver resolver) {
            //给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
            //SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范
            // 有些用户可能配置了MultipartResolver这个类型的组件,但是组件名字不规范
            return resolver;
        }

2、优先使用用户配置

@Bean
@ConditionalOnMissingBean // 如果容器中没有CharacterEncodingFilter 这个bean才开启,相当于优先使用用户的,用户没有会配置,将会开启这个兜底
public CharacterEncodingFilter characterEncodingFilter() {
    CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
    filter.setEncoding(this.properties.getCharset().name());
    filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST));
    filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));
    return filter;
}

三、总结 

1、SpringBoot启动时先加载META-INF/spring.factories下所有的自动配置类 xxxxxAutoConfiguration

2、每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定

3、生效的配置类就会给容器中装配很多组件

4、只要容器中有这些组件,相当于这些功能就有了

5、用户可以自己定制化配置,有两种方式:

         a、直接自己写配置类使用@Bean替换底层的组件

         b、用户去看这个组件是获取的配置文件什么值就去修改。

6、如果想要查看容器是否开启了对应组件,有两种方式

        a、在启动类中获取容器中的bean,查看容器中是否成功加载。

        b、还有一种就是在配置文件中配置debug=true开启自动配置报告。Negative(不生效)\Positive(生效),直接配置没有前缀。

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

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

相关文章

linux 操作系统下cupsdisable命令介绍和使用案例

linux 操作系统下cupsdisable命令介绍和使用案例 cupsdisable 命令是 Linux 操作系统中用于禁用 CUPS&#xff08;通用打印服务&#xff09;打印机的命令。它允许用户将指定的打印机设置为不可用状态&#xff0c;从而阻止任何新的打印作业被发送到该打印机 cupsdisable 命令概…

句子成分——每日一划(七)

目录 一、原句 二、第一部分 三、第二部分 一、原句 Such a state of affairs can only produce antagonism between the laboring class and the owning, i.e., do-nothing, class. The fight breaks out and hatred delivers its blows. 来源&#xff1a;Why I Was a Bur…

C++11第四弹:包装器

&#x1f308;个人主页&#xff1a; 南桥几晴秋 &#x1f308;C专栏&#xff1a; 南桥谈C &#x1f308;C语言专栏&#xff1a; C语言学习系列 &#x1f308;Linux学习专栏&#xff1a; 南桥谈Linux &#x1f308;数据结构学习专栏&#xff1a; 数据结构杂谈 &#x1f308;数据…

探索UWB技术的独特优势:实现高精度定位

UWB定位技术是一种利用无线信号进行精确位置定位的技术&#xff0c;它利用超宽带无线电信号通过测量信号的到达时间、相位差和信号能量等参数来确定物体的精确位置。 UWB定位技术具有多种优势&#xff0c;首先&#xff0c;它具有较高的定位精度&#xff0c;可实现毫米级的精确…

如何防止ZIP压缩文件被随意打开?

ZIP文件是常见的压缩文件格式&#xff0c;为了保护压缩包不被随意打开&#xff0c;很多人还会给ZIP压缩包设置密码&#xff0c;用于保护文件的数据安全&#xff0c;以下是一篇关于如何防止ZIP压缩文件被随意打开的详细探讨。 引言 ZIP文件因其高效的压缩率和广泛的兼容性&…

目标检测中的解耦和耦合、anchor-free和anchor-base

解耦和耦合 写在前面 在目标检测中&#xff0c;objectness&#xff08;或 objectness score&#xff09;指的是一个评分&#xff0c;用来表示某个预测框&#xff08;bounding box&#xff09;中是否包含一个目标物体。 具体来说&#xff0c;YOLO等目标检测算法需要在每个候选区…

基于OpenSSL的密码管理系统-应用密码学课程报告

第1章 概要设计 1.1 设计目的 本研究旨在设计并实现一个基于OpenSSL的密码管理系统&#xff0c;该系统具备密钥对的生成、密钥上传、密钥的核对、身份认证、文件与邮件的加密和解密、数字签名及数字证书管理等常用功能。研究的意义主要体现在以下几个方面&#xff1a; 提升网…

Ubuntu20-xrdp与Windows-mstsc远程桌面连接

ubuntu端 sudo adduser yu //输入密码和确认密码&#xff0c;后面一路回车&#xff0c;新建用户yu&#xff0c;确保用户没有被登录 sudo apt install xrdp //安装xrdp sudo systemctl status xrdp //查看xrdp服务状态 sudo adduser xrdp ssl-cert //将用户 xrdp 添加到 ss…

悟空crm客户管理系统二次开发 单独新增表格字段

1&#xff0c;仪表盘&#xff08;数据来源修改&#xff09; 注意点&#xff1a;有层级关系&#xff0c;管理员账号可以看到全部数据&#xff0c;主管只能看到下属数据。 2、在客户管理菜单里面 增加一个时间筛选、额度汇总 /*** 获取客户列表** param $type* param $content*…

在线API文档,技术文档工具源码ShowDoc

ShowDoc是一个非常适合IT团队的在线API文档、技术文档工具。通过showdoc&#xff0c;你可以方便地使用markdown语法来书写出美观的API文档、数据字典文档、技术文档、在线excel文档等等。 代码下载

单机docker-compose部署minio

单机多副本docker-compose部署minio 简单介绍 如果服务器有限可以单机挂载多硬盘实现多副本容错&#xff08;生产不推荐&#xff09; 部署好的文件状态 有两个重要文件 docker-compose.yaml和nginx.conf docker-compose.yaml是docker部署容器的配置信息包括4个minio和1个ng…

云微客全流程闭环,实现在短视频营销中快速拿结果

不知道大家有没有在抖音或者是其他短视频平台见过这样的视频&#xff0c;这一类的视频制作的非常简单&#xff0c;只有一个简单的文字搭配上背景素材&#xff0c;但是它的播放量和互动量却是惊人的好。在短视频领域的朋友想必有过这样心声&#xff0c;这么好的播放量&#xff0…

USB虚拟串口——CDC ACM 虚拟串口(不使用 IAD)

文章目录 CDC ACM 虚拟串口实现描述符结构设备描述符配置描述符集合配置描述符接口 1 的描述符接口描述符类特殊描述符输入端点描述符接口 2 的描述符接口描述符输出端点描述符输入端点描述符类特殊请求set control line statusget line codingset line codingCDC 数据交互主机…

【数据结构】之排序

&#x1f3c0;&#x1f3c0;&#x1f3c0;来都来了&#xff0c;不妨点个关注&#xff01; &#x1f3a7;&#x1f3a7;&#x1f3a7;博客主页&#xff1a;欢迎各位大佬! 文章目录 1 排序1.1 排序的概念1.2 几种常见的排序算法&#xff1a; 2 常见排序算法的实现2.1 插入排序2.…

Java项目: 基于SpringBoot+mybatis+maven美发门店管理系统(含源码+数据库+毕业论文)

一、项目简介 本项目是一套基于SpringBootmybatismaven美发门店管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简…

Java在零工市场中的应用:构建灵活高效的劳动力平台

随着数字经济的迅猛发展&#xff0c;零工经济作为一种新兴的劳动力市场模式&#xff0c;正在全球范围内迅速崛起。零工市场通过互联网平台将服务提供者与需求者进行快速匹配&#xff0c;使得个人可以临时、自由地提供服务&#xff0c;企业则能够按需雇佣劳动力&#xff0c;实现…

总算学到路由了————vue3中路由介绍

基本创建步骤 下载vue-router的依赖&#xff1a;npm install vue-router4 创建好路由组件&#xff0c;放在pages/views里面 &#xff08;views 文件夹通常包含应用的页面。这些页面通常是与路由相对应的组件&#xff0c;代表应用的不同视图&#xff0c;components 文件夹通…

基于yolov8的行人过马路危险行为检测告警系统python源码+onnx模型+精美GUI界面

【算法介绍】 基于YOLOv8的行人过马路危险行为检测告警系统是一种高效、精准的智能交通监控解决方案。该系统利用YOLOv8这一前沿的目标检测算法&#xff0c;能够快速识别图像或视频中的行人&#xff0c;并准确判断其是否存在过马路时的危险行为&#xff0c;如玩手机、打电话等…

MySQL 查询数据库的数据总量

需求&#xff1a;查看MySQL数据库的数据总量&#xff0c;以MB为单位展示数据库占用的磁盘空间 实践&#xff1a; 登录到MySQL数据库服务器。 选择你想要查看数据总量的数据库&#xff1a; USE shield;运行查询以获取数据库的总大小&#xff1a; SELECT table_schema AS Datab…

抖音视频下载

对于特别喜欢的视频有时需要珍藏&#xff0c;下文方法可能会帮到你&#xff0c;但要注意尊重版权和遵守相关声明。 Edge浏览器打开抖音短视频&#xff0c;按F12&#xff0c;选择 网络&#xff1b;筛选条件?a&#xff1b;双击搜索结果打开视频&#xff1b;选择想要的视频&…