第四十二讲-条件装配底层
第四十二讲-条件装配底层
我们在看Spring Boot底层自动配置类的时候发现,Spring Boot底层的自动配置类用到了注解作为条件,条件成立了才会走相应的配置类,那我们也来模仿一下:
有这样一个需求,如何当前项目中引入了Druid数据库连接池,那么我们指定Condition1条件成立,并使得AutoConfiguration1
生效;如果项目中找不带Druid数据库连接池,那么我们指定Condition2条件成立,并使得AutoConfiguration2
生效。关于AutoConfiguration1和AutoConfiguration2的生效时互斥的。
首先在maven中引入Druid连接池相关依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
编写逻辑代码:
public class A42_1 {
@SuppressWarnings("all")
public static void main(String[] args) throws IOException {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
@Configuration // 本项目的配置类
@Import(MyImportSelector.class)
static class Config {
}
static class MyImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{AutoConfiguration1.class.getName(), AutoConfiguration2.class.getName()};
}
}
// 自定义成立条件1
static class MyCondition1 implements Condition { // 存在 Druid 依赖
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 判断类路径下有没有Druid这个class,来决定这个条件是否成立,存在返回true,反之返回false
return ClassUtils.isPresent("com.alibaba.druid.pool.DruidDataSource", null);
}
}
// 自定义成立条件2
static class MyCondition2 implements Condition { // 不存在 Druid 依赖
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 判断类路径下有没有Druid这个class,来决定这个条件是否成立,不存在返回true,反之返回false
return !ClassUtils.isPresent("com.alibaba.druid.pool.DruidDataSource", null);
}
}
@Configuration // 第三方的配置类
@Conditional(MyCondition1.class) // 类路径下存在Druid类则该注解生效
static class AutoConfiguration1 {
@Bean
public Bean1 bean1() {
return new Bean1();
}
}
@Configuration // 第三方的配置类
@Conditional(MyCondition2.class) // 类路径下不存在Druid类则该注解生效
static class AutoConfiguration2 {
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean1 {}
static class Bean2 {}
}
测试结果如下:
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
com.cherry.a42.A42_1$AutoConfiguration1
bean1
我们发现 AutoConfiguration1 自动配置生效了,而AutoConfiguration2并没有生效。
现在我们去除Druid的maven依赖,重新测试一下:
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
com.cherry.a42.A42_1$AutoConfiguration2
bean2
此时 AutoConfiguration2 自动配置生效了,而AutoConfiguration1并没有生效。
接下来我们看看我们的代码有没有可以改进的地方?其实是有的。
-
第一点就是我们的条件类里把要判断某个是否存在的类写死在条件代码中的,将来我们要判断另外一个类是否存在,我们的代码岂不是要一直改?所以这一点是不太好的。最好是把这个类名抽成一个变量,扩展其灵活性。
-
第二点就是刚才的两个条件判断是为互斥的条件,一个是判断存在,另一个是判断不存在,既然这两个条件非常类似,我们也可以做将这两个条件合二为一。我们也可以通过一个布尔变量来选择,布尔变量为真,就按照存在的逻辑去找;布尔变量为假,就按照不存在的逻辑去找。
-
第三点就是Spring Boot里用的是自定义注解作为条件。我们也自定义一个注解来组合刚才的
@Conditional
注解来完成条件类的功能。
接下来我们改动之前的代码:
public class A42_2 {
@SuppressWarnings("all")
public static void main(String[] args) throws IOException {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
@Configuration // 本项目的配置类
@Import(MyImportSelector.class)
static class Config {
}
static class MyImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{AutoConfiguration1.class.getName(), AutoConfiguration2.class.getName()};
}
}
static class MyCondition implements Condition { // 存在 Druid 依赖
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取@ConditionalOnClass注解中的属性
Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());
String className = attributes.get("className").toString(); // 获取className属性值
boolean exists = (boolean) attributes.get("exists"); // 获取exists属性值
boolean present = ClassUtils.isPresent(className, null);
return exists ? present : !present;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Conditional(MyCondition.class)
@interface ConditionalOnClass {
boolean exists(); // true 判断存在 false 判断不存在
String className(); // 要判断的类名
}
@Configuration // 第三方的配置类
@ConditionalOnClass(className = "com.alibaba.druid.pool.DruidDataSource", exists = false)
static class AutoConfiguration1 {
@Bean
public Bean1 bean1() {
return new Bean1();
}
}
@Configuration // 第三方的配置类
@ConditionalOnClass(className = "com.alibaba.druid.pool.DruidDataSource", exists = true)
static class AutoConfiguration2 {
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean1 {}
static class Bean2 {}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构