SpringBoot自动装配源码分析

1.源码分析

首先我们进入@SpringBootApplication注解
在这里插入图片描述
找到@EnableAutoConfiguration注解
在这里插入图片描述
我们先打开@EnableAutoConfiguration注解
在这里插入图片描述
我们来看主要入口@Import({AutoConfigurationImportSelector.class})
在这里插入图片描述
我们点进AutoConfigurationImportSelector.class
在这里插入图片描述
我们发现AutoConfigurationImportSelector类实现了DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered六个接口,我们观察接口发现,这六个接口可以分成三类,一类是DeferredImportSelector ,第二类是以Aware结尾的BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware,第三类是Ordered。让我们由简到难来分析这三类接口。

第一类
优先级很好理解,就不过多解释了,我们点开getOrder方法找到AutoConfigurationImport Selector实现类中的getOrde()方法。
在这里插入图片描述
通过观察得知:getOrder()方法,返回的优先级是最高优先级-1,由此接口我们可以知道,类的加载是由顺序的,为什么有顺序?我们假设一共有20个类需要加载,那么如果第10个类的加载依赖第20个类,那么如果第20个类加载在第10个类的后面,那么第10个类的加载是失败的,所以第20个类就必须加载在第10个类的前面。
第2类接口
以Aware结尾的BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware接口。实现此类接口的Bean可以在此环境中使用Aware前面的对象。(在此无需过多关注此类接口)

第3类接口(重点)
DeferredImportSelector接口。此接口实现ImportSelector接口,此接口中有selectImports方法,此方法会在process方法执行后执行(为什么不是直接调用selectImports方法而是在process 方法后执行可看第二点
![在这里插入图片描述](https://img-blog.csdnimg.cn/0a2d8881f96d40498ce17a2769253d15.png
我们点开getAutoConfigurationEntry()方法
在这里插入图片描述

我们可以看到getAutoConfigurationEntry()中就是一次逻辑判断,那么我们就看看逻辑判断的条件,(!this.isEnabled(annotationMetadata))此逻辑判断方法是判断springboot引导类上是否存在@springbootApplication注解,如果不存在,则返回EMPTY_ENTRY(空目录),如果存在,则开始读取@springbootApplication注释中的属性
进入getAutoConfigurationEntry()方法中我们找到getCandidateConfigurations方法
在这里插入图片描述
进入getCandidateConfigurations方法找到loadFactoryNames方法
在这里插入图片描述
进入loadSpringFactories方法
在这里插入图片描述
因此我们知道了,自动转给最终会读取从一个叫“META-INF/spring.factorites”的文件中读取需要配置的候选配置。

2.SpringBoot自动装配的时候为什么没有走selectImports方法?

我们之前在分析SpringBoot自动装配源码的时候讲过在 @EnableAutoConfiguration注解上通过 @Import注解导入了一个 ImportSelector接口的实现类 AutoConfigurationImportSelector。按照之前对 @Import 注解的理解,应该会执行重写的 selectImports 方法,但调试的时候,执行的流程好像和我们期待的不一样, 没有走 selectImports方法。
通过Debug模式,端点定位我们能够发现实际上进入到了getAutoConfigurationEntry方法中。
在这里插入图片描述
但是没有进入selectImports方法。
在这里插入图片描述
这是什么原因呢?他不是实现了 ImportSelector接口吗?为什么没有走selectImports方法,这就需要我们再来细说下@Import注解了。

2.1@Import

@Import注解可以根据添加的不同类型做出不同的操作

导入类型注入方式
实现了ImportSelector接口不注入该类型的对象,调用selectImports方法,将返回的数据注入到容器中
实现了ImportBeanDefinitionRegistrar接口不注入该类型的对象,调用registerBeanDefinitions方法,通过注册器注入
普通类型直接注入该类型的对象

而在自动装配中导入的AutoConfigurationImportSelector这个类型有点特殊。具体看下类图结构
在这里插入图片描述

那这个DeferredImportSelector这个接口的作用是什么呢?字面含义是延迟导入的意思。具体怎么实现的后面再说,我们先来说下他的作用。

2.2 DeferredImportSelector接口

DeferredImportSelector接口本身也有ImportSelector接口的功能,如果我们仅仅是实现了DeferredImportSelector接口,重写了selectImports方法,那么selectImports方法还是会被执行的,来看代码。

public class MyDeferredImportSelector implements DeferredImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        System.out.println("selectImports方法执行了---->");
        return new String[0];
    }
}

