SpringBoot自动配置

一、Condition条件判断功能

Condition 是在Spring 4.0 增加的条件判断功能,其主要作用是判断条件是否满足,从而决定是否初始化并向容器注入Bean对象。通过@Conditional注解及其一系列的其他相关注解实现。

在Spring Boot中,条件匹配(Condition Evaluation)是一个非常重要的特性,它允许开发者根据特定的条件来决定是否创建和配置Bean。这一机制在Spring框架中是通过@Conditional注解及其一系列的变体来实现的。

以下是一些关于Spring Boot中条件匹配的基础知识:

1.@Conditional 注解

@Conditional 注解可以放在类或者方法上。当你放置在配置类上时,只有当所有指定的条件都满足时,配置类中的Bean才会被创建。放置在Bean声明的方法上时,只有当条件满足时,对应的Bean才会被注册。

@Configuration
@Conditional(YourCondition.class)
public class YourConfig {// ...
}
2. 条件类

matches 方法两个参数:

context:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等。

metadata:元数据对象,用于获取注解属性

你需要创建一个实现了Condition接口的类,来定义你的条件。

public class YourCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 你的条件逻辑return true; // 或者 false}
}
3. 内置条件注解

Spring Boot提供了一系列内置的条件注解,可直接使用:

  • @ConditionalOnBean: 当指定的Bean存在时。
  • @ConditionalOnMissingBean: 当指定的Bean不存在时。
  • @ConditionalOnClass: 当指定的字节码文件存在时。
  • @ConditionalOnMissingClass: 当指定的字节码文件不存在时。
  • @ConditionalOnProperty: 当指定的属性有指定的值时。
  • @ConditionalOnResource: 当指定的资源存在时。
  • @ConditionalOnWebApplication: 当项目是一个Web应用程序时。
  • @ConditionalOnNotWebApplication: 当项目不是一个Web应用程序时。

4. 示例1

假设我们只有在类SomeService存在时才想要创建一个Bean。

@Configuration
public class ExampleConfig {@Bean@ConditionalOnClass(SomeService.class)public SomeBean someBean() {return new SomeBean();}
}

在这个例子中,SomeBean对象只会在SomeService类存在时被创建。

5.示例2

在Spring IOC容器中有一个User的Bean对象:

1.导入Jedis坐标后,加载该User;没导入,则不加载,通过注解实现。

  • 创建一个User对象
public class User {
}
  • 在配置类UserConfig中加载User对象
@Configuration
public class UserConfig {@Bean@Conditional(value = ClassCondition.class)public User user(){return new User();}
}
  • 在ClassCondition类中判断Jedis坐标是否导入
public class ClassCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {//固定输入坐标boolean flag=true;try {Class<?> aClass = Class.forName("redis.clients.jedis.Jedis");} catch (ClassNotFoundException e) {flag=false;}return flag;}
}

2.动态指定文件,通过自定义注解实现

  • 自定义注解
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(value = ClassCondition.class)
public @interface ConditiononClass {String[] value();
}
  • 配置类UserConfig,使用自定义注解@ConditiononClass实现

动态指定坐标,将坐标保存在String类型的数组中,只有数组中所有的坐标都被导入时,才会加载对象

@ConditiononClass("redis.clients.jedis.Jedis")
public User user1(){return new User();
}
@ConditiononClass(value = {"redis.clients.jedis.Jedis","com.alibaba.fastjson.JSON"})
public User user1(){return new User();
}
  • 在ClassCondition类中判断String类型的数组中的坐标是否导入
public class ClassCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {//动态坐标或坐标数组Map<String, Object> map = metadata.getAnnotationAttributes(ConditiononClass.class.getName());System.out.println(map);String[] classname = (String[])map.get("value");boolean flag=true;try {for (String name:classname){Class<?> aClass = Class.forName(name);}} catch (ClassNotFoundException e) {flag=false;}return flag;}
}

3.启动类

