参考资料:彻底搞定 Java 注解

Spring Boot中所有的@Conditional 注解如下:

Conditional (o.s.context.annotation)|-ConditionalOnBean (o.s.boot.autoconfigure.condition)|-ConditionalOnClass (o.s.boot.autoconfigure.condition)|-ConditionalOnCloudPlatform (o.s.boot.autoconfigure.condition)|-ConditionalOnEnabledResourceChain (o.s.boot.autoconfigure.web)|-ConditionalOnExpression (o.s.boot.autoconfigure.condition)|-ConditionalOnJava (o.s.boot.autoconfigure.condition)|-ConditionalOnJndi (o.s.boot.autoconfigure.condition)|-ConditionalOnMissingBean (o.s.boot.autoconfigure.condition)|-ConditionalOnMissingClass (o.s.boot.autoconfigure.condition)|-ConditionalOnNotWebApplication (o.s.boot.autoconfigure.condition)|-ConditionalOnProperty (o.s.boot.autoconfigure.condition)|-ConditionalOnResource (o.s.boot.autoconfigure.condition)|-ConditionalOnSingleCandidate (o.s.boot.autoconfigure.condition)|-ConditionalOnWebApplication (o.s.boot.autoconfigure.condition)|-Profile (o.s.context.annotation)

我们选最常用的ConditionalOnJava ConditionalOnClass ConditionalOnBean 3个来解析其源码 , 有了这几个基础后 , 对于剩余的大家可以自行查看源码 .

首先要明白的是,上面列出所有的条件注解都使用了 @Conditional 来标注,其value值是个Condition 类型的数组。

在 Spring Framework 的源码中,有个地方的代码会处理标注在Bean上的条件注解,并获取@Conditional 注解中的 Condition 接口的实现类,使用反射创建该实现类的实例并调用其matches方法。

我们画个图看看Spring Framework 处理条件注解的流程

从图中可以看出 , 这个注解的处理时在解析配置文件时来处理的 , 创建Condition接口实现类的实例使用的是反射 , 而不是使用Spring中的getBean来创建的 .

思考: 为什么不像创建ConfigurationClassPostProcessor对象bean一样使用getBean?

Condition是和配置项相关的 , 每个Condition 可能都不一样 , 不可复用, 也就没必要使用bean的方式创建对象从而放入容器中

1/@ConditionalOnJava

1.1/测试程序

1.2/注解定义

1.3/条件匹配类

2/ @ConditionalOnClass

1.1/测试程序

1.2/注解定义

1.3/条件匹配类

3/ @ConditionalOnBean

1.1/测试程序

1.2/注解定义

1.3/条件匹配类

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {/*** All {@link Condition}s that must {@linkplain Condition#matches match}* in order for the component to be registered.*/Class<? extends Condition>[] value();}
public interface Condition {/*** Determine if the condition matches.* @param context the condition context* @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}* or {@link org.springframework.core.type.MethodMetadata method} being checked.* @return {@code true} if the condition matches and the component can be registered* or {@code false} to veto registration.*/boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);}

我翻看了几个注解的源码,发现 OnJavaCondition 比较简单,我们就从它先开始

1、OnJavaCondition

1.1、测试程序:

@Component
@FirstConditionAnnotation(values={"firstCondition1","firstCondition2"})
@ConditionalOnJava(range = ConditionalOnJava.Range.EQUAL_OR_NEWER,value = ConditionalOnJava.JavaVersion.SEVEN)
public class FirstConditionBean {
}@SpringBootApplication
public class HelloSpringBootApplication {public static void main(String[] args) {SpringApplication.run(HelloSpringBootApplication.class,args);}
}

1.2、OnJavaCondition调用入口

我在OnJavaCondition#getMatchOutcome 方法上打上断点,启动程序调用栈如下:

