1、简介
@Conditional 是 Spring 4.0 提出的一个新的注解,当标注的对象满足所有的条件时,才能注册为 Spring 中的 bean。
@Conditional 源码中的定义如下:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
可以看到 @Conditional 可以使用在类上或者方法上。具体使用方式如下:
1、 在标注或元标注了 @Component 组件的类上进行标注;
2、作为元注解直接标注在其他注解上面;
3、在标注了 @Bean 注解的方法上标注。
@Conditional 注解有一个 value 属性,其值只能为 Condition 类型的数组,使用 @Conditional 时必须进行指定(多个 Condition 是与的关系,多个条件需要都满足。默认按照定义的顺序指定,可以通过 @Order 对Condition 进行排序)。
Condition 是指具体的条件,是一个接口,需要自己实现。
@FunctionalInterface
public interface Condition {
/**
* 确定条件是否匹配
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
Condition 是一个函数式接口,只有一个 matches 方法,返回 true 表示 Component 可以注册为 Spring 中的 bean。有两个参数:
1、 ConditionContext:表示条件的上下文信息;
public interface ConditionContext {
/**
* 获取持有 BeanDefinition 的注册中心,BeanDefinition 是 Spring bean 的定义,BeanDefinitionRegistry 可以用来注册或查找 BeanDefinition。
*/
BeanDefinitionRegistry getRegistry();
/**
* 获取 BeanFactory,BeanFactory 是 spring 最基础的容器,保存有 spring 的 bean 实例
*/
@Nullable
ConfigurableListableBeanFactory getBeanFactory();
/**
* 获取当前应用运行的环境信息
*/
Environment getEnvironment();
/**
* 获取当前使用的 ResourceLoader,其可以用来加载各种各样的资源
*/
ResourceLoader getResourceLoader();
/**
* 获取加载其他类的 ClassLoader
*/
@Nullable
ClassLoader getClassLoader();
}
2、 AnnotatedTypeMetadata:表示类或方法上的注解元信息。
public interface AnnotatedTypeMetadata {
/**
* 返回直接标注的注解详情
* @since 5.2
*/
MergedAnnotations getAnnotations();
/**
* 确定元素是否具有给定类型定义的注解或元注解
*/
default boolean isAnnotated(String annotationName) {
return getAnnotations().isPresent(annotationName);
}
/**
* 获取给定类型的注解属性,考虑属性重写
*/
@Nullable
default Map<String, Object> getAnnotationAttributes(String annotationName) {
return getAnnotationAttributes(annotationName, false);
}
/**
* 获取给定类型的注解属性,考虑属性重写
*/
@Nullable
default Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
MergedAnnotation<Annotation> annotation = getAnnotations().get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared());
if (!annotation.isPresent()) {
return null;
}
return annotation.asAnnotationAttributes(Adapt.values(classValuesAsString, true));
}
/**
* 获取给定类型的所有注解的所有属性,不考虑属性重写
*/
@Nullable
default MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName) {
return getAllAnnotationAttributes(annotationName, false);
}
/**
* 获取给定类型的所有注解的所有属性,不考虑属性重写
*/
@Nullable
default MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
Adapt[] adaptations = Adapt.values(classValuesAsString, true);
return getAnnotations().stream(annotationName)
.filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes))
.map(MergedAnnotation::withNonMergedAttributes)
.collect(MergedAnnotationCollectors.toMultiValueMap(map -> map.isEmpty() ? null : map, adaptations));
}
}
2、实战
要实现的目标:根据不同的环境,配置不同的线程池。步骤如下:
1、自定义 Condition,匹配条件:
/**
* @Des 匹配本地开发环境
* @Author jidi
* @Email jidi_jidi@163.com
* @Date 2022/7/23
*/
public class ProfilesActiveOfDevCondition implements Condition {
private static final String DEV = "dev";
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 获取配置属性 spring.profiles.active
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("spring.profiles.active");
return Objects.equals(DEV, property);
}
}
/**
* @Des 匹配生产环境
* @Author jidi
* @Email jidi_jidi@163.com
* @Date 2022/7/23
*/
public class ProfilesActiveOfProCondition implements Condition {
private static final String PRO = "pro";
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
// 获取配置属性 spring.profiles.active
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("spring.profiles.active");
return Objects.equals(PRO, property);
}
}
2、定义具体的线程池,通过 @Conditional 注解指定各自生效的环境:
/**
* @Des
* @Author jidi
* @Email jidi_jidi@163.com
* @Date 2022/7/23
*/
@Configuration
@Slf4j
public class ApplicationConfig {
/**
* 开发环境
*/
@Bean
@Conditional(value = {ProfilesActiveOfDevCondition.class})
public ThreadPoolExecutor devExecutor(){
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
5,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100),
new ThreadPoolExecutor.CallerRunsPolicy());
// 预先初始化所有核心线程
executor.prestartAllCoreThreads();
log.info("初始化开发环境线程池!");
return executor;
}
/**
* 生产环境
*/
@Bean
@Conditional(value = {ProfilesActiveOfProCondition.class})
public ThreadPoolExecutor proExecutor(){
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
20,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1024),
new ThreadPoolExecutor.CallerRunsPolicy());
// 预先初始化所有核心线程
executor.prestartAllCoreThreads();
log.info("初始化生产环境线程池!");
return executor;
}
}
3、在配置文件中配置属性 spring.profiles.active,启动工程,即可根据不同的配置加载不同的线程池。
3、其他 @Conditional 扩展注解
除了 @Conditional 注解外,Spring 还内置了很多其他的条件注解,并且已经实现了它们的匹配逻辑,可以直接使。主要有以下注解:
1、@ConditionalOnBean:当Spring容器内存在指定Bean时生效;
2、@ConditionalOnMissingBean:当Spring容器内不存在指定Bean时生效;
3、@ConditionalOnClass:当Spring容器内存在指定Class时生效;
4、@ConditionalOnMissingClass:当Spring容器内不存在指定Class时生效;
5、@ConditionalOnProperty:当指定的属性满足匹配条件时生效;
6、@ConditionalOnResource:当类路径下有指定资源时生效;
7、@ConditionalOnWebApplication:当项目为web项目时生效;
8、@ConditionalOnNotWebApplication:当项目不为web项目时生效;
9、@ConditionalOnExpression:当SpEL表达式成立时生效;
10、@ConditionalOnJava:当满足JVM版本时生效;
11、@ConditionalOnJndi:当指定JNDI存在时生效。