@SpringBootApplication
public class SpringbootCondition01Application {public static void main(String[] args) {ConfigurableApplicationContext context = SpringApplication.run(SpringbootCondition01Application.class, args);//固定坐标在condition输入,使用原有注解实现Object user = context.getBean("user");System.out.println(user);//不存在坐标:NoSuchBeanDefinitionException: No bean named 'user' available//存在坐标:com.example.springboot_condition_01.domain.User@726a6b94//动态输入坐标,在config配置类使用自定义注解@ConditiononClass(坐标数组)输入,使用自定义注解实现Object user1 = context.getBean("user1");System.out.println(user1);Object user2 = context.getBean("user2");System.out.println(user2);}}

二、@Enable注解

SpringBoot中提供了很多Enable开头的注解,都用于启动某些功能。其底层原理是使用@Import注解导入一些配置类,实现Bean对象的加载。使用@Import导入的类会被Spring加载到IOC容器中。

源代码分析:

@Import提供4中用法:

1. 导入Bean
  • 创建enable模块

创建一个User的Bean对象

创建一个配置类,加载User对象

  • 测试模块

依赖enable的模块

<dependency><groupId>com.apesource</groupId><artifactId>enable</artifactId><version>0.0.1SNAPSHOT</version>
</dependency>

在启动类测试获取User

@SpringBootApplication
@Import(User.class)
public class SpringbootEnable01Application {public static void main(String[] args) {ConfigurableApplicationContext run = SpringApplication.run(SpringbootEnable01Application.class, args);User user = run.getBean(User.class);System.out.println(user);}}

方法二:在enable模块写一个@Enable注解

在测试模块的启动类上方通过@EnableUser注解导入Bean对象

2. 导入配置类

在启动类上方导入enable模块的config配置类

也可以在启动类上扫描enable模块的config配置类

3. 导入 ImportSelector 实现类
  • enable模块:

创建一个Student 的对象

MyImportSelector类,实现了ImportSelector接口,重写接口中的selectImports方法,往String[]中传入两个Bean对象

public class MyImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{"com.example.springboot_enable_02_other.domain.User","com.example.springboot_enable_02_other.domain.Student"};}
}
  • 测试模块