getMatchOutcome:45, OnJavaCondition (o.s.boot.autoconfigure.condition)
matches:47, SpringBootCondition (o.s.boot.autoconfigure.condition)
shouldSkip:102, ConditionEvaluator (o.s.context.annotation)
shouldSkip:81, ConditionEvaluator (o.s.context.annotation)
shouldSkip:64, ConditionEvaluator (o.s.context.annotation)
isConditionMatch:371, ClassPathScanningCandidateComponentProvider (o.s.context.annotation)
isCandidateComponent:355, ClassPathScanningCandidateComponentProvider (o.s.context.annotation)
// 查找包下的所有的标注了Contitional注解的类,发现了FirstConditionBean标注了ConditionalOnJava
// 此时会实例化 ConditionalOnJava 注解的 value 值 OnJavaCondition,并调用其matches方法
findCandidateComponents:288, ClassPathScanningCandidateComponentProvider (o.s.context.annotation)
// 扫描HelloSpringBootApplication所在的包
doScan:272, ClassPathBeanDefinitionScanner (o.s.context.annotation)
parse:135, ComponentScanAnnotationParser (o.s.context.annotation)
// 处理 HelloSpringBootApplication的 @ComponentScan 注解
doProcessConfigurationClass:287, ConfigurationClassParser (o.s.context.annotation)
processConfigurationClass:245, ConfigurationClassParser (o.s.context.annotation)
parse:198, ConfigurationClassParser (o.s.context.annotation)
// 解析配置类 HelloSpringBootApplication
parse:167, ConfigurationClassParser (o.s.context.annotation)
processConfigBeanDefinitions:308, ConfigurationClassPostProcessor (o.s.context.annotation)
postProcessBeanDefinitionRegistry:228, ConfigurationClassPostProcessor (o.s.context.annotation)
invokeBeanDefinitionRegistryPostProcessors:272, PostProcessorRegistrationDelegate (o.s.context.support)
invokeBeanFactoryPostProcessors:92, PostProcessorRegistrationDelegate (o.s.context.support)
invokeBeanFactoryPostProcessors:687, AbstractApplicationContext (o.s.context.support)
refresh:525, AbstractApplicationContext (o.s.context.support)
refresh:122, EmbeddedWebApplicationContext (o.s.boot.context.embedded)
refresh:693, SpringApplication (o.s.boot)
refreshContext:360, SpringApplication (o.s.boot)
run:303, SpringApplication (o.s.boot)
run:1118, SpringApplication (o.s.boot)
run:1107, SpringApplication (o.s.boot)
main:14, HelloSpringBootApplication (com.yh.stu.springboot)

调用过程在调用栈中已经注释了,这里总结一下:

// TODO

OnJavaCondition 源码解析

@Conditional(OnJavaCondition.class)
public @interface ConditionalOnJava {Range range() default Range.EQUAL_OR_NEWER;JavaVersion value();enum Range {/*** Equal to, or newer than the specified {@link JavaVersion}.*/EQUAL_OR_NEWER,/*** Older than the specified {@link JavaVersion}.*/OLDER_THAN}enum JavaVersion {/*** Java 1.9.*/NINE(9, "1.9", "java.security.cert.URICertStoreParameters"),/*** Java 1.8.*/EIGHT(8, "1.8", "java.util.function.Function"),......JavaVersion(int value, String name, String className) {this.value = value;this.name = name;// className指定了各个版本新增的class类,Class.forName(className)抛异常则说明不支持改版本this.available = ClassUtils.isPresent(className, getClass().getClassLoader());}// 返回当前环境支持的最高版本public static JavaVersion getJavaVersion() {for (JavaVersion candidate : JavaVersion.values()) {// 选取支持的最高版本if (candidate.available) {return candidate;}}return SIX;}}......
}

OnJavaCondition

OnJavaCondition的类图:

我们在 OnJavaCondition 中没有发现 matches 方法,所以我们看看其父类

/************** org.springframework.boot.autoconfigure.condition.SpringBootCondition **************/
@Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 这里的metadata是标注注解的类的元数据// 获取被注解类的class名称或者类名+方法名称// 本例中的值: com...FirstConditionBeanString classOrMethodName = getClassOrMethodName(metadata);try {ConditionOutcome outcome = getMatchOutcome(context, metadata);logOutcome(classOrMethodName, outcome);recordEvaluation(context, classOrMethodName, outcome);return outcome.isMatch();}catch (NoClassDefFoundError ex) {throw new IllegalStateException("......",ex);}catch (RuntimeException ex) {throw new IllegalStateException("......" + getName(metadata), ex);}
}
private static String getClassOrMethodName(AnnotatedTypeMetadata metadata) {if (metadata instanceof ClassMetadata) {ClassMetadata classMetadata = (ClassMetadata) metadata;return classMetadata.getClassName();}MethodMetadata methodMetadata = (MethodMetadata) metadata;return methodMetadata.getDeclaringClassName() + "#"+ methodMetadata.getMethodName();
}

@Order(Ordered.HIGHEST_PRECEDENCE + 20)
class OnJavaCondition extends SpringBootCondition {private static final JavaVersion JVM_VERSION = JavaVersion.getJavaVersion();@Overridepublic ConditionOutcome getMatchOutcome(ConditionContext context,AnnotatedTypeMetadata metadata) {// 本例中attributes 的值如下图,真实我们定义的值Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnJava.class.getName());Range range = (Range) attributes.get("range");JavaVersion version = (JavaVersion) attributes.get("value");return getMatchOutcome(range, JVM_VERSION, version);}protected ConditionOutcome getMatchOutcome(Range range, JavaVersion runningVersion,JavaVersion version) {// 判断是否满足版本要求boolean match = runningVersion.isWithin(range, version);String expected = String.format(range == Range.EQUAL_OR_NEWER ? "(%s or newer)" : "(older than %s)",version);// 创建ConditionMessage对象,并初始化一些信息ConditionMessage message = ConditionMessage.forCondition(ConditionalOnJava.class, expected).foundExactly(runningVersion);// 将匹配结果和message封装为ConditionOutcome 对象,方便SpringBootCondition统一处理return new ConditionOutcome(match, message);}}

参考资料:彻底搞定 Java 注解

12、常见Conditional注解源码解析(ok)相关推荐

