欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > SpringBoot自动配置

SpringBoot自动配置

2025/5/15 17:46:39 来源:https://blog.csdn.net/m0_74249133/article/details/141287730  浏览:    关键词: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}}}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词