Spring 从 3.0 之后,就逐步倾向于使用 java code config 方式来进行 bean 的配置,在 spring-boot 中,这种风格就更为明显了。
在查看 spring-boot 工程的时候,总想着探究一下 spring-boot 如何简单的声明一个 starter、Enable**,就能额外增加一个强大的功能,spring 是如何找到这些具体的实现 bean 的呢。
目前,大概有这么几种:
-
直接在工程中使用 @Configuration 注解
这个就是基本的 java code 方式,在 @SpringBootApplication 里面就包含了 @ComponentScan,因而会把工程中的 @Configuration 注解找到,并加以解释。
-
通过 @Enable×× 注解里面的 @Import 注解
我们在 Enable 某个功能时,实际上是通过 @Import 注解加载了另外的配置属性类。
例如: 如果要给工程加上定时任务的功能,只需要在某个配置文件上加上 @EnableScheduling,实际上它是引入了 SchedulingConfiguration.class,代码如下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import(SchedulingConfiguration.class) @Documented public @interface EnableScheduling { }
而 SchedulingConfiguration 就是一个标准的配置文件了,里面定义了 ScheduledAnnotationBeanPostProcessor 这个 bean。
@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(); } }
有了 ScheduledAnnotationBeanPostProcessor 这 bean,就会在 context 初始化时候,查找我们代码中的 @Scheduled,并把它们转换为定时任务。
-
通过 @EnableAutoConfiguration 注解
添加了这个异常强大的注解,spring-boot 会利用 AutoConfigurationImportSelector 搜索所有 jar 包中 META-INF 文件夹中 spring.factories,找到其中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的属性值,并把它作为需要解析的 @Configuration 文件。
例如:spring-cloud-commons 里面的 spring.factories
# AutoConfiguration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.client.CommonsClientAutoConfiguration,\ org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration,\ org.springframework.cloud.client.hypermedia.CloudHypermediaAutoConfiguration,\ org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration,\ org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\ org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\ org.springframework.cloud.commons.util.UtilAutoConfiguration,\ org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration
-
自己实现 ImportSelector
AutoConfigurationImportSelector 显然有时候还是不够用的,这时候就可以自己实现 ImportSelector,实现更灵活的加载功能。
public interface ImportSelector { String[] selectImports(AnnotationMetadata importingClassMetadata); }
ImportSelector 接口定义了一个 selectImports()方法,根据程序里面定义的注解信息,动态返回能被加载的类列表。这个实现就非常灵活了,简单的可以自己判断注解,直接返回类名;复杂的可以自己定制类似 AutoConfigurationImportSelector 的功能。
例如:我们看看 spring-cloud 的 @EnableCircuitBreaker
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(EnableCircuitBreakerImportSelector.class) public @interface EnableCircuitBreaker { }
发现,它引入了 EnableCircuitBreakerImportSelector,它本身并没有实现 ImportSelector,而是其父类 SpringFactoryImportSelector 实现的。
@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-××.jar 的 pring.factories 中找到如下配置:
org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\ org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration
这样就完成了通过 @EnableCircuitBreaker 的注解,最终加载到 Hystrix 的实现 ystrixCircuitBreakerConfiguration,实现了功能定义和具体实现的分离。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于