SpringBoot自动装配原理笔记:视讯

来源: 腾讯云 时间:2022-11-30 12:20:18

理论介绍

SpringBoot通过自动装配实现了第三方框架系统对象的注入。这种实现机制和我们前面介绍的SPI(服务扩展机制)很相似。

源码分析

Spring的IoC

SpringBoot的本质是SpringFramework【IoC,AOP】的再次封装的上层应用框架。

run方法

我们启动一个SpringBoot项目,本质上就是执行了启动类中的主方法,然后调用执行了run方法,那么run方法到底做了什么操作呢?我们可以先来分析下:


(资料图片)

@SpringBootApplication@MapperScan("com.xx.mapper")public class SpringBootVipDemoApplication {    public static void main(String[] args) {        // 基于配置文件的方式        ApplicationContext ac1 = new ClassPathXmlApplicationContext("");        // 基于Java配置类的方式        ApplicationContext ac2 = new AnnotationConfigApplicationContext(SpringBootVipDemoApplication.class);// run 方法的返回对象是 ConfigurableApplicationContext 对象        ConfigurableApplicationContext ac3 = SpringApplication.run(SpringBootVipDemoApplication.class, args);    }}

ConfigurableApplicationContext这个对象其实是 ApplicationContext接口的一个子接口

那么上面的代码可以调整为

@SpringBootApplication@MapperScan("com.xx.mapper")public class SpringBootVipDemoApplication {    public static void main(String[] args) {        // 基于配置文件的方式        ApplicationContext ac1 = new ClassPathXmlApplicationContext("");        // 基于Java配置类的方式        ApplicationContext ac2 = new AnnotationConfigApplicationContext(SpringBootVipDemoApplication.class);        // run 方法执行完成后返回的是一个 ApplicationContext 对象        // 到这儿我们是不是可以猜测 run 方法的执行 其实就是Spring的初始化操作[IoC]        ApplicationContext ac3 = SpringApplication.run(SpringBootVipDemoApplication.class, args);    }}

根据返回结果,我们猜测SpringBoot项目的启动其实就是Spring的初始化操作【IoC】。

下一步:

下一步:

直接调用:

到这儿,其实我们就可以发现SpringBoot项目的启动,本质上就是Spring的初始化操作。但是并没有涉及到SpringBoot的核心装配。

@SpringBootApplication

@SpringBootApplication点开后我们能够发现@SpringBootApplication这个注解其实是一个组合注解

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(    excludeFilters = {@Filter(    type = FilterType.CUSTOM,    classes = {TypeExcludeFilter.class}), @Filter(    type = FilterType.CUSTOM,    classes = {AutoConfigurationExcludeFilter.class})})

我们发现@SpringBootApplication注解的前面四个注解是JDK中自动的元注解(用来修饰注解的注解)

@Target({ElementType.TYPE}) // 表明 修饰的注解的位置 TYPE 表示只能修饰类@Retention(RetentionPolicy.RUNTIME) // 表明注解的作用域@Documented // API 文档抽取的时候会将该注解 抽取到API文档中@Inherited  // 表示注解的继承

还有就是@ComponentScan注解,该注解的作用是用来指定扫描路径的,如果不指定特定的扫描路径的话,扫描的路径是当前修饰的类所在的包及其子包。

@SpringBootConfiguration这个注解的本质其实是@Configuration注解。

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented// 上面是3个元注解// @Configuration 注解修饰的Java类是一个配置类@Configuration// @Indexed@Indexedpublic @interface SpringBootConfiguration {    @AliasFor(        annotation = Configuration.class    )    boolean proxyBeanMethods() default true;}

这样一来7个注解,咱们清楚了其中的6个注解的作用,而且这6个注解都和SpringBoot的自动装配是没有关系的。

@EnableAutoConfiguration

@EnableAutoConfiguration这个注解就是SpringBoot自动装配的关键。

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})public @interface EnableAutoConfiguration {    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";​    Class[] exclude() default {};​    String[] excludeName() default {};}

我们发现要搞清楚EnableAutoConfiguration注解的关键是要弄清楚@Import注解。这个内容我们前面在注解编程发展中有详细的介绍。AutoConfigurationImportSelector实现了ImportSelector接口,那么我们清楚只需要关注selectImports方法的返回结果即可

public String[] selectImports(AnnotationMetadata annotationMetadata) {        if (!this.isEnabled(annotationMetadata)) {            return NO_IMPORTS;        } else {            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);            // 返回的就是需要注册到IoC容器中的对象对应的类型的全类路径名称的字符串数组            // ["com.bobo.pojo.User","com.bobo.pojo.Person", ....]            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());        }    }

我们清楚了该方法的作用就是要返回需要注册到IoC容器中的对象对应的类型的全类路径名称的字符串数组。那么我接下来分析的关键是返回的数据是哪来的?所以呢进入getAutoConfigurationEntry方法中。

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {        if (!this.isEnabled(annotationMetadata)) {            return EMPTY_ENTRY;        } else {            // 获取注解的属性信息            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);            // 获取候选配置信息 加载的是 当前项目的classpath目录下的 所有的 spring.factories 文件中的 key 为            // org.springframework.boot.autoconfigure.EnableAutoConfiguration 的信息            List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);            configurations = this.removeDuplicates(configurations);            Set exclusions = this.getExclusions(annotationMetadata, attributes);            this.checkExcludedClasses(configurations, exclusions);            configurations.removeAll(exclusions);            configurations = this.getConfigurationClassFilter().filter(configurations);            this.fireAutoConfigurationImportEvents(configurations, exclusions);            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);        }    }

先不进入代码,直接DEBUG调试到 候选配置信息这步。我们发现里面有很多个Java类。

然后进入getCandidateConfiguration方法中,我们可以发现加载的是 META-INF/spring.factories 文件中的配置信息

然后我们可以验证,进入到具体的META-INF目录下查看文件。

最后几个

在我们的Debug中还有一个配置文件(MyBatisAutoConfiguration)是哪来的呢?

深入源码也可以看到真正加载的文件

然后我们继续往下看源码

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {        if (!this.isEnabled(annotationMetadata)) {            return EMPTY_ENTRY;        } else {            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);            List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);            // 因为会加载多个 spring.factories 文件,那么就有可能存在同名的,            // removeDuplicates方法的作用是 移除同名的            configurations = this.removeDuplicates(configurations);            // 获取我们配置的 exclude 信息            // @SpringBootApplication(exclude = {RabbitAutoConfiguration.class})            // 显示的指定不要加载那个配置类            Set exclusions = this.getExclusions(annotationMetadata, attributes);            this.checkExcludedClasses(configurations, exclusions);            configurations.removeAll(exclusions);            // filter的作用是 过滤掉咱们不需要使用的配置类。            configurations = this.getConfigurationClassFilter().filter(configurations);            this.fireAutoConfigurationImportEvents(configurations, exclusions);            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);        }    }

先来看过滤的效果:

那么我们需要考虑这个过滤到底是怎么实现的呢?进入filter方法

我们可以看到有具体的匹配方法 match。里面有个关键的属性是 autoConfigurationMetadata,的本质是 加载的 META-INF/spring-autoconfigure-metadata.properties的文件中的内容。我们以 RedisAutoConfiguration为例:

通过上面的配置文件,我们发现RedisAutoConfiguration被注入到IoC中的条件是系统中要存在 org.springframework.data.redis.core.RedisOperations这个class文件。首先系统中不存在 RedisOperations 这个class文件。

过滤后,我们发现 RedisAutoConfiguration 就不存在了。

但是当我们在系统中显示的创建 RedisOperations Java类后,filter就不会过滤 RedisAutoConfiguration 配置文件了。

到这其实我们就已经给大家介绍完了SpringBoot的自动装配原理。

上一篇:

下一篇:

X 关闭

热门推荐

天天看热讯:申请低息贷款?小心诈骗犯罪!

2022-11-30   北京瀛台律师事务所

张小云《土楼》(北京)

2022-11-30   回来逛街了

北京今天阵风6至7级全天冰冻

2022-11-30   北京青年报官网

热议:1安等于多少毫安

2022-11-30   万能网

秘鲁寒流的成因

2022-11-30   万能网

每日视讯:国内首条市内高铁顺利完成联调联试

2022-11-30   中国产业经济信息网

金融动能助力“专精特新”企业跑出发展加速度

2022-11-30   中国产业经济信息网

潍柴:创两个“世界第一” 再破极限之限

2022-11-30   中国产业经济信息网

王鸥温柔的背后(wow gs)

2022-11-30   万能网

乎组词(乎读音及解释)

2022-11-29   万能网

致敬!寒风中的坚守

2022-11-29   北京昌平

快讯:C919大型客机获颁生产许可证

2022-11-29   北京商报官方账号

Output sensitive data with Terraform

2022-11-29   腾讯云

2022科技赋能乡村发展概念短片

2022-11-29   中国网·中国发展门户网

mt6(关于mt6的介绍)

2022-11-29   万能网

Kubernetes网络模型

2022-11-29   腾讯云

暴力石三街区

2022-11-29   万能网

偷录他人微信形成的证据法院能采纳吗?

2022-11-29   北京互联网法院

繁忙的蔬菜批发市场

2022-11-29   北京青年报官网

北京第四实验学校落地大兴2天天快播报

2022-11-29   北京青年报官网

兵器工业集团:“互联网+帮扶”助力乡村振兴

2022-11-29   中国产业经济信息网

李小秀:坚守店铺要为员工谋条路2天天观热点

2022-11-28   中国产业经济信息网

薛良平——北京华夏兰亭书画院院士

2022-11-28   兰亭书画艺术

Copyright   2015-2022 中国行业信息网版权所有  备案号:   联系邮箱:29 59 11 57 8@qq.com