springboot filter_Spring Boot 自动装配是怎么一回事?

记得关注我呀~

每天进步一点点,沉淀技术分享知识。

面试季「 Spring Boot」自动装配

SpringBoot 目前已经成为了Java程序员必备的技能项了,不论你是应届毕业生还是跳槽程序员,熟练掌握SpringBoot是必不可或缺的技能。最近自己打算利用SpringBoot的自动配置原理自己来实现一个类库,借此机会也为大家分享一下我用到了一些技术。临近秋招季,希望本文能秋招的同学带来一些帮助。

来吧!灵魂三问:

  • 什么是自动装配?
  • 自动装配为我们装配了什么?
  • 怎么实现自动装配?

同学你会吗?慌了吗?怀疑人生了吗?稳住!下面请跟着我来一探究竟,寻求答案吧。

SpringBoot自动配置

大家刚开始学习SpringBoot的时候就知道,我们只需要在:application.properties或application.yml,进行少量的配置便可以实现一个web项目的启动和使用,SpringBoot自动帮我们装配了很多Bean定义最后通过Bean工厂生成Bean缓存到容器中供你使用。 下面给大家提供除了一些常用的配置更多配置项目,大家可以参考官方文档。

SpringBoot配置项[1]


下面咱们就进入正文,看看SpringBoot是怎么实现自动装配的。

SpringBoot启动类

@SpringBootApplicationpublic class App {    public static void main(String[] args) {        SpringApplication.run(App.class, args);    }}

