SpringBoot自动配置

自动配置到底配了什么?

对于一个Spring项目,主要就是有两种配置:

  1. 一种是类似端口号、数据库地址、用户名密码等

  1. 一种是各种Bean,比如整合Mybatis需要配置的MapperFactoryBean,比如整合事务需要配置DataSourceTransactionManager

SpringBoot中的自动配置,更多的是配置各种Bean,因为对于第一种配置,SpringBoot也无法去配置,比如数据库地址、密码之类的,SpringBoot肯定是无法知道的,但是对于端口号这些配置,SpringBoot也是会提供一种默认值的,也相当于一种自动配置。

那SpringBoot是如何自动的帮助我们来配置这些Bean的呢?并且如果某些Bean程序员自己也配置了,那SpringBoot是如何进行选择的呢?

自动配置类

SpringBoot要自动帮我们配置Bean,那要支持的就多了:

  1. 比如Spring整合各种Servlet容器(Tomcat、Jetty)的Bean

  1. 比如Spring整合各种消息队列的Bean

  1. 等等

所以SpringBoot中肯定不能把所有的Bean都定义在一个配置类中,需要分门别类,这样就针对不同的场景定义了不同的自动配置类,比如:

  1. ServletWebServerFactoryAutoConfiguration:配置了Servlet Web场景中所需要的一些Bean

  1. TransactionAutoConfiguration:配置了事务场景中所需要的一些Bean

  1. AopAutoConfiguration:配置了AOP场景中所需要的一些Bean

  1. RabbitAutoConfiguration:配置了Rabbitmq场景中所需要的一些Bean

  1. 等等

使用这种结构后,SpringBoot就能让程序员更为方便的来控制某个Bean或某些Bean要不要生效,如果某个自动配置类不生效,那该配置类中所定义的Bean则都不会生效。

那SpringBoot中是如何控制某个自动配置类或某个Bean生不生效呢?

条件注解

SpringBoot中的条件注解有:

  1. ConditionalOnBean:是否存在某个某类或某个名字的Bean

  1. ConditionalOnMissingBean:是否缺失某个某类或某个名字的Bean

  1. ConditionalOnSingleCandidate:是否符合指定类型的Bean只有一个

  1. ConditionalOnClass:是否存在某个类

  1. ConditionalOnMissingClass:是否缺失某个类

  1. ConditionalOnExpression:指定的表达式返回的是true还是false

  1. ConditionalOnJava:判断Java版本

  1. ConditionalOnJndi:JNDI指定的资源是否存在

  1. ConditionalOnWebApplication:当前应用是一个Web应用

  1. ConditionalOnNotWebApplication:当前应用不是一个Web应用

  1. ConditionalOnProperty:Environment中是否存在某个属性

  1. ConditionalOnResource:指定的资源是否存在

  1. ConditionalOnWarDeployment:当前项目是不是以War包部署的方式运行

  1. ConditionalOnCloudPlatform:是不是在某个云平台上

当然我们也可以利用@Conditional来自定义条件注解。

条件注解是可以写在类上和方法上的,如果某个条件注解写在了自动配置类上,那该自动配置类会不会生效就要看当前条件能不能符合,或者条件注解写在某个@Bean修饰的方法上,那这个Bean生不生效就看当前条件符不符合。

具体原理是:

  1. Spring在解析某个自动配置类时,会先检查该自动配置类上是否有条件注解,如果有,则进一步判断该条件注解所指定的条件当前能不能满足,如果满足了则继续解析该配置类,如果不满足则不进行解析了,也就是配置类所定义的Bean都得不到解析,也就是相当于没有这些Bean了。

  1. 同理,Spring在解析某个@Bean的方法时,也会先判断方法上是否有条件注解,然后进行解析,如果不满足条件,则该Bean不会生效

我们可以发现,SpringBoot的自动配置,实际上就是SpringBoot的源码中预先写好了一些配置类,预先定义好了一些Bean,我们在用SpringBoot时,这些配置类就已经在我们项目的依赖中了,而这些自动配置类或自动配置Bean到底生不生效,就看具体所指定的条件了。

自动配置开启原理

