SpringBoot 加载Bean的几种方式

@EnableAutoConfiguration
@SpringBootApplication 已经有了 @EnableAutoConfiguration
对于不需要的bean,可以在使用方用@EnableAutoConfiguration的exclude属性进行排除。

加载Bean的几种方式

1、@Configuration

@ComponentScan扫描 bean

2、@Import

比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
}
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {

@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}

SchedulingConfiguration实际上就是一个配置文件,配置文件中有写好的bean

3、spring.factories 文件

需要在META-INF/spring.factories中加入如下

1
2
3
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
gt.maxzhao.boot.xxxBean,\
gt.maxzhao.boot.xxxBeanB

原理就是AutoConfigurationImportSelector搜索所有jar中的spring.factories文件,然后把org.springframework.boot.autoconfigure.EnableAutoConfiguration属性的值加载为@Configuration注解的文件。

4、自定义ImportSelector

下面来自其他人的见解(修改了部分表达与词句错误问题):
自定义ImportSelector可以类似于AutoConfigurationImportSelector的功能。
例如spring-cloud下的@EnableCircuitBreaker

1
2
3
4
5
6
7
8
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableCircuitBreakerImportSelector.class)
public @interface EnableCircuitBreaker {

}

发现,它引入了EnableCircuitBreakerImportSelector,它本身并没有实现ImportSelector,而是其父类SpringFactoryImportSelector实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public String[] selectImports(AnnotationMetadata metadata) {
if (!isEnabled()) {
return new String[0];
}
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(this.annotationClass.getName(), true));

Assert.notNull(attributes, "No " + getSimpleName() + " attributes found. Is "
+ metadata.getClassName() + " annotated with @" + getSimpleName() + "?");

// Find all possible auto configuration classes, filtering duplicates
// 调用SpringFactoriesLoader的loadFactoryNames去加载
List<String> factories = new ArrayList<>(new LinkedHashSet<>(SpringFactoriesLoader
.loadFactoryNames(this.annotationClass, this.beanClassLoader)));

//省略了错误判断和多于一个的log

return factories.toArray(new String[factories.size()]);
}

这里,我们看到,实际加载的代码是传入了this.annotationClass,那么对于EnableCircuitBreakerImportSelector来说,就是在spring.factories找它的全类名:
org.springframework.cloud.client.circuitbreaker.EnableCircuitBreakerImportSelector对应的值。
最终在spring-cloud-netflix-core-××.jarspring.factories中找到如下配置

1
2
org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\
org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration

这样就完成了通过@EnableCircuitBreaker的注解,最终加载到Hystrix的实现HystrixCircuitBreakerConfiguration,实现了功能定义和具体实现的分离。

本文地址: https://github.com/maxzhao-it/blog/post/63451/