1.
@EnableAutoConfiguration源码解析
2.
SpringBoot常用条件注解源码解析
3.
SpringBoot之Mybatis自动配置源码解析
4.
SpringBoot之AOP自动配置源码解析
5.
SpringBoot Jar包启动过程源码解析
DeferredImportSelector接口
DeferredImportSelector和ImportSelector的区别在于:
1. 在解析ImportSelector时,所导入的配置类会被直接解析,而DeferredImportSelector导入的配置类会延迟进行解析(延迟在其他配置类都解析完之后)
2. DeferredImportSelector支持分组,可以实现getImportGroup方法以及定义Group对象,就相当于指定了DeferredImportSelector所导入进来的配置类所属的组,比如SpringBoot就把所有自动配置类单独做了分组 AutoConfigurationGroup
常用条件注解
SpringBoot中的常用条件注解有:
1. ConditionalOnBean:是否存在某个某类或某个名字的Bean
2. ConditionalOnMissingBean:是否缺失某个某类或某个名字的Bean
3. ConditionalOnSingleCandidate:是否符合指定类型的Bean只有一个
4. ConditionalOnClass:是否存在某个类
5. ConditionalOnMissingClass:是否缺失某个类
6. ConditionalOnExpression:指定的表达式返回的是true还是false
7. ConditionalOnJava:判断Java版本
8. ConditionalOnWebApplication:当前应用是不是一个Web应用
9. ConditionalOnNotWebApplication:当前应用不是一个Web应用
10. ConditionalOnProperty:Environment中是否存在某个属性
当然我们也可以利用@Conditional来自定义条件注解。
条件注解是可以写在类上和方法上的,如果某个条件注解写在了自动配置类上,那该自动配置类会不会生效就要看当前条件能不能符合,或者条件注解写在某个@Bean修饰的方法上,那这个Bean生不生效就看当前条件符不符合。
具体原理是:
1.Spring在解析某个自动配置类时,会先检查该自动配置类上是否有条件注解,如果有,则进一步判断该条件注解 所指定的条件当前能不能满足,如果满足了则继续解析该配置类,如果不满足则不进行解析了,也就是配置类所定义的Bean都得不到解析,也就是相当于没有这些Bean了。
2.同理,Spring在解析某个@Bean的方法时,也会先判断方法上是否有条件注解,然后进行解析,如果不满足条件,则该Bean不会生效
我们可以发现,SpringBoot的自动配置,实际上就是SpringBoot的源码中预先写好了一些配置类,预 先定义好了一些Bean,我们在用SpringBoot时,这些配置类就已经在我们项目的依赖中了,而这些自动配置类或自动配置Bean到底生不生效,就看具体所指定的条件了。
自定义条件注解
SpringBoot中众多的条件注解,都是基于Spring中的@Conditional来实现的,所以我们先来用一下
@Conditional注解。
先来看下@Conditional注解的定义:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition} classes that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
根据定义我们在用@Conditional注解时,需要指定一个或多个Condition的实现类,所以我们先来提供一个实现类:
public class ZhouyuCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false;
}
}
很明显,我们可以在matches方法中来定义条件逻辑:
1.ConditionContext:表示条件上下文,可以通过ConditionContext获取到当前的类加载器、BeanFactory、 Environment环境变量对象
2.AnnotatedTypeMetadata:表示当前正在进行条件判断的Bean所对应的类信息,或方法信息(比如@Bean定 义的一个Bean),可以通过AnnotatedTypeMetadata获取到当前类或方法相关的信息,从而就可以拿到条件注 解的信息,当然如果一个Bean上使用了多个条件注解,那么在解析过程中都可以获取到,同时也能获取Bean上定义的其他注解信息
@ConditionalOnClass的底层工作原理
先来看一个案例:
@Configuration
@ConditionalOnClass(name = "com.zhouyu.Jetty")
@ConditionalOnMissingClass(value = "com.zhouyu.Tomcat")
public class ZhouyuConfiguration {
}
我们在ZhouyuConfiguration这个类上使用了两个条件注解:
1.@ConditionalOnClass(name = "com.zhouyu.Jetty"):条件是项目依赖中存在 "com.zhouyu.Jetty"这个类,则 表示符合条件2.@ConditionalOnMissingClass(value = "com.zhouyu.Tomcat"):条件是项目依赖中不存 在 "com.zhouyu.Tomcat"这个类,则表示符合条件
这两个注解对应的都是
@Conditional(OnClassCondition.class)
,那在OnClassCondition类中是如 何对这两个注解进行区分的呢?
Spring在解析到ZhouyuConfiguration这个配置时,发现该类上用到了条件注解就会进行条件解析, 相关源码如下:
// 这是Spring中的源码,不是SpringBoot中的
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;
}
}
conditions中保存了两个OnClassCondition对象,这段代码会依次调用OnClassCondition对象的 matches方法进行条件匹配,一旦某一个条件不匹配就不会进行下一个条件的判断了,这里return的 是true,但是这段代码所在的方法叫做shouldSkip,所以true表示忽略。
我们继续看OnClassCondition的matches()方法的实现。
OnClassCondition类继承了FilteringSpringBootCondition,FilteringSpringBootCondition类又继承了SpringBootCondition,而SpringBootCondition实现了Condition接口,matches()方法也是在SpringBootCondition这个类中实现的:
@Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取当前解析的类名或方法名
String classOrMethodName = getClassOrMethodName(metadata);
try {
// 进行具体的条件匹配,ConditionOutcome表示匹配结果
ConditionOutcome outcome = getMatchOutcome(context, metadata);
// 日志记录匹配结果
logOutcome(classOrMethodName, outcome);
recordEvaluation(context, classOrMethodName, outcome);
// 返回true或false
return outcome.isMatch();
}
catch (NoClassDefFoundError ex) {
// ...
}
catch (RuntimeException ex) {
// ...
}
}
所以具体的条件匹配逻辑在getMatchOutcome方法中,而SpringBootCondition类中的
getMatchOutcome方法是一个抽象方法,具体的实现逻辑就在子类OnClassCondition中:
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ClassLoader classLoader = context.getClassLoader();
ConditionMessage matchMessage = ConditionMessage.empty();
// 拿到ConditionalOnClass注解中的value值,也就是要判断是否存在的类名
List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
if (onClasses != null) {
// 在getMatchOutcome方法中的逻辑为:
// 判断onClasses中不存在的类
List<String> missing = filter(onClasses, ClassNameFilter.MISSING,classLoader);
// 如果有缺失的类,那就表示不匹配
if (!missing.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
.didNotFind("required class", "required classes").items(Style.QUOTE, missing));
}
// 否则就表示匹配
matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
.found("required class", "required classes")
.items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader));
}
// 和上面类似,只不过是判断onMissingClasses是不是全部缺失,如果是则表示匹配
List<String> onMissingCla