12、常见Conditional注解源码解析(ok)
参考资料:彻底搞定 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)相关推荐
- 12、常见Conditional注解源码解析-ConditionalOnBean(写作中...)
ConditionalOnBean 源码解析 ConditionalOnBean 注解 看看 ConditionalOnBean 注解 @Conditional(OnBeanCondition.cla ...
- 12、常见Conditional注解源码解析-ConditionalOnClass
举例(代码如下)说明@ConditionalOnClass注解的作用: 在 classpath 下加载 DruidDataSource.class(classpath下有对应的jar包) , 加载成功 ...
- 过滤器、监听器常见应用(源码解析)
过滤器.监听器常见应用 监听器:GUI(图形界面编程)编码中经常使用 Filter实现权限拦截: 用户登录之后才能登录首页!用户注销之后就不能进入主页了! 登录页面: package com.kk.s ...
- Retry注解源码解析
@Retry注解是Spring提供的一个重试注解,使用简单(这里是为了分析其原理,其实个人认为guava的重试好用多了). 使用例子 引入pom.xml依赖 <dependency>< ...
- Spring 事务源码(2)—<tx:annotation-driven/>事务标签源码解析
基于最新Spring 5.x,详细介绍了Spring 事务源码,包括< tx:annotation-driven/>标签源码解析. 此前我们已经学习了Spring的<tx:a ...
- Spring源码深度解析(郝佳)-Spring 常用注解使用及源码解析
我们在看Spring Boot源码时,经常会看到一些配置类中使用了注解,本身配置类的逻辑就比较复杂了,再加上一些注解在里面,让我们阅读源码更加难解释了,因此,这篇博客主要对配置类上的一些注解的使用 ...
- Spring源码解析之@Component注解的扫描
阅读须知 Spring源码版本:4.3.8 文章中使用/* */注释的方法会做深入分析 正文 承接Spring源码解析之context:component-scan标签解析,下面就是扫描的流程: Cl ...
- Android xUtils3源码解析之注解模块
本文已授权微信公众号<非著名程序员>原创首发,转载请务必注明出处. xUtils3源码解析系列 一. Android xUtils3源码解析之网络模块 二. Android xUtils3 ...
- spring 注解试事物源码解析
spring 注解试事物源码解析 基于xml注解式事务入口 public class TxNamespaceHandler extends NamespaceHandlerSupport {stati ...
最新文章
- 二叉树外部节点_leetcode 102 二叉树的层序遍历1 /BFS
- 用启明云端基于ESP32模块的开发板来快速了解天猫精灵
- kindle阅读_如何在Kindle上清除最远的阅读页面
- python中tkinter的使用-下
- 部署knight项目
- 叠螺机_火锅底料加工车间废水离不开叠螺机应用
- 从零开始学统计 05 | 技术重复和生物学重复
- ABAP BDC使用EXCEL模板批量修改物料
- 5 个有效好用的恢复文件和照片的 Android恢复软件推荐
- 怎样用excel剔除异常数据_excel如何去除异常值?(excel表格数据异常丢失)
- 搜狗输入html,搜狗输入法:回家的路
- 串级PID 位置环+速度环
- Swift - GCD 和延时调用
- 【Android系列1.0 驱动开发 --- Linux 驱动 内核】
- iOS开发系列--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook详解
- 浅谈架构、操作系统、芯片的理解
- OpenCVSharp异常--OpenCVSharp.NativeMethods的初始值异常
- 2020年书法落款_学书法的请注意,落款不要写“庚子年”
- STM32中文参考手册下载地址
- Dijkstra-POJ-2387-Til the Cows Come Home
热门文章
- jqGrid 实现这种select - 同一列的不同行的select 的option 不同
- 收集的一些好的网址[持续更新]
- bt解析 开源 java_开源一个BT客户端:BitWave
- cdr三角形转化为圆角_CDR金属质感小图标图文解析
- 【电脑帮助】解决Wind10系统桌面没有“我的电脑”图标的问题
- js在html中加文字走马灯特效,jQuery简单的文字跑马灯特效
- 用python的pandas打开csv文件_使用CSV模块和Pandas在Python中读取和写入CSV文件
- linux svn上传目录_Linux系统下svn更新自动同步到web目录
- 如何将网站前端如何添加登录密码访问_如何在Mac上查找保存的密码的所有信息...
- excel小写转大写公式_喂!这边居然有演示的Excel快捷键!