@SpringBootApplication//ImportSelector实现类
@Import(MyImportSelector.class)public class SpringbootEnable01Application {public static void main(String[] args) {ConfigurableApplicationContext run = SpringApplication.run(SpringbootEnable01Application.class, args);//ImportSelector实现类User user = run.getBean(User.class);System.out.println(user);Student student = run.getBean(Student.class);System.out.println(student);}}
4. 导入 ImportBeanDefinitionRegistrar 实现类
  • enable模块

MyImportBeanDefinitionRegister类,实现了ImportBeanDefinitionRegistrar接口,重写接口中的registerBeanDefinitions方法,往String[]中传入两个Bean对象

public class MyImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {//AnnotationMetadata注解//BeanDefinitionRegistry向spring容器中注入//1.获取user的definition对象AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition();//2.通过beanDefinition属性信息,向spring容器中注册id为user的对象registry.registerBeanDefinition("user",beanDefinition);}
}
  • 测试模块
@SpringBootApplication//ImportBeanDefinitionRegister实现类
@Import(MyImportBeanDefinitionRegister.class)
public class SpringbootEnable01Application {public static void main(String[] args) {ConfigurableApplicationContext run = SpringApplication.run(SpringbootEnable01Application.class, args);//ImportBeanDefinitionRegister实现类User user = run.getBean(User.class);System.out.println(user);}}

三、@EnableAutoConfiguration 注解

@SpringBootApplication

标明这个类是一个主启动类

@SpringBootApplication注解内部详细代码如下

@SpringBootConfiguration

作用:SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类;

@ComponentScan

作用:自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中

@EnableAutoConfiguration:开启自动配置功能

当SpringBoot扫描到@EnableAutoConfiguration注解时,会将所有包名为autoconfigure的包下的META-INF/spring.factories文件中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的value里的所有xxxConfiguration类加载到IOC容器中。

而xxxAutoConfiguration类一般都会有@ConditionalOnxxx注解,通过这些条件注解来判断是否真正的创建xxxConfiguration对象。

@AutoConfigurationPackage :自动配置包

Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器


spring boot处理@EnableAutoConfiguration源码分析

EnableAutoConfiguration进入AutoConfigurationImportSelector

AutoConfigurationImportSelector实现了DeferredImportSelector接口,而DeferredImportSelector这个接口实现了ImportSelector接口

所以在AutoConfigurationImportSelector类重写了selectImports方法

进入getAutoConfigurationEntry方法

进入getCandidateConfigurations方法

getCandidateConfigurations会到classpath下的读取META-INF/spring.factories文件的配置,并返回一个字符串数组。

当SpringBoot应用启动时,会自动加载这些配置类

在所有包名叫做autoConfiguration的包下面都有META-INF/spring.factories文件

总结原理:

  • @EnableAutoConfiguration 注解内部使用 @Import(AutoConfigurationImportSelector.class) 来加载配置类。
  • 配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot应用启动时,会自动加载这些配置类,初始化Bean。但并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean

四、自定义启动器

自定义redisstarter,要求当导入redis坐标时,SpringBoot自动创建Jedis的Bean

直接设置port、host

1.1 创建redisspringbootautoconfigure模块
  • 导入Jedis坐标
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId>
</dependency>
  • 初始化Jedis的Bean
@Configuration
public class RedisAutoconfiguration {@Beanpublic Jedis jedis(){return new Jedis("localhost",6379);}
}
  • 在resources中定义METAINF/spring.factories文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.apesource.redisspringbootautoconfigure.RedisAutoconfiguration
1.2 创建redisspringbootstarter模块

依赖redisspringbootautoconfigure的模块

<dependency><groupId>com.apesource</groupId><artifactId>redisspringbootautoconfigure</artifactId><version>0.0.1SNAPSHOT</version>
</dependency>
1.3 测试模块
  • 引入自定义的redisstarter依赖
<dependency><groupId>com.apesource</groupId><artifactId>redisspringbootstarter</artifactId><version>0.0.1SNAPSHOT</version>
</dependency>
  • 测试获取Jedis的Bean,操作redis。
@SpringBootApplication
public class SpringbootStarter01Application {public static void main(String[] args) {ConfigurableApplicationContext run = SpringApplication.run(SpringbootStarter01Application.class, args);Jedis bean = run.getBean(Jedis.class);System.out.println(bean);}}//BinaryJedis{Connection{DefaultJedisSocketFactory{localhost:6379}}}

在application.yml文件设置port、host

2.1 测试类
spring:redis:host: localhostport: 6060
2.2 redisspringbootautoconfigure模块
  • 批量注入写在yml文件中的host和port
//批量注入
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {//如果有值就存入,没有值就使用默认的private String host="localhost";private int port=6379;public String getHost() {return host;}public void setHost(String host) {this.host = host;}public int getPort() {return port;}public void setPort(int port) {this.port = port;}
}
  • 初始化Jedis的Bean时传入redisProperties对象(即:port、host),通过get方法获得对应的值
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoconfiguration {@Beanpublic Jedis jedis(RedisProperties redisProperties){return new Jedis(redisProperties.getHost(),redisProperties.getPort());}
}
2.3 在测试类中获取,方法与上面相同

得到结果:BinaryJedis{Connection{DefaultJedisSocketFactory{localhost:6060}}}

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

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

相关文章

性能优化理论篇 | swap area是个什么东西

我们知道每台计算机的内存&#xff08;RAM&#xff09;都是有限的&#xff0c;而我们的应用程序需要加载到内存才能被运行&#xff0c;如果一台机器运行多个应用程序时&#xff0c;内存可能会耗尽。Linux 系统中的“交换空间&#xff08;也称为交换分区&#xff09;”可以帮助缓…

使用AWS Lambda轻松开启Amazon Rekognition之旅

这是本系列文章的第一篇&#xff0c;旨在通过动手实践&#xff0c;帮助大家学习亚马逊云科技的生成式AI相关技能。通过这些文章&#xff0c;大家将掌握如何利用亚马逊云科技的各类服务来应用AI技术。 那么让我们开始今天的内容吧&#xff01; 介绍 什么是Amazon Rekognition&…

微软的免费“后悔药“-Windows File Recovery (WinFR)

微软的免费"后悔药"-Windows File Recovery (WinFR) 当你不小心误删除了文件或因各种意外情况导致数据丢失&#xff0c;可以使用 EasyRecovery、Disk Drill、DiskGenius、Recuva 等“上古”软件&#xff0c;也可以交由专业机构进行恢复。微软&#xff08;Microsoft&…

揭秘!移动安全管理系统是什么?有什么功能?(从小白到精通一文揭晓!)

在2024年&#xff0c;移动终端管控软件在企业和组织中的应用日益广泛。 移动安全管理系统不仅提高了管理效率&#xff0c;还增强了数据安全性和移动办公的便捷性。 以下是六款值得推荐的移动终端管控软件&#xff1a; 1. 安企神 特点&#xff1a;作为行业领头羊&#xff0c;…

框架漏洞大全【万字总结】

文章目录 常见语言开发框架&#xff1a;Thinkphp远程代码执行5.0.23 rce介绍影响版本复现 CNVD-2018-24942介绍影响版本复现 任意文件包含包含日志-3.2x介绍影响版本复现 包含语言&#xff08;QVD-2022-46174&#xff09;介绍影响版本复现 sql注入漏洞(5.0.x)介绍影响版本复现 …

(26)微信检查联系人和清粉(针对删除和拉黑)-微信UI自动化(.Net+C#)

整理 | 小耕家的喵大仙 出品 | CSDN&#xff08;ID&#xff1a;lichao19897314&#xff09; Q Q | 978124155 往期知识回顾 (1)开启探索微信自动化之路-微信UI自动化(.NetC#) (2)初始化微信窗体UI自动化实例-微信UI自动化(.NetC#) (3)采用热键终止微信采集任务-微信UI自动…

UI自动化测试:遍历页面元素并获取文本的实践分享!

遍历读取元素的文本 在写UI自动化过程中还会遇到需要遍历读取元素的情况。下面分享以「稿定设计」网站为例&#xff0c;想要通过UI自动化读取素材内容的操作菜单列表&#xff0c;如下图&#xff1a; 代码片段和解释 # 获取菜单列表元素的文本信息&#xff0c;例如&#xff1a…

算法基础及例题

1、双指针 维护区间信息、子序列匹配、利用序列有序性、单项链表找环双指针 - OI Wiki (oi-wiki.org) 盛最多水的容器https://leetcode.cn/problems/container-with-most-water/ public class Solution {public int maxArea(int[] height) {int l 0, r height.length - 1;int…

泡泡玛特2024半年报发布:首度划分四大品类 手办收入占比首次低于60%

8月20日&#xff0c;泡泡玛特发布2024上半年业绩报告。报告显示&#xff0c;2024年上半年泡泡玛特国际集团实现营收45.6亿元&#xff08;人民币&#xff0c;下同&#xff09;&#xff0c;同比增长62.0%&#xff0c;经调整后净利10.2亿元&#xff0c;同比增长90.1%。 上半年泡泡…

MacOS升级ruby版本

​ ​ 您好&#xff0c;我是程序员小羊&#xff01; 前言 升级Ruby版本在MacOS上相对简单&#xff0c;但需要一些基础的命令行知识。本文将详细介绍如何在MacOS上升级Ruby版本&#xff0c;包括使用常见的版本管理工具、解决可能遇到的问题、以及确保你的环境配置不会受到影响。…

第二百零九节 Java格式 - Java数字格式类

Java格式 - Java数字格式类 以下两个类可用于格式化和解析数字: java.text.NumberFormatjava.text.DecimalFormat NumberFormat 类可以格式化一个数字特定地区的预定义格式。 DecimalFormat 类可以格式化数字以特定区域设置的自定义格式。 NumberFormat类的 getXXXInstance…

宠物空气净化器怎么选?新手必看猫用除毛空气净化器热门品牌推荐

作为资深铲屎官来说&#xff0c;一到换毛季节&#xff0c;家里的猪咪经常会出现掉毛的情况&#xff0c;而且如果不勤打扫的话&#xff0c;粑粑的臭味也挺重的。如果长期不清理家里的浮毛&#xff0c;很容易就会得鼻炎。 看了身边好几个铲屎官都在用宠物空气净化器&#xff0c;…

什么牌子的开放式耳机性价比高?五款高口碑精品推荐!

由于传统入耳式耳机可能对耳道健康造成长期影响&#xff0c;许多人开始偏好选择开放式耳机的非侵入式设计。这种耳机有助于减少耳内湿润、细菌增长&#xff0c;以及耳道闷热的不适感。为了帮助大家在众多产品中挑选合适的开放式耳机&#xff0c;我将列举一些市场反馈良好的款式…

Android10.0 人脸解锁流程分析

人脸解锁概述 人脸解锁即用户通过注视设备的正面方便地解锁手机或平板。Android 10 为支持人脸解锁的设备在人脸认证期间添加了一个新的可以安全处理相机帧、保持隐私与安全的人脸认证栈的支持&#xff0c;也为安全合规地启用集成交易的应用&#xff08;网上银行或其他服务&am…

TikTok本土店海外仓发货总超时?EasyBoss ERP支持提前申请面单助力解决

近期有部分通过海外仓自发货的TikTok本土卖家表示&#xff1a;通过ERP推送订单至海外仓却无法立即出库&#xff0c;导致超出平台规定发货时间被平台处罚。 而出现这样的原因在于&#xff1a;通过ERP处理的TikTok订单&#xff0c;在使用认证的海外仓发货时&#xff0c;订单会先…

【C++ Primer Plus习题】2.2

问题: 解答: #include <iostream> using namespace std;#define LONG_TO_MA 220int main() {double distance 0;cout << "请输入距离(单位为long):";while (true){cin >> distance;if (cin.fail()){cout << "输入有误!请输入数字:&qu…

Redis 集群三主三从配置

1&#xff1a;安装 Redis安装Linux ubuntu_ubuntu离线安装redis7.2.5-CSDN博客 2&#xff1a;主从复制配置 参考 Redis主从同步配置-CSDN博客 3&#xff1a;哨兵配置 参考 Redis 哨兵模式配置-CSDN博客 4&#xff1a;集群配置 Redis 集群三主三从配置-CSDN博客 5&…

OpenCV与AI深度学习 | 使用OpenCV图像修复技术去除眩光

本文来源公众号“OpenCV与AI深度学习”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;使用OpenCV图像修复技术去除眩光 眩光是一种因过度和不受控制的亮度而引起的视觉感觉。眩光可能会使人丧失能力或只是让人感到不舒服。眩光是一…

【一起学Rust | 框架篇 | Tauri2.0框架】tauri中rust和前端的相互调用(rust调用前端)

文章目录 前言1. rust中调用前端2. 如何向前端发送事件3. 前端监听事件4. 执行js代码 前言 近期Tauri 2.0 rc版本发布&#xff0c;2.0版本迎来第一个稳定版本&#xff0c;同时官方文档也进行了更新。Tauri是一个使用Rust构建的框架&#xff0c;可以让你使用前端技术来构建桌面…

Redis7基础篇(九)

springboot集成redis 目录 springboot集成redis 总体概述 java连接redis常见问题 集成jedis 集成lettuce 集成redistemplate 连接单机 ​编辑​编辑​编辑redis集群 总体概述 java要想连接mysql的话需要jdbc java想要连接redis也需要中间件 jedis是第一代 lettuce第…