SpringBoot-SpringBoot自动配置底层源码解析

本文深入探讨了SpringBoot的自动配置机制,包括DeferredImportSelector接口的工作原理,条件注解如@ConditionalOnClass和@ConditionalOnBean的底层实现,以及Starter、Tomcat、AOP和Mybatis的自动配置细节。
摘要由CSDN通过智能技术生成
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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长情知热爱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值