1. 前言
我们经常会有根据条件来加载不同的接口。比如你查询目录下文件列表,Windows 下你会用 CMD
的 dir
命令,而 Linux 下你会使用 ls
命令。熟悉 Spring Boot 自动配置的也知道 Spring Boot 能根据不同的实际情况启用不同的配置。这就是 @Conditional
注解在发挥作用。
该注解指定了在什么条件下创建 Bean 进行配置。
2. @Conditional 注解
Spring Boot 包含多个 @Conditional
注释,可以在 @Configuration
注解的类和 @Bean
注解方法中使用。@Conditional
类型的注解,可以注解在类上,可以注解在 Bean
方法上,可以允许基于 Spring Environment 属性包含配置,可以仅允许在存在特定资源时包含配置。也可自定义,接下来我们来熟悉一下 Spring Boot 提供的一些具体的条件注解。
2.1 Class Conditions
@ConditionalOnClass
和@ConditionalOnMissingClass
两个在类上的注解:
判断指定的类是否存在来构建自动配置,也可使用 name
属性名来指定类名。
2.2 Bean Conditions
@ConditionalOnBean
和@ConditionalOnMissingBean
两个在 Bean 方法上的注解:
判断指定的 Bean 是否存在来构建自动配置,可以使用 value
属性来按类型或名称 (id) 指定 Bean, 可以使用 search
属性指定 ApplicationContext 层次结构来搜索Bean
。
@Configuration
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService() { ...}
}
要添加注意添加 Bean 时的顺序,官方建议在自动配置类上仅使用 @ConditionalOnBean
和 @ConditionalOnMissingBean
注释,因为可以保证这些注释在添加用户定义的 Bean 后执行。
@ConditionalOnBean
和 @ConditionalOnMissingBean
注解作用在 @Configuration
注释的类上,等同于在作用在每个包含 @Bean
的方法上。
2.3 Property Conditions
@ConditionalOnProperty
注解可以基于 Spring Environment 属性包含的配置进判断,再决定自动配置的执行,使用 prefix
和 name
属性指定应检查的属性。默认情况下,匹配 存在 且不等于 false 的任何属性。您还可以使用 havingValue
和matchIfMissing
属性创建更高级的检查。
2.4 Resource Conditions
@ConditionalOnResource
注释仅允许在存在特定资源时执行自动配置。可以使用常用的 Spring 约定来指定资源,如以下示例所示:file:/home/user/test.dat。
2.5 Web Application Conditions
@ConditionalOnWebApplication
和 @ConditionalOnNotWebApplication
注解用于判断应用程序是否为 Web 应用程序
,Web 应用程序是使用Spring WebApplicationContext,定义会话范围或具有StandardServletEnvironment 的任何应用程序。
2.6 SpEL Expression Conditions
@ConditionalOnExpression
注解允许根据 SpEL 表达式的结果来执行配置。
3. 自定义 Condition
如果上面几种都不能满足你的需要。那么我们可以通过实现 Condition
接口,并重写其 matches
方法来构造判断条件。
- 1. 实现 Condition 接口来定义判断条件
//Windows 系统的判断条件
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return context.getEnvironment().getProperty("os.name").contains("Windows");
}
}
//Linux 系统的判断条件
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {return context.getEnvironment().getProperty("os.name").contains("Linux");
}
}
- 2. 实现不同系统下的 Bean 类
// 接口
public interface ListService {public String showListCmd();
}
//Windows 下的实现类
public class WindowsListServiceImpl implements ListService {
@Override
public String showListCmd() {return "dir";}
}
//Linux 下的实现类
public class LinuxListServiceImpl implements ListService {
@Override
public String showListCmd() {return "ls";}
}
- 3. 配置类
@Conditional注解调用条件判断的类并根据返回的结果来创建Bean
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ConditionConfig {
@Bean
@Conditional(WindowsCondition.class)
public ListService windowsListService() {return new WindowsListServiceImpl();
}
@Bean
@Conditional(LinuxCondition.class)
public ListService linuxListService() {return new LinuxListServiceImpl();
}
}
- 4. 运行类
@RunWith(SpringRunner.class)
@SpringBootTest
public class SecurityLearningApplicationTests {
@Resource
private ListService listService ;
@Resource
private ApplicationContext context;
public void testCondition(){System.out.println(context.getEnvironment().getProperty("os.name")
+ "系统下的列表命令:"
+ listService.showListCmd());
}
}
4. 总结
今天我们对 Spring Boot 中的 Condition 条件判断注入进行了系统性的了解。不仅对 Spring Boot 提供的一些开箱即用的 Condition 进行了学习,还实现了自定义的 Condition。如果你要对 Spring Boot 的自动配置深入学习或者根据业务来灵活定制,就必须对 Condition 进行系统性的学习。
关注公众号:Felordcn 获取更多资讯
个人博客:https://felord.cn