对应的配置启动类

@Configuration
@Import(MyDeferredImportSelector.class)
public class JavaConfig {

    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(JavaConfig.class);
    }
}

启动效果:
在这里插入图片描述

但是如果我们重写了DeferredImportSelector中的Group接口,并重写了getImportGroup,那么容器在启动的时候就不会执行selectImports方法了,而是执行getImportGroup方法。进而执行Group中重写的方法。

public class MyDeferredImportSelector implements DeferredImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        System.out.println("selectImports方法执行了---->");
        return new String[0];
    }

    @Override
    public Class<? extends Group> getImportGroup() {
        System.out.println("getImportGroup");
        return MyDeferredImportSelectorGroup.class;
    }

    public static class MyDeferredImportSelectorGroup implements Group{
        private final List<Entry> imports = new ArrayList<>();
        @Override
        public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
            System.out.println("MyDeferredImportSelectorGroup.Group");
        }

        @Override
        public Iterable<Entry> selectImports() {
            System.out.println("Group中的:selectImports方法");
            return imports;
        }
    }
}


执行效果:
在这里插入图片描述
通过上面的效果解释了为什么在SpringBoot自动装配的时候没有走selectImports方法。那么DeferredImportSelector接口的作用是什么呢?为什么要这么设计呢?我们接下来继续分析

2.3DeferredImportSelector的作用

通过前面的类图结构我们知道DeferredImportSelector是ImportSelector接口的一个扩展。
在这里插入图片描述
ImportSelector实例的selectImports方法的执行时机,是在@Configguration注解中的其他逻辑被处理之前,所谓的其他逻辑,包括对@ImportResource@Bean这些注解的处理(注意,这里只是对@Bean修饰的方法的处理,并不是立即调用@Bean修饰的方法,这个区别很重要!)
DeferredImportSelector实例的selectImports方法的执行时机,是在@Configguration注解中的其他逻辑被处理完毕之后,所谓的其他逻辑,包括对@ImportResource@Bean这些注解的处理.

上面的结论我们可以直接在源码中看到对应的答案。首先定位到ConfigurationClassParser中的parse方法。
在这里插入图片描述
上面代码有两个非常重要的分支,我们在下面逐一的介绍

parse方法
我们先看parse方法,也就是解析注解类的方法。进入
在这里插入图片描述看到调用的是processConfigurationClass,翻译过来就比较好理解了,处理配置类
在这里插入图片描述
再进入到循环的方法中。
在这里插入图片描述
继续往下看
在这里插入图片描述

在这里插入图片描述
逻辑处理还是非常清楚的。然后我们需要回到上面的处理@Import注解的方法processImports中。在这个方法中我们可以看到@Import注解的实现逻辑
在这里插入图片描述
也就是前面给大家回顾的@Import注解的作用

导入类型注入方式
实现了ImportSelector接口不注入该类型的对象,调用selectImports方法,将返回的数据注入到容器中
实现了ImportBeanDefinitionRegistrar接口不注入该类型的对象,调用registerBeanDefinitions方法,通过注册器注入
普通类型直接注入该类型的对象

然后来看下导入的类型是ImportSelector接口的逻辑。
在这里插入图片描述
上面的代码重点解决了ImportSelector接口的不同类型的实现。
在这里插入图片描述
对应的实例存储了起来
在这里插入图片描述

process方法
好了上面的代码分析清楚了,然后我们再回到process方法中来看下DeferredImportSelectorHandler是如何处理的。
在这里插入图片描述
进入process方法
在这里插入图片描述
先看register方法
在这里插入图片描述
然后再看processGroupImports方法。
在这里插入图片描述
进去后我们需要进入getImports方法中。
在这里插入图片描述
最后我们进入到process方法中,可以看到自动装配的方法被执行了!
在这里插入图片描述
到这儿我们就清晰了自动装配为什么没有走 AutoConfigurationImportSelector中的 selectImports 方法了

同时也介绍清楚了ImportSelectorDeferredImportSelector的区别,就是selectImports方法执行时机有差别,这个差别期间,spring容器对此Configguration类做了些其他的逻辑:包括对@ImportResource@Bean这些注解的处理

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值