Spring Boot中默认会扫描的启动类对应的子包下面的类,但是项目引入的其他包下面的类要加入到IOC中必须要有所说明,以下说到的自动配置就是干这个活的,springboot就会把配置中的类加载到ioc容器中。
(1)自动配置注册文件
从Spring boot2.7开始自动配置注册有了一个比较大的调整,之前都是写在下面 文件中的:
META-INF/spring.factories
格式为: org.springframework.boot.autoconfigure.EnableAutoConfiguration=[XXXConfig,YYYConfig]
自Spring Boot 2.7 起改名了:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
格式也变了,直接每一行是一个自动配置类,比如spring-cloud-netflix-eureka-client-4.0.3.jar 中的该文件内容为:
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration
org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration
org.springframework.cloud.netflix.eureka.reactive.EurekaReactiveDiscoveryClientConfiguration
org.springframework.cloud.netflix.eureka.loadbalancer.LoadBalancerEurekaAutoConfiguration
(2)新注解(@AutoConfiguration)
新增了一个自动配置注解 @AutoConfiguration,用来代替之前的 @Configuration,用于标识新自动配置注册文件中的顶级自动配置类,由 @AutoConfiguration 注解嵌套、导入进来的其他配置类可以继续使用 @Configuration 注解。
另外,为方便起见,@AutoConfiguration 注解还支持 after, afterNames, before 和 beforeNames 属性进行自动配置排序,用于代替之前的 @AutoConfigureAfter 和 @AutoConfigureBefore 注解。
@AutoConfiguration(
before = { CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class },
after = MetricsAutoConfiguration.class)
@ConditionalOnBean(Clock.class)
@ConditionalOnClass(ElasticMeterRegistry.class)
@ConditionalOnEnabledMetricsExport("elastic")
@EnableConfigurationProperties(ElasticProperties.class)
public class ElasticMetricsExportAutoConfiguration {
}
上述@AutoConfiguration表示ElasticMetricsExportAutoConfiguration.class类的bean应该在如下类MetricsAutoConfigurationa.class的bean被创建之后才被创建,并且需要在类CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class的bean被创建之前,清楚的表明了bean被创建的先后顺序。
上述@ConditionalOnBean(Clock.class)表示存在类 Clock.class的bean才创建ElasticMetricsExportAutoConfiguration.class的bean。
上述@ConditionalOnClass(ElasticMeterRegistry.class)表示classpath中存在类ElasticMeterRegistry.class才创建ElasticMetricsExportAutoConfiguration.class的bean。
上述@ConditionalOnEnabledMetricsExport("elastic") 表示需要在引入了 elastic 并且 actuator 暴露了 elastic 端口的情况下才创建ElasticMetricsExportAutoConfiguration.class的bean。
上述@EnableConfigurationProperties(ElasticProperties.class)表示让@ConfigurationProperties 注解的类ElasticProperties.class被加载生效。
SpringBoot中常用的条件注解:
注解定义都在spring-boot-autoconfigure-x.y.z.jar的包org.springframework.boot.autoconfigure.condition中:
@ConditionalOnBean(CqlSession.class) 配置的CqlSession.class类的bean存在时,才会创建这个bean;
@ConditionalOnMissingBean 配置的bean不存在时,才会创建这个bean;
@ConditionalOnClass(RabbitTemplate.class) Classpath中存在配置的类RabbitTemplate.class,才会创建这个bean;
@ConditionalOnMissingClasses Classpath中不存在配置的类,才会创建这个bean;
@ConditionalOnJava JDK版本在范围以内,才会创建这个bean;
@ConditionalOnExpression 指定的SpEL表达式结果为true,才会创建这个bean;
@ConditionalOnWebApplication 是一个WEB应用程序,才会创建这个bean;
@ConditionalOnNotWebApplication 不是一个WEB应用程序,才会创建这个bean;
@ConditionalOnCloudPlatform 仅当我们在某个云平台上运行时才加载bean:
@ConditionalOnJndi("java:comp/env/ejb/myEJB") 仅当通过JNDI提供某个资源时才加载bean:
@ConditionalOnProperty(prefix = "management.auditevents", name = "enabled") 仅在存在环境属性(management.auditevents.enabled)且配置的值不等于false才创建bean。
@ConditionalOnProperty(prefix = "management.health.readinessstate", name = "enabled", havingValue = "true")仅在存在环境属性(management.health.readinessstate.enabled)且配置的值为true才创建bean。
@ConditionalOnResource(resources = "${spring.info.build.location:classpath:META-INF/build-info.properties}") 仅当指定的资源文件出现在classpath中才创建bean
关于@AutoConfigureBefore和@AutoConfigureAfter的用法
以下为案例:
@AutoConfigureBefore(Test1.class)
@AutoConfigureAfter(Test2.class)
public class TestConfig {
}
表示TestConfig.class应该在Test2.class加载后即自动加载,并且要在类Test1.class加载之前。
条件注解的起源:
条件注解发源自注解Conditional,该注解传入的参数是Condition。
注解:org.springframework.context.annotation.Conditional.class
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Classextends Condition>[] value();
}
接口org.springframework.context.annotation.Condition.class,是一个函数式接口:
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
以条件注解@ConditionalOnBean为例来看具体的实现:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
Class[] value() default {};
String[] type() default {};
Classextends Annotation>[] annotation() default {};
String[] name() default {};
SearchStrategy search() default SearchStrategy.ALL;
Class[] parameterizedContainer() default {};
}
从上述代码可以看到,真正来判定Bean是否存在的条件是由类OnBeanCondition.class来完成的。
参考如下,OnBeanCondition.java 是来实现ConfigurationCondition.java接口的,ConfigurationCondition.java是继承自Condition.java 这个函数式接口,真正的判定条件匹配是在boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);方法中来实现的。
@Order(Ordered.LOWEST_PRECEDENCE)
class OnBeanCondition extends FilteringSpringBootCondition implements ConfigurationCondition {
}
Spring和Spring Boot的注解水很深,用法出神入化了。
(3)配置提示功能
如下3个文件都是用于配置提示:
spring-configuration-metadata.json
additional-spring-configuration-metadata.json
spring-autoconfigure-metadata.properties
其中spring-configuration-metadata.json和spring-autoconfigure-metadata.properties是插件生成的,additional-spring-configuration-metadata.json一般没额外的补充信息需求的话也不用写。
自定义配置提示只需要在项目中引入如下依赖即可:
dependency>
groupId>org.springframework.bootgroupId>
artifactId>spring-boot-autoconfigureartifactId>
dependency>
dependency>
groupId>org.springframework.bootgroupId>
artifactId>spring-boot-autoconfigure-processorartifactId>
dependency>
dependency>
groupId>org.springframework.bootgroupId>
artifactId>spring-boot-configuration-processorartifactId>
optional>trueoptional>
dependency>
(4)spring-boot-starter-xxx.jar为什么都是空项目?
以spring-cloud-starter-loadbalancer-4.0.4.jar为例,本身是一个空项目,只是依赖项目spring-cloud-loadbalancer-4.0.4.jar。
在spring-cloud-loadbalancer-4.0.4.jar中的关键自动配置部分:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,
给出了自动配置的信息。
之所以拆分为一个空工程和一个配置工程的原因,只能先猜测一下。
(1)有可能是空的spring-cloud-starter-xxx工程可以有比较灵活的依赖,不一定要和spring-cloud-xxx完全绑死,除了依赖spring-cloud-xxx外还可以依赖其他工程。
但是spring-boot-starter-3.1.4.jar和spring-boot-start-cache-3.1.4.jar和spring-boot-start-freemarker-3.1.4.jar 这些都是空工程,连依赖都没有,根本体现不出来上面的意义啊。
(2)也可能是有名字好理解一点
如果是这个理由,直接把spring-cloud-xxx命名为spring-cloud-starter-xxx,用一个工程不是更好吗,感觉牵强得很。