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> onMissingClasses = getCandidates(metadata,
ConditionalOnMissingClass.class);
if (onMissingClasses != null) {
List<String> present = filter(onMissingClasses,
ClassNameFilter.PRESENT, classLoader);
if (!present.isEmpty()) {
return
ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class)
.found("unwanted class", "unwanted classes").items(Style.QUOTE, present));
}
matchMessage =
matchMessage.andCondition(ConditionalOnMissingClass.class)
.didNotFind("unwanted class", "unwanted classes")
.items(Style.QUOTE, filter(onMissingClasses,
ClassNameFilter.MISSING, classLoader));
}
return ConditionOutcome.match(matchMessage);
}
在getMatchOutcome方法中的逻辑为:
1.如果类或方法上有@ConditionalOnClass注解,则获取@ConditionalOnClass注解中的value属性,也就是要判断是否存在的类名
2.利用ClassNameFilter.MISSING来判断这些类是否缺失,把缺失的类的类名存入missing集合
3.如果missing不为空,则表示有类缺失,则表示不匹配,并利用ConditionMessage记录哪些类是缺失的,直接 return,表示条件不匹配
4.否则,则表示条件匹配,继续执行代码
5.如果类或方法上有ConditionalOnMissingClass注解,则获取ConditionalOnMissingClass注解中的value属 性,也就是要判断是否缺失的类名
6.利用ClassNameFilter.PRESENT来判断这些类是否存在,把存在的类的类名存入present集合
7.如果present不为空,则表示有类存在,则表示不匹配,并利用ConditionMessage记录哪些类是存在的,直接 return,表示条件不匹配
8否则,则表示条件匹配,继续执行代码
9.return,表示条件匹配
@Configuration
@ConditionalOnClass(Tomcat.class)
@ConditionalOnMissingClass(value = "com.zhouyu.Tomcat")
public class ZhouyuConfiguration {
}
1.如果@ConditionalOnClass条件匹配、@ConditionalOnMissingClass条件也匹配,那么getMatchOutcome 方法会执行两次
2.如果@ConditionalOnClass条件不匹配,那么getMatchOutcome方法会执行一次
3.如果@ConditionalOnClass条件匹配、@ConditionalOnMissingClass条件不匹配,那么getMatchOutcome方法也只会执行一次,因为在getMatchOutcome方法处理了这种情况
protected enum ClassNameFilter {
PRESENT {
@Override
public boolean matches(String className, ClassLoader
classLoader) {
return isPresent(className, classLoader);
}
},
MISSING {
@Override
public boolean matches(String className, ClassLoader
classLoader) {
return !isPresent(className, classLoader);
}
};
abstract boolean matches(String className, ClassLoader classLoader);
static boolean isPresent(String className, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
try {
resolve(className, classLoader);
return true;
}
catch (Throwable ex) {
return false;
}
}
}
protected static Class<?> resolve(String className, ClassLoader classLoader) throws
ClassNotFoundException {
主要就是用类加载器,来判断类是否存在。
@ConditionalOnBean和@ConditionalOnClass的底层实现应该是差不多的,一个是判断Bean存不
存在,一个是判断类存不存在,事实上也确实差不多。
首先@ConditionalOnBean和@ConditionalOnMissingBean对应的都是OnBeanCondition类,
OnBeanCondition类也是继承了SpringBootCondition,所以SpringBootCondition类中的
getMatchOutcome方法才是匹配逻辑:
if (classLoader != null) {
return Class.forName(className, false, classLoader);
}
return Class.forName(className);
}