@Conditional注解分析,SpringBoot自动化配置的关键
基于SpringBoot 2.1.5.RELEASE分析
@Conditional系列注解
@Conditional系列注解是SpringBoot自动化配置的核心要点之一,主要用于设定条件,在达到一定条件的情况下才能注册Bean。看下@Conditional注解的定义
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition Conditions} that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
看定义及注释可知,value是要传入继承于Condition的Class数组,要在满足所有匹配的条件后才会注册组件。Condition如下
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
Spring设定的@Conditional系列注解有以下几种,位于包org.springframework.boot.autoconfigure.condition下
| 注解 | 作用 |
|---|---|
| @ConditionalOnBen | 当容器中存在指定的Bean时 |
| @ConditionalOnClass | 当classpath中存在指定的class时 |
| @ConditionalOnExpression | 当指定的EL表达式成立时 |
| @ConditionalOnCloudPlatform | 当运行在指定的云平台上时时 |
| @ConditionalOnJava | 当Java版本为指定的版本时 |
| @ConditionalOnJndi | 当指定JDDI加载后 |
| @ConditionalOnMissingBean | 当容器中没有指定的Bean时 |
| @ConditionalOnMissingClass | 当classpath中没有指定的class时 |
| @ConditionalOnNotWebApplication | 当运行的项目不是web项目时 |
| @ConditionalOnProperty | 当指定的属性值符合期望时 |
| @ConditionalOnResource | 当指定的资源存在于classpath中时 |
| @ConditionalOnSingleCandidate | 当容器中存在指定的Bean且只存在一个时 |
| @ConditionalOnWebApplication | 当运行的项目是web项目时 |
这里有些我也没用过,大概知道就可以了
详细分析
那么现在拉个简单的出来分析下,就你了,@ConditionalOnJava吧
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnJavaCondition.class})
public @interface ConditionalOnJava {
ConditionalOnJava.Range range() default ConditionalOnJava.Range.EQUAL_OR_NEWER;
JavaVersion value();
public static enum Range {
EQUAL_OR_NEWER,
OLDER_THAN;
private Range() {
}
}
}
再看下OnJavaCondition
@Order(-2147483628)
class OnJavaCondition extends SpringBootCondition {
private static final JavaVersion JVM_VERSION = JavaVersion.getJavaVersion();
OnJavaCondition() {
}
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnJava.class.getName());
Range range = (Range)attributes.get("range");
JavaVersion version = (JavaVersion)attributes.get("value");
return this.getMatchOutcome(range, JVM_VERSION, version);
}
protected ConditionOutcome getMatchOutcome(Range range, JavaVersion runningVersion, JavaVersion version) {
boolean match = this.isWithin(runningVersion, range, version);
String expected = String.format(range != Range.EQUAL_OR_NEWER ? "(older than %s)" : "(%s or newer)", version);
ConditionMessage message = ConditionMessage.forCondition(ConditionalOnJava.class, new Object[]{expected}).foundExactly(runningVersion);
return new ConditionOutcome(match, message);
}
private boolean isWithin(JavaVersion runningVersion, Range range, JavaVersion version) {
if (range == Range.EQUAL_OR_NEWER) {
return runningVersion.isEqualOrNewerThan(version);
} else if (range == Range.OLDER_THAN) {
return runningVersion.isOlderThan(version);
} else {
throw new IllegalStateException("Unknown range " + range);
}
}
}
OnJavaCondition里没有Condition中定义的matches方法与SpringBootCondition有关,挑点SpringBootCondition的内容看下
public abstract class SpringBootCondition implements Condition {
//...省略部分内容
public abstract ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata);
protected final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata, Condition condition) {
return condition instanceof SpringBootCondition ? ((SpringBootCondition)condition).getMatchOutcome(context, metadata).isMatch() : condition.matches(context, metadata);
}
}
可以看到SpringBootCondition实现了Condition接口,并实现了matches方法,当condition是SpringBootCondition或子类时调用getMatchOutcome方法得到ConditionOutcome以isMatch为判断结果,getMatchOutcome方法等待继承者去实现
ConditionOutcome呢,也很简单,new的时候直接传入match和message就行了
public class ConditionOutcome {
private final boolean match;
private final ConditionMessage message;
public ConditionOutcome(boolean match, String message) {
this(match, ConditionMessage.of(message, new Object[0]));
}
//...省略部分内容
}
其它也大致如此,简单明了,那自定义一个应该也不难
自定义条件判断注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Conditional({OnTestCondition.class})
public @interface ConditionalOnTest {
String value();
}
条件判断如下,message用于日志打印
public class OnTestCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnTest.class.getName());
String value = (String) attributes.get("value");
if("123".equals(value)){
return new ConditionOutcome(true, "test yes");
}
return new ConditionOutcome(false, "test no");
}
}
找个地用上这个注解
@ConditionalOnTest("123")
@RestController
@RequestMapping("/")
public class IndexController {
@RequestMapping("hello")
public JSONObject hello() {
JSONObject result = new JSONObject();
result.put("1", "test");
return result;
}
}
写在了controller上做下测试,这样当@ConditionalOnTest中的值是123时,就能会注册IndexController,访问/hello能看到test,否则就不能。
还可以在属性配置文件application.yml中添加属性"debug: true",打开debug开关,然后在启动项目时,可以看到日志输出,这里会出现我们设置的message信息
============================
CONDITIONS EVALUATION REPORT
============================
...
IndexController matched:
- test yes (OnTestCondition)
...
补充
那么@Conditional系列注解执行的地方在哪?在@Conditional同路径的ConditionEvaluator类中shouleSkip方法,如果方法结果为true则跳过这个bean的注册
for (Condition condition : conditions) {
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
return true;
}
}
就在这condition.matches(this.context, metadata),进行了注册的条件判断
@Conditional注解分析,SpringBoot自动化配置的关键的更多相关文章
- springboot笔记-1.自动化配置的关键
最近发现看过的东西容易忘,但是写一遍之后印象倒是会深刻的多. 总所周知springboot极大的简化了java开发繁琐性,而其最大的优势应该就是自动化配置了.比如要使用redis,我们直接引入相关的包 ...
- SpringBoot自动化配置之二:自动配置(AutoConfigure)原理、EnableAutoConfiguration、condition
自动配置绝对算得上是Spring Boot的最大亮点,完美的展示了CoC约定优于配置: Spring Boot能自动配置Spring各种子项目(Spring MVC, Spring Security, ...
- SpringBoot自动化配置之四:SpringBoot 之Starter(自动配置)、Command-line runners
Spring Boot Starter是在SpringBoot组件中被提出来的一种概念,stackoverflow上面已经有人概括了这个starter是什么东西,想看完整的回答戳这里 Starter ...
- SpringBoot自动化配置之一:SpringBoot内部的一些自动化配置原理
springboot用来简化Spring框架带来的大量XML配置以及复杂的依赖管理,让开发人员可以更加关注业务逻辑的开发. 比如不使用springboot而使用SpringMVC作为web框架进行开发 ...
- 让SpringBoot自动化配置不再神秘
本文若有任何纰漏.错误,还请不吝指出! 注:本文提到的Spring容器或者Bean容器,或者Spring Bean容器,都是指同一个事情,那就是代指BeanFactory.关于BeanFactory, ...
- SpringBoot自动化配置之四:@Conditional注解详解
前言 之前在分析spring boot 源码时导出可见@ConditionalOnBean 之类的注解,那么它到底是如何使用的以及其工作流程如何,我们这里就围绕以下几点来分析: @Conditiona ...
- SpringBoot自动化配置的注解开关原理
我们以一个最简单的例子来完成这个需求:定义一个注解EnableContentService,使用了这个注解的程序会自动注入ContentService这个bean. @Retention(Retent ...
- SpringBoot自动化配置之三:深入SpringBoot:自定义EnableAutoConfiguration
前言 上面几篇文章介绍了SpringFramework的一些原理,这里开始介绍一下SpringBoot,并通过自定义一些功能来介绍SpringBoot的原理.SpringBoot在SpringFram ...
- SpringBoot 为什么能够自动的注入一些常用的Bean ?详细分析SpringBoot 自动配置的实现
转载至:https://blog.csdn.net/qq_29941401/article/details/79605388 有一个问题一直让我好奇,为什么在SpringBoot中有的bean 我们都 ...
- SpringBoot自动配置(装配)流程
源码分析 SpringBoot自动配置流程 首先,我们要了解在@SpringBootApplication注解的内部,还具有@EnableAutoConfiguration,@SpringBo ...
随机推荐
- MyBatis_01(前置知识)
1-学习思路(课程主要内容): 2-MyBatis特性 3- MyBatis下载 但是我们在使用MyBatis的时候,都是直接 "Maven导入MyBatis的jar包" (所以, ...
- 【Java学习Day10】类型转换
类型转换 由于Java是强类型语言,所以要进行某些运算的时候,需要用到类型转换 低------------------------------------------>高 byte-->s ...
- java中取整数绝对值_Java之——位运算求整数绝对值通过下面的位运算可以得到一个整数的绝对值
public int abs( int a ) {return (a + (a >> 31)) ^ (a >> 31) ;//前半部分-1或+0,后半部分取反 } a为正数的情 ...
- LeetCode习题集
作为一名本科计算机科学与技术的学生,<数据结构与算法><算法分析与设计>这两门课没有学到特别好 上了研究生之后感觉,这些东西需要重新拾起 于是,我找了一个平台,LeetCode ...
- 【阿里云ACP】-02(弹性储存、对象储存OSS)
弹性网卡 弹性网卡: 是一种可以附加到专有网络VPC类型ECS实例上的虚拟网卡.通过弹性网卡,您可以在任何阿里云地域下实现高可用集群搭建.低成本故障转移和精细化的网络管理. 弹性网卡属性: 属性 规格 ...
- RecyclerView setHasFixedSize(true)
RecyclerView setHasFixedSize(true)当recycleview大小高宽不变的时候使用这个,可以提升效率
- map函数中调用多个async await请求出现的promise问题解决
以上这个打印会返回[promise,promise,promise]那么是什么原因造成的呢?我们先来一个方法解决: 但是以上这种解决方式并没有真正解决问题,还是会返回一个[promise,promis ...
- Centos8——Nginx下载安装 & 部署项目
Centos8--Nginx下载安装 & 部署项目 官网:http://nginx.org/ 官网下载:http://nginx.org/en/download.html 创建文件夹 ps: ...
- shell中;与&&的区别
&& 后一个命令执行需要依赖前一个命令执行成功 :各个命令都会依次执行,无论成功与否
- vue子组件为父组件属性写值
父组件调用子组件代码(关键字sync): <importModel :visible.sync="dialogModelVisible"></importMode ...