  1. 12、常见Conditional注解源码解析-ConditionalOnBean(写作中...)

    ConditionalOnBean 源码解析 ConditionalOnBean 注解 看看 ConditionalOnBean 注解 @Conditional(OnBeanCondition.cla ...

  2. 12、常见Conditional注解源码解析-ConditionalOnClass

    举例(代码如下)说明@ConditionalOnClass注解的作用: 在 classpath 下加载 DruidDataSource.class(classpath下有对应的jar包) , 加载成功 ...

  3. 过滤器、监听器常见应用(源码解析)

    过滤器.监听器常见应用 监听器:GUI(图形界面编程)编码中经常使用 Filter实现权限拦截: 用户登录之后才能登录首页!用户注销之后就不能进入主页了! 登录页面: package com.kk.s ...

  4. Retry注解源码解析

    @Retry注解是Spring提供的一个重试注解,使用简单(这里是为了分析其原理,其实个人认为guava的重试好用多了). 使用例子 引入pom.xml依赖 <dependency>< ...

  5. Spring 事务源码(2)—<tx:annotation-driven/>事务标签源码解析

      基于最新Spring 5.x,详细介绍了Spring 事务源码,包括< tx:annotation-driven/>标签源码解析.   此前我们已经学习了Spring的<tx:a ...

  6. Spring源码深度解析(郝佳)-Spring 常用注解使用及源码解析

      我们在看Spring Boot源码时,经常会看到一些配置类中使用了注解,本身配置类的逻辑就比较复杂了,再加上一些注解在里面,让我们阅读源码更加难解释了,因此,这篇博客主要对配置类上的一些注解的使用 ...

  7. Spring源码解析之@Component注解的扫描

    阅读须知 Spring源码版本:4.3.8 文章中使用/* */注释的方法会做深入分析 正文 承接Spring源码解析之context:component-scan标签解析,下面就是扫描的流程: Cl ...

  8. Android xUtils3源码解析之注解模块

    本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...

  9. spring 注解试事物源码解析

    spring 注解试事物源码解析 基于xml注解式事务入口 public class TxNamespaceHandler extends NamespaceHandlerSupport {stati ...

最新文章

  1. 二叉树外部节点_leetcode 102 二叉树的层序遍历1 /BFS
  2. 用启明云端基于ESP32模块的开发板来快速了解天猫精灵
  3. kindle阅读_如何在Kindle上清除最远的阅读页面
  4. python中tkinter的使用-下
  5. 部署knight项目
  6. 叠螺机_火锅底料加工车间废水离不开叠螺机应用
  7. 从零开始学统计 05 | 技术重复和生物学重复
  8. ABAP BDC使用EXCEL模板批量修改物料
  9. 5 个有效好用的恢复文件和照片的 Android恢复软件推荐
  10. 怎样用excel剔除异常数据_excel如何去除异常值?(excel表格数据异常丢失)
  11. 搜狗输入html,搜狗输入法:回家的路
  12. 串级PID 位置环+速度环
  13. Swift - GCD 和延时调用
  14. 【Android系列1.0 驱动开发 --- Linux 驱动 内核】
  15. iOS开发系列--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook详解
  16. 浅谈架构、操作系统、芯片的理解
  17. OpenCVSharp异常--OpenCVSharp.NativeMethods的初始值异常
  18. 2020年书法落款_学书法的请注意,落款不要写“庚子年”
  19. STM32中文参考手册下载地址
  20. Dijkstra-POJ-2387-Til the Cows Come Home

热门文章

  1. jqGrid 实现这种select - 同一列的不同行的select 的option 不同
  2. 收集的一些好的网址[持续更新]
  3. bt解析 开源 java_开源一个BT客户端:BitWave
  4. cdr三角形转化为圆角_CDR金属质感小图标图文解析
  5. 【电脑帮助】解决Wind10系统桌面没有“我的电脑”图标的问题
  6. js在html中加文字走马灯特效,jQuery简单的文字跑马灯特效
  7. 用python的pandas打开csv文件_使用CSV模块和Pandas在Python中读取和写入CSV文件
  8. linux svn上传目录_Linux系统下svn更新自动同步到web目录
  9. 如何将网站前端如何添加登录密码访问_如何在Mac上查找保存的密码的所有信息...
  10. excel小写转大写公式_喂!这边居然有演示的Excel快捷键!