@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
但是从EnableAutoCOnfiguration上面的import注解来看,这里面并不是引入另外一个Configuration。而是一个ImportSelector。这个是什么东西呢?
AutoConfigurationImportSelector是什么?
Enable注解不仅仅可以像前面演示的案例一样很简单的实现多个Configuration的整合,还可以实现一些复杂的场景,比如可以根据上下文来激活不同类型的bean,@Import注解可以配置三种不同的class
-
第一种就是前面演示过的,基于普通bean或者带有@Configuration的bean进行诸如
-
实现ImportSelector接口进行动态注入
实现ImportBeanDefinitionRegistrar接口进行动态注入
CacheService
public class CacheService {
}
LoggerService
public class LoggerService {
}
EnableDefineService
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited --允许被继承
@Import({GpDefineImportSelector.class})
public @interface EnableDefineService {
String\[\] packages() default "";
}
GpDefineImportSelector
public class GpDefineImportSelector implements ImportSelector {
@Override
public String\[\] selectImports(AnnotationMetadata annotationMetadata) {
//获得指定注解的详细信息。我们可以根据注解中配置的属性来返回不同的class,
//从而可以达到动态开启不同功能的目的
annotationMetadata.getAllAnnotationAttributes(EnableDefineService.class.getName(),true)
.forEach((k,v) -> {
log.info(annotationMetadata.getClassName());
log.info("k:{},v:{}",k,String.valueOf(v));
});
return new String\[\]{CacheService.class.getName()};
}
}
EnableDemoTest
@SpringBootApplication
@EnableDefineService(name = “gupao”,value = “gupao”)
public class EnableDemoTest {
public static void main(String\[\] args) {
ConfigurableApplicationContext ca=SpringApplication.run(EnableDemoTest.class,args);
System.out.println(ca.getBean(CacheService.class));
System.out.println(ca.getBean(LoggerService.class));
}
}
了解了selector的基本原理之后,后续再去分析AutoConfigurationImportSelector的原理就很简单了,它本质上也是对于bean的动态加载。
@EnableAutoConfiguration注解的实现原理
了解了ImportSelector和ImportBeanDefinitionRegistrar后,对于EnableAutoConfiguration的理解就容易一些了
它会通过import导入第三方提供的bean的配置类:AutoConfigurationImportSelector
@Import(AutoConfigurationImportSelector.class)
从名字来看,可以猜到它是基于ImportSelector来实现基于动态bean的加载功能。之前我们讲过Springboot @Enable*注解的工作原理ImportSelector接口selectImports返回的数组(类的全类名)都会被纳入到spring容器中。
那么可以猜想到这里的实现原理也一定是一样的,定位到AutoConfigurationImportSelector这个类中的selectImports方法
selectImports
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO\_IMPORTS;
}
// 从配置文件(spring-autoconfigure-metadata.properties)中加载 AutoConfigurationMetadata
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 获取所有候选配置类EnableAutoConfiguration
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY\_ENTRY;
}
//获取元注解中的属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//使用SpringFactoriesLoader 加载classpath路径下META-INF\spring.factories中,
//key= org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的value
List configurations = getCandidateConfigurations(annotationMetadata,
attributes);
//去重
configurations = removeDuplicates(configurations);
//应用exclusion属性
Set exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//过滤,检查候选配置类上的注解@ConditionalOnClass,如果要求的类不存在,则这个候选类会被过滤不被加载
configurations = filter(configurations, autoConfigurationMetadata);
//广播事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
本质上来说,其实EnableAutoConfiguration会帮助springboot应用把所有符合@Configuration配置都加载到当前SpringBoot创建的IoC容器,而这里面借助了Spring框架提供的一个工具类SpringFactoriesLoader的支持。以及用到了Spring提供的条件注解@Conditional,选择性的针对需要加载的bean进行条件过滤
SpringFactoriesLoader
为了给大家补一下基础,我在这里简单分析一下SpringFactoriesLoader这个工具类的使用。它其实和java中的SPI机制的原理是一样的,不过它比SPI更好的点在于不会一次性加载所有的类,而是根据key进行加载。
首先,SpringFactoriesLoader的作用是从classpath/META-INF/spring.factories文件中,根据key来加载对应的类到spring IoC容器中。接下来带大家实践一下
创建外部项目jar
org.springframework
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中…(img-s3gv6pou-1711178748727)]
[外链图片转存中…(img-3k9ltEhm-1711178748727)]
[外链图片转存中…(img-wq1vYyrg-1711178748728)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-kv6NX4nQ-1711178748728)]