1. 概述
Spring Boot提供了一种自动配置的机制,它可以根据当前的Spring应用所加载的依赖项对项目进行自动的配置。比如当Spring Boot检测到项目仅依赖于H2数据库时,将自动启动H2数据库做为项目的默认数据库。
该自动配置机制的存在无疑使得应用开发起来更轻松、更简单。本文我们将围绕Spring Boot的自动配置展开阐述。
2. 版本信息
本文基于Spring Boot版本为2.3.3.RELEASE,采用java1.8。
3. 按是否存在特定的Class配置
按Class判断是否加载配置信息指:
- 当前Spring项目中存在指定的class时,加载配置信息,否则不加载配置信息。使用 @ConditionalOnClass 注解。
- 当前Spring项目中不存在指定的class时,加载配置信息,否则不加载配置信息。使用 @ConditionalOnMissingClass 注解。
3.1 @ConditionalOnClass 适用环境
@ConditionalOnClass接收的参数为Class<?>,也就是我们需要如下使用:@ConditionalOnClass<Student.class>。这同时意味着如果该语句能过顺利的通过编译器,首先要保证Student.class是存在的。
细想下会发现以下问题:只有当Student.class存在,@ConditionalOnClass<Student.class>才能通过编译,项目才能成功启动;而当Student.class不存在时,@ConditionalOnClass<Student.class>不能通过编译,项目同时无法启动。
再总结一下:使用@ConditionalOnClass<Student.class>注解时,只有存在Student.class时编译才能通过。也就是说使用了@ConditionalOnClass<Student.class>注解的项目,能启动的前提是存在Student.class。那么在@ConditionalOnClass<Student.class>注解下的方法或是类恒为ture,当然也就失去了使用此注解的意义。
其实该注解的用武之地并不是普通的Spring项目,而是基于Spring开发的第三方包。@ConditionalOnClass(Class<?>)中的Class往往是指其它的依赖中的类。假设开发一个发送短信的第三方jar包,开发的思想为:如果依赖于该包的项目同时依赖于阿里短信,则启动短信发送功能。
@ConditionalOnClass(AliSmsService.class) public class AliSmsAutoconfigration { // 配置相关的BEAN }
则其它依赖于上述第三方包的Spring应用如果依赖了阿里短信,则会启动该配置文件,从而达到了:如果该Spring应用依赖阿里短信,则启用短信发送功能,否则不启动短信功能的目的。
3.2 使用方法
Spring Boot在启动时,会扫描所依赖包的资源文件:resources/META-INF/spring.factories,并根据该文件中的相应值加载自动配置文件:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=club.codedemo.springbootcustomautoconfiguration.MyzAutoConfiguration
@Configuration @ConditionalOnClass(YunzhiService.class) public class MyzAutoConfiguration { @Bean SmsService smsService() { return new SmsServiceMyzImpl(); } }
上述代码使用@Configuration表示当前为配置类,使用@ConditionalOnClass(YunzhiService.class)表示当项目中存在于YunzhiService时,该配置文件下生效。YunzhiService依赖于com.mengyunzhi.core
<dependency> <groupId>com.mengyunzhi</groupId> <artifactId>core</artifactId> <version>2.1.7.0</version> </dependency>
此时其它依赖于本第三方包的应用如果同时依赖了com.mengyunzhi.core,则将自动配置MyzAutoConfiguration。接下来便可以在该项目中注入SmsService了。
3.3 ConditionalOnMissingClass
与@ConditionalOnClass不同,@ConditionalOnMissingClass注解收到的参数为String而非Class<?>,表示:当某个类不存在时....。
@Configuration @ConditionalOnMissingClass("com.mengyunzhi.core.service.YunzhiService") public class OnMissingClassAutoConfiguration { @Bean SmsService smsService() { return new SmsServiceErrorImpl(); } }
如果当前项目的定位为第三方包,则还应该将其加入到resources/META-INF/spring.factories中,以使依赖于该包的Spring项目能够启用该自动配置文件:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=club.codedemo.springbootcustomautoconfiguration.MyzAutoConfiguration,club.codedemo.springbootcustomautoconfiguration.OnMissingClassAutoConfiguration
注意:两个文件以,
相隔。
3.4 加载顺序
当存在多个自动配置文件时,还可以使用@AutoConfigureOrder(int 权重)来指定其加载的顺序,比如将MyzAutoConfiguration的加载顺序设置为最前(优先级最高):
@ConditionalOnClass(YunzhiService.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) public class MyzAutoConfiguration {
其中权重的范围为:-2147483648 至 2147483647,值越小则权重越高,加载的顺序越靠前。
注意:必须将@AutoConfigureOrder注解的类同步添加到resources/META-INF/spring.factories中时,该注解才会生效。
4. 按是否存在特定的Bean配置
可以使用@ConditionalOnBean、@ConditionalOnMissingBean来根据某个Bean是否存在来选择加载配置项。
@Configuration @AutoConfigureOrder(1) public class BeanConditionalAutoConfiguration { /** * 当前容器中存在SmsService时生效 * @return 邮件服务 */ @Bean @ConditionalOnBean(SmsService.class) public EmailService emailService() { return new EmailServiceImpl(); } /** * 当前容器中 不 存在SmsService时生效 * @return 邮件服务 */ @Bean @ConditionalOnMissingBean(SmsService.class) public EmailService customEmailService() { return (address, title, description) -> { throw new RuntimeException("未找到默认的emailService实现"); }; } }
上述代码的作用是:按SmsService Bean是否存在为项目装配不同的EmailService实现。
5. 按配置项自动配置
在Spring中可以使用@ConditionalOnProperty注解来关联相应的配置文件,比如在classPath中存在ding.properties配置文件,则可以使用@ConditionalOnProperty注解来完成配置文件与类之间的关联。
@PropertySource("classpath:ding.properties") @Configuration public class ConditionalOnPropertyAutoConfiguration {
@PropertySource关联ding.properties,@Configuration以表明此类为配置类。
则可以根据ding.properties中的属性值来决定自动装配的情况:
@PropertySource("classpath:ding.properties") @Configuration public class ConditionalOnPropertyAutoConfiguration { /** * 当url 值为alibaba 时,装配此bean * bean名起为dingService * @return */ @Bean(name = "dingService") @ConditionalOnProperty( name = "url", havingValue = "alibaba") DingService dingService() { return message -> { // 处理钉钉消息 }; } /** * 当URL值为codedemo时,装配此bean * bean名称为ding1Service * @return */ @Bean(name = "ding1Service") @ConditionalOnProperty( name = "url", havingValue = "codedemo") DingService dingService1() { return message -> { // 处理钉钉消息 }; } }
6. 按配置文件存在与否自动配置
除了可以根据配置文件中的属性来进行自动配置外,还可以根据是否存在某个配置文件来配置。
比如当存在codedemo.properties时,装配ding3Service:
@Configuration @ConditionalOnResource(resources = "classpath:codedemo.properties") public class ConditionalOnResourceAutoConfiguration { @Bean("ding2Service") DingService dingService() { return (message) -> { }; } }
当存在alibaba.properties时,装配ding2Service:
@Configuration @ConditionalOnResource(resources = "classpath:alibaba.properties") public class ConditionalOnResourceAutoConfiguration1 { @Bean("ding3Service") DingService dingService() { return (message) -> { }; } }
7. 自定义自动配置条件
当Spring提供的自动配置条件注解不能满足我们的要求时,还可以自定义自动配置条件。
自定自动配置条件仅需要继承SpringBootCondition抽象类并重写其getMatchOutcome() 方法:
public class CustomerConditionTrue extends SpringBootCondition { @Override public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { // 第一个参数返回true,表示该注解下的配置生效。生产条件应该根据当前情景动态计算出true或false return new ConditionOutcome(true, "message"); } }
在方法上使用该注解:
@Bean("ding4Service") @Conditional(CustomerConditionTrue.class) DingService ding4Service() { return message -> { }; }
8. 根据是否为WEB应用进行配置
还可以通过 @ConditionalOnWebApplication 以及 @ConditionalOnNotWebApplication注解以达到:当前应用为web应用时自动配置某些bean,以及当前应用非web应用时,自动配置某些bean的目的。
9. 禁用自动配置类
有些时候我们并不希望某些自动配置类在本项目中生效,则可以将该类加入到@EnableAutoConfiguration注解的exclude属性中:
//@SpringBootApplication @SpringBootConfiguration @EnableAutoConfiguration(exclude = DisableAutoConfiguration.class) @ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public class SpringBootCustomAutoConfigurationApplication {
注意:被排除的类必须存在于resources/META-INF/spring.factories文件的org.springframework.boot.autoconfigure.EnableAutoConfiguration属性中。
由于某些原因@SpringBootApplication不能够与@EnableAutoConfiguration同时使用,导致上述代码看起来比较臃肿。所以在条件允许的情况下,推荐使用配置项目的spring.autoconfigure.exclude来替换上述使用注解的方案:
spring.autoconfigure.exclude=club.codedemo.springbootcustomautoconfiguration.DisableAutoConfiguration
10. 总结
本文阐述了Spring提供的几种自动配置方法。合理的规划自动配置可以提升项目的健壮性与适用性,而Spring自动配置则是一把利器,使用Spring自动配置往往能起到事半功倍的效果。