Spring Boot 自动配置是 Spring Boot 的一个非常重要、也是非常好用的特性。
Spring Boot 自动配置功能可以根据不同条件自动决定 Spring 配置中,那些 Bean 该创建,哪些 Bean 不该创建。举个例子:
在 classpath 中是否存在 Spring 的 JdbcTemplate?如果在 classpath 中存在 JdbcTemplate 类,并且 DataSource 也存在,那么久自动配置一个 JdbcTemplate Bean。
在 classpath 中是否存在 Thymeleaf 类?如果存在,则自动配置 Thymeleaf 的模板解析器、视图解析器、模板引擎等
那个这个是怎么实现的呢?原因就在于它利用了 Spring 的条件化配置,条件化配置允许配置存在于应用中。但是在满足某些特定条件前会忽略这些配置;只有满足某个条件时,配置才会有效。
要实现条件化配置我们要用到 @Conditional 注解(位于 org.springframework.context.annotation 包,注意:自 spring 4.0 开始才支持),源码如下:
package org.springframework.context.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { /** * All {@link Condition} classes that must {@linkplain Condition#matches match} * in order for the component to be registered. */ Class<? extends Condition>[] value(); }
只有当所有在 @Conditional 注解中指定条件都匹配时,组件才有资格注册。@Conditional 注解的条件是可以在要注册 Bean 定义之前以编程方式确定的任何状态。
@Conditional 注解可以通过以下任何一种方式使用:
作为直接或间接用 @Component 注解的任何类的类级别注释,包括 @Configuration 修饰的类;
作为元注释,用于组合自定义注解;
作为任何 @Bean 方法上的方法级注释;
如果 @Configuration 注解修饰的类上标记有 @Conditional 注解,则与该类关联的所有 @Bean 方法,@Import 注解和@ComponentScan 注解将受此条件限制。
注意:
@Conditional 注解不支持继承特性,不会考虑超类或重写方法中的任何条件。为了强制执行这些语义,@Conditional 本身未使用 @Inherited 注解声明;
此外,任何使用 @Conditional 进行元注释的自定义组合注解都不得包含 @Inherited 注解。
Spring Boot 在 @Conditional 注解的基础上提供了更多的便捷注解,见 org.springframework.boot.autoconfigure.condition 包,常见便捷注解如下:
ConditionalOnBean:@Conditional 仅在满足所有指定要求的bean已包含在 BeanFactory 中时才匹配。
ConditionalOnClass:@Conditional 仅当指定的类在类路径上时才匹配。
ConditionalOnCloudPlatform:当指定的云平台处于活动状态时,@Conditional 匹配。
ConditionalOnExpression:取决于 SpEL 表达式值的条件元素的配置注释。
ConditionalOnJava:根据应用程序运行的 JVM 版本进行匹配的 @Conditional。
ConditionalOnJndi:根据 JNDI InitialContext 的可用性和查找特定位置的能力进行匹配的 @Conditional。
ConditionalOnMissingBean:@Conditional 仅在BeanFactory中已不包含满足指定要求的bean时匹配。
ConditionalOnMissingClass:@Conditional 仅在指定的类不在类路径上时才匹配。
ConditionalOnNotWebApplication:@Conditional 仅在应用程序上下文不是Web应用程序上下文时才匹配。
ConditionalOnProperty:@Conditional 检查指定的属性是否具有特定值。
ConditionalOnResource:@Conditional 仅在指定资源位于类路径上时才匹配。
ConditionalOnSingleCandidate:@Conditional 仅在 BeanFactory 中已经包含指定类的 bean 并且可以确定单个候选对象时才匹配。
ConditionalOnWarDeployment:当应用程序为传统 WAR 部署时匹配的 @Conditional。
ConditionalOnWebApplication:当应用程序是Web应用程序时匹配的@Conditional。
我们在 Spring Cloud 源码中,随处可见 Spring Boot 自动配置的条件注解的身影,例如:
@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties @ConditionalOnClass(EurekaClientConfig.class) @ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true) @ConditionalOnDiscoveryEnabled @ConditionalOnBlockingDiscoveryEnabled public class EurekaDiscoveryClientConfiguration { @Bean @ConditionalOnMissingBean public EurekaDiscoveryClient discoveryClient(EurekaClient client, EurekaClientConfig clientConfig) { return new EurekaDiscoveryClient(client, clientConfig); } // ... }
上面代码中的 @ConditionalOnClass、@ConditionalOnProperty、@ConditionalOnMissingBean 等注解的含义和详细用法将在后续章节进行详细介绍。