我们在使用SpringBoot时,通常使用的是@SpringBootApplication这个注解,比如:


@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class);
    }
    
}

而这个注解的定义为:


@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@interfaceSpringBootApplication {
    // ...
}

可以发现这个注解上有另外三个注解:

  1. @SpringBootConfiguration

  1. @EnableAutoConfiguration

  1. @ComponentScan

所以我们可以认为@SpringBootApplication是一个三合一注解,也就是我们也可以这么用:


@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
publicclassMyApplication {
​
    publicstaticvoidmain(String[] args) {
        SpringApplication.run(MyApplication.class);
    }
​
}

如果我们这么用就能自己控制要不要用@EnableAutoConfiguration这个注解,如果用就表示开启自动配置,如果不用就表示不开启自动配置,那开启和不开启自动配置到底该怎么理解呢?

我们前面分析过,SpringBoot的自动配置就是SpringBoot帮助程序员配置一些Bean,从而让程序员在用SpringBoot时可以少去配置很多Bean,所以如果我们开启了自动配置,那最终Spring容器中就有SpringBoot帮我们配置的Bean,如果没有开启自动配置,那Spring容器中就没有这些Bean,就需要程序员去配置。

那我们来看看@EnableAutoConfiguration这个注解是如何工作的?先看源码定义:


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public@interfaceEnableAutoConfiguration {
}

其中非常核心的就是:

@Import(AutoConfigurationImportSelector.class)

而AutoConfigurationImportSelector实现了DeferredImportSelector这个接口,Spring容器在启动时,会在解析完其他所有程序员定义的配置类之后,来调用AutoConfigurationImportSelector中的selectImports方法,然后把该方法返回的类名对应的类作为配置类进行解析。

该方法会利用SpringFactoriesLoader找到所有的META-INF/spring.factories文件中key为EnableAutoConfiguration.class的value值,也就是众多自动配置类的类名。

拿到这些类名后会进行去重,去重的代码为:

newArrayList<>(newLinkedHashSet<>(list))

去重完之后,就会看是否存在某些自动配置类需要排除,我们可以通过@EnableAutoConfiguration注解的exclude属性,或者spring.autoconfigure.exclude配置来指定一些自动配置类的名字,然后把它们从自动配置类集合中排除掉。

然后会继续利用ConfigurationClassFilter对自动配置类进行进一步筛选,ConfigurationClassFilter会利用AutoConfigurationMetadata进行筛选,而AutoConfigurationMetadata对象对应的是"META-INF/spring-autoconfigure-metadata.properties"文件中的内容,这是一种加快SpringBoot启动速度的机制,默认是开启了的(不过要通过maven或gradle的方式引入springboot的依赖来使用才能看到效果,因为这个文件的内容是在SpringBoot源码工程编译的时候自动生成出来的,当然我们也可以手动创建这个文件,以及这个文件的内容),自动生成的这个文件内容样例:

内容格式为:自动配置类名.条件注解=条件

有了这个文件的内容,SpringBoot会在通过spring.facotries文件找到所有的自动配置类后,会把这个文件中的内容读出来,然后利用AutoConfigurationImportFilter对所有的自动配置类进行条件匹配,这里的条件判断,只会判断所需要的类是否存在,如果需要的类,或者需要的Bean对应的类,都不存在,那么肯定不符合条件了,对于像@ConditionalOnMissingBean这样的条件,在这一步是不会去判断的,最后条件匹配成功的自动配置类就会记录下来,并最终返回给Spring容器,继续进行其他条件的匹配。

所以通过这个机制,使得Spring并不需要解析所有的自动配置类,从而提高了效率。

自动配置类解析的大体流程为:

  1. 读取spring.factories中的所有自动配置类

  1. 看是否配置了需要排除的自动配置类,进行排除

  1. 然后利用spring-autoconfigure-metadata.properties文件来过滤掉一些自动配置类(条件中指定的类不存在的自动配置类)

  1. 解析过滤后自动配置类,判断自动配置类所有的条件注解,条件全部符合才会真正去解析自动配置类上的其他内容,比如@Bean(也会进行条件判断)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值