这一个小小的注解帮我们启动了SpringBoot,其背后默认帮我们配置了很多自动配置类。其中最重要是 @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) })public @interface SpringBootApplication {

解说一下三个注解:

  • @SpringBootConfiguration : Spring Boot的配置类,标注在某个类上,表示这是一个Spring Boot的配置类(等于xml方式下.xml文件)。
  • @EnableAutoConfiguration: 开启自动配置类,SpringBoot的精华所在。
  • @ComponentScan:包扫描,等同于xml下开启并设置包扫描路径。

@EnableAutoConfiguration

@EnableAutoConfiguration:告诉SpringBoot开启自动配置功能,这样自动配置才能生效。

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration {
  • @AutoConfigurationPackage:自动配置包
  • @Import: 导入自动配置的组件 @Import:通常用于有时没有把某个类注入到IOC容器中,但在运用的时候需要获取该类对应的bean,此时就需要用到@Import注解。加入IOC容器的方式有很多种,当然@Bean注解也可以,但是@Import注解快速导入的方式更加便捷。

@AutoConfigurationPackage

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {        @Override        public void registerBeanDefinitions(AnnotationMetadata metadata,                BeanDefinitionRegistry registry) {            register(registry, new PackageImport(metadata).getPackageName());        }

它其实是注册了一个Bean的定义,返回了当前主程序类的同级以及子级的包组件。如果你把类写到了APP类的上级时SpringBoot便不会帮你加载报出如404的问题,这也就是为什么,我们要把App放在项目的最高级中。

  • SpringApplication.run(App.class, args);为什么要传入主启动类? 其一方面就是要根据该类去解析他所在包,并实现对同级和下级类的扫描。
d7884f41325bd3dba4da412f1ead5d92.png

@Import

@Import(AutoConfigurationImportSelector.class)

到这就到自动装配的核心了,离成功不远了,咱们直接看重点,AutoConfigurationImportSelector通过继承 DeferredImportSelector,ImportSelector重写selectImports方法。

该方法奠定了SpringBoot自动批量装配的核心功能逻辑。

 @Override public String[] selectImports(AnnotationMetadata annotationMetadata) {  if (!isEnabled(annotationMetadata)) {   return NO_IMPORTS;  }  AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader    .loadMetadata(this.beanClassLoader);  AnnotationAttributes attributes = getAttributes(annotationMetadata);  List configurations = getCandidateConfigurations(annotationMetadata,    attributes);  configurations = removeDuplicates(configurations);  Set exclusions = getExclusions(annotationMetadata, attributes);  checkExcludedClasses(configurations, exclusions);  configurations.removeAll(exclusions);  configurations = filter(configurations, autoConfigurationMetadata);  fireAutoConfigurationImportEvents(configurations, exclusions);  return StringUtils.toStringArray(configurations); }

可以看到getCandidateConfigurations()方法,它其实是去加载 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" 外部文件。这个外部文件里面,默认有很多自动配置的类,这些类的定义信息将会被SpringBoot批量的加载到Bean定义Map中等待被创建实例。如下:

d32d4d8e81dcf3a12efd09111c31a807.png

AutoConfigurationMetadata

Provides access to meta-data written by the auto-configure annotation processor.

官方注释翻译:提供对自动配置注解写入的元数据的访问能力。

a6ecdc27573673488ea42db497e49726.png

这个Path下都有什么东西呢?我大概给大家罗列几个:

#Thu Jun 14 11:34:18 UTC 2018org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfigurationorg.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,org.springframework.data.cassandra.core.ReactiveCassandraTemplate,reactor.core.publisher.Fluxorg.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepositoryorg.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfigurationorg.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration.ConditionalOnClass=com.couchbase.client.java.CouchbaseBucket,com.couchbase.client.java.Clusterorg.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration.ConditionalOnClass=org.springframework.amqp.rabbit.core.RabbitTemplate,com.rabbitmq.client.Channel
3ef9156f3c4e60261d625b1d041997e8.png

这些东西不是本文的重点,就不细说了。简单来讲 autoConfigurationMetadata 这个参数会在后面的filter方法使用到,而filter方法处理是你的配置类中某些xxxconditionOnClass的,这些在配置类中使用的Class的信息应该就是上述方法提供。

获取候选配置项

见文知意:getCandidateConfigurations 获取候选配置项,这个候选配置项就是spring.factories下的配置项。

List configurations = getCandidateConfigurations(annotationMetadata,attributes);

下面我们来看看这个方法的细节和作用。翻译下面这句话(返回自动配置类名称)

2d4f18a7a4158e5e0668e2fa193a4253.png

在loadSpringFactorise方法中边是通过循环如读取spring.factories下的配置信息并缓存到Map中。

31d536895631f7d4833ce5a5b01c96fe.png
a40a66f922f13fa612fa14741ea6f9fd.png
  • 第一个箭头首先一次性去加载一些自动配置类,减少重复加载提高效率。
  • 第二个箭头则是去该路径下找spring.factories并读取其中设置的自动配置类的信息
  • 第三个箭头则是将读取的结果写入Map缓存中

其他方法

在读取完自动配置信息后,会经历下面几个方法,主要是对其进行去重处理。

  • 防止自己写的自动装配类和Springboot装配的类出现重复,进行去重。
configurations = removeDuplicates(configurations);
  • 读取@SpringBootApplication中 exclude设置的属性,可以排除自动装填配类。
Set exclusions = getExclusions(annotationMetadata, attributes);
36d98120be25624b5b8dba9c994a91c7.png
  • 处理自动配置类中如@ConditionalOnClass等注解,如果不满足则该配置不生效等情况。
  checkExcludedClasses(configurations, exclusions);  configurations.removeAll(exclusions);  configurations = filter(configurations, autoConfigurationMetadata);
acebfd764cd986d44be04876f760cc15.png

触发自动配置导入事件

a99f66e9c4908a929f8e04970aa01420.png

归纳总结

当我们使用@EnableAutoConfiguration注解激活自动装配时,实质对应着很多XXXAutoConfiguration类在执行装配工作,这些XXXAutoConfiguration类是在spring-boot-autoconfigure jar中的META-INF/spring.factories文件中配置好的,@EnableAutoConfiguration通过SpringFactoriesLoader机制创建XXXAutoConfiguration这些bean。XXXAutoConfiguration的bean会依次执行并判断是否需要创建对应的bean注入到Spring容器中。

在每个XXXAutoConfiguration类中,都会利用多种类型的条件注解@ConditionOnXXX对当前的应用环境做判断,如应用程序是否为Web应用、classpath路径上是否包含对应的类、Spring容器中是否已经包含了对应类型的bean。如果判断条件都成立,XXXAutoConfiguration就会认为需要向Spring容器中注入这个bean,否则就忽略。


更多精彩好文尽在:Java编程之道

欢迎各位好友前去关注!

参考资料

[1]SpringBoot配置项: https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#common-application-properties

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值