@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 ...
随机推荐
- python随机数模拟
`#随机数 import random red = range(1,36,1) red_target_list=[] i=1 while i< 6: red_ran_index=random.r ...
- drf Serializer基本使用
drf序列化 在前后端不分离的项目中,可以使用Django自带的forms组件进行数据验证,也可以使用Django自带的序列化组件对模型表数据进行序列化. 那么在前后端分离的项目中,drf也提供了数据 ...
- vscode 终端中运行执行js文件
问题汇总 1.在vscode中执行node -v没有反应或者执行js文件出现下图错误 解决办法: 1.先关闭vscode,找到vscode的执行文件,在兼容性中勾上以管理员身份运行此程序,该问题win ...
- python bottle小记
# coding=utf-8import bottle @bottle.route('/url/url', method=['GET','POST'])def big_data(): # 获取请求参数 ...
- strace 跟踪特定系统调用
/usrdsk/strace -tt -e trace=write -p 664 /usrdsk/strace -tt -e trace=%memory -p 664
- ESXI密码正确无法登录
场景描述: 今天新安装了一个VMware ESXi 6.5的系统,密码仍然用的习惯采用的密码.但在使用中,无论是使用vSphere Client连接,还是在vCenter Server中添加这台ESX ...
- mac上gitclone出现password: Permission denied, please try again.
问题在mac上拉取项目出现一下问题,然后我输入密码还是这样的提示. password: Permission denied, please try again.1分析解决情况1:git地址为http协 ...
- RTC@@@Real-Time Clock(实时时钟的简称)及电路问题分析
RTC@@@Real-Time Clock(实时时钟的简称) 实时时钟(Real-Time Clock)是PC主板上的晶振及相关电路组成的时钟电路的生成脉冲,提供稳定的时钟信号给后续电路用.主要功能有 ...
- JavaScript的原型和原型链
说到JavaScript的原型和原型链,相关文章已有不少,但是大都晦涩难懂.本文将换一个角度出发,先理解原型和原型链是什么,有什么作用,再去分析那些令人头疼的关系. 一.引用类型皆为对象 原型和原型链 ...
- [CSP-S2019] Emiya 家今天的饭
洛咕 题意:原题面见链接,简单来说就是给出一个\(n*m\)的矩阵,每一行代表同一种烹饪方法,每一列代表同一种食材,\(a_{i,j}\)表示使用第i种烹饪方法第j种食材能做出多少种菜,要求至少做一道 ...