在前面的分析中,SpringFramework一直致力于解决一个问题,即如何让bean的管理更简单,如何让开发者尽可能少的关注对一些尽可能基础优化的bean的配置,从而实现自动装配。所以,所谓的自动组装,其实就是如何把bean自动加载到Ioc容器中。其实在Spring3.x版本中Enablemodule-driven注解的出现就已经有了一定的自动装配的雏形,但真正实现这种机制的是spring4中conditional条件注解的出现。x版本。ok,我们来看看springboot的自动组装是怎么回事。自动装配演示org.springframework.bootspring-boot-starter-data-redisspring:redis:host:127.0.0.1port:6379@AutowiredprivateRedisTemplateredisTemplate;按以下顺序添加启动器,然后添加配置,可以使用RedisTemplate吗?大家有没有想过一个问题,为什么RedisTemplate可以直接注入呢?什么时候加入Ioc容器的?这是自动装配。自动组装可以让classpath下依赖包相关的bean自动加载到SpringIoc容器中。怎么做?深入解析EnableAutoConfigurationEnableAutoConfiguration的主要作用是帮助springboot应用将所有符合条件的@Configuration配置加载到SpringBoot当前创建使用的IoC容器中。回到EnableAutoConfiguration的注解,我们发现它的import就像@Import(AutoConfigurationImportSelector.class)public@interfaceEnableAutoConfiguration{但是从EnableAutoConfiguration上面的import注解来看,它并没有引入另外一个Configuration。相反,它是一个ImportSelector。这是什么?什么是自动配置导入选择器?Enable注解不仅可以像前面的演示案例那样简单的实现多个Configurations的集成,还可以实现一些复杂的场景,比如根据上下文激活不同类型的bean。@Import注解可以配置三个不同的类。第一种就是前面演示过的,基于普通bean或者带@Configuration的bean,比如实现ImportSelector接口进行动态注入。实现用于动态注入的ImportBeanDefinitionRegistrar接口。CacheServicepublicclassCacheService{}LoggerServicepublicclassLoggerService{}EnableDefineService@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited--允许继承@Import({GpDefineImportSelector.class})public@interfaceEnableDefineService{String[]packages()default"";}GpDefineImportSelectorpublicclassGpDefineImportSelectorimplements@verportSelector{publicString[]selectImports(AnnotationMetadataannotationMetadata){//获取指定注解的详细信息。我们可以根据注解中配置的属性返回不同的类,//从而达到动态启用不同功能的目的annotationMetadata.getAllAnnotationAttributes(EnableDefineService.class.getName(),true).forEach((k,v)->{log.info(annotationMetadata.getClassName());log.info("k:{},v:{}",k,String.valueOf(v));});返回新的String[]{CacheService.class。getName()};}}EnableDemoTest@SpringBootApplication@EnableDefineService(name="gupao",value="gupao")publicclassEnableDemoTest{publicstaticvoidmain(String[]args){ConfigurableApplicationContextca=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方法}//从配置文件(spring-autoconfigure-metadata.properties)中加载AutoConfigurationMetadataAutoConfigurationMetadataautoConfigurationMetadata=AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);//获取所有可选配置类EnableAutoConfigurationAutoConfigurationEntryautoConfigurationEntry=getAutoConfigurationMetadata(annotation)返回StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}getAutoConfigurationEntryprotectedAutoConfigurationEntrygetAutoConfigurationEntry(AutoConfigurationMetadataautoConfigurationMetadata,AnnotationMetadataannotationMetadata){如果(!isEnabled(annotationMetadata)){返回EMPTY_ENTRY;}//获取meta注解中的属性AnnotationAttributesattributes=getAttributes(annotationMetadata);//使用SpringFactoriesLoader加载classpath路径下的META-INF\spring.factories,//key=org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的valueListconfigurations=getCandidateConfigurations(annotationMetadata,attributes);//去除重复配置=removeDuplicates(configurations);//应用排除属性Setexclusions=getExclusions(annotationMetadata,attributes);checkExcludedClasses(配置,排除);configurations.removeAll(exclusions);//过滤,检查候选配置类上的注解@ConditionalOnClass,如果需要的类不存在,则过滤候选类不加载configurations=filter(configurations,autoConfigurationMetadata);//广播事件fireAutoConfigurationImportEvents(configurations,exclusions);returnnewAutoConfigurationEntry(configurations,exclusions);}本质上,EnableAutoConfiguration会帮助springboot应用将所有@Configuration配置加载到当前SpringBoot创建的IoC容器中,该容器使用了Spring框架提供的一个工具类SpringFactoriesLo对ader的支持以及Spring提供的条件注解@Conditional用于选择性的过滤需要加载的bean。SpringFactoriesLoader为了给大家补充基础,这里简单分析一下工具类SpringFactoriesLoader的使用。它其实和java中的SPI机制是一样的原理,但是比SPI更好的地方在于它不是一次加载所有的类,而是根据key加载。首先,SpringFactoriesLoader的作用是根据classpath/META-INF/spring.factories文件中的key,将对应的类加载到springIoC容器中。接下来带大家练习创建外部工程jarorg.springframeworkspring-context4.3.13.RELEASEcreationbeanandconfigpublicclassGuPaoCore{publicStringstudy(){System.out.println("好好好好学习,天天向上");return"古炮教育网";}}@ConfigurationpublicclassGuPaoConfig{@BeanpublicGuPaoCoreguPaoCore(){returnnewGuPaoCore();}}再创建一个项目(spring-boot)将之前的项目打包成jar,当前项目依赖jar包com.gupaoedu.practiceGupao-Core1.0-SNAPSHOT通过以下代码获取依赖包中的属性,会报错,因为GuPaoCore还没有被Spring的IoC容器加载,即Not由EnableAutoConfiguration@SpringBootApplication导入publicclassSpringBootStudyApplication{publicstaticvoidmain(String[]args)throwsIOException{ConfigurableApplicationmotionContextac=SpringApplication.run(SpringBootStudyApplication.class,args);GuPaoCoregpc=ac.getBean(GuPaoCore.class);System.out.println(gpc.study());}}解决方法是在GuPao-Core项目资源下新建一个文件夹META-INF,在文件夹下新建一个spring.factories文件,在文件中配置,关键是自定义配置类EnableAutoConfiguration的全路径,以及value是配置类的全路径org.springframework.boot.autoconfigure.EnableAutoConfiguration=com重新打包.gupaoedu.practice.GuPaoConfig,重新运行SpringBootStudyApplication类,发现加载了我们写的类@EnableAutoConfiguration的实现原理annotation理解了ImportSelector和ImportBeanDefinitionRegistrar之后,再理解EnableAutoConfiguration就比较容易了。它会通过import导入第三方提供的bean配置类:AutoConfigurationImportSelector@Import(AutoConfigurationImportSelector.class)从名字就可以猜到它是基于ImportSelector实现基于动态bean的加载功能。之前我们讲过Springboot@Enable*注解的工作原理。ImportSelector接口selectImports返回的数组(类的完整类名)将被包含在spring容器中。那么你可以猜想这里的实现原理一定是一样的,定位到AutoConfigurationImportSelector类中的selectImports方法}//从配置文件(spring-autoconfigure-metadata.properties)中加载AutoConfigurationMetadataAutoConfigurationMetadataautoConfigurationMetadata=AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);//获取所有可选配置类EnableAutoConfigurationAutoConfigurationEntryautoConfigurationEntry=getAutoConfigurationMetadata(annotation)返回StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}getAutoConfigurationEntryprotectedAutoConfigurationEntrygetAutoConfigurationEntry(AutoConfigurationMetadataautoConfigurationMetadata,AnnotationMetadataannotationMetadata){如果(!isEnabled(annotationMetadata)){返回EMPTY_ENTRY;}//获取meta注解中的属性AnnotationAttributesattributes=getAttributes(annotationMetadata);//使用SpringFactoriesLoader加载classpath路径下的META-INF\spring.factories,//key=org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的valueListconfigurations=getCandidateConfigurations(annotationMetadata,attributes);//去除重复配置=removeDuplicates(configurations);//应用排除属性Setexclusions=getExclusions(annotationMetadata,attributes);checkExcludedClasses(配置,排除);configurations.removeAll(exclusions);//过滤,检查候选配置类上的注解@ConditionalOnClass,如果需要的类不存在,则过滤候选类不加载configurations=filter(configurations,autoConfigurationMetadata);//广播事件fireAutoConfigurationImportEvents(configurations,exclusions);returnnewAutoConfigurationEntry(configurations,exclusions);}本质上,EnableAutoConfiguration会帮助springboot应用将所有@Configuration配置加载到当前SpringBoot创建的IoC容器中,该容器使用了Spring框架提供的工具类SpringFactoriesLoader的支持和Spring提供的条件注解@Conditional用于选择性的过滤需要加载的bean。版权声明:除特别声明外,本博客所有文章均采用CCBY-NC-SA4.0许可协议。转载请注明来自Mic带你学建筑!如果本文对您有帮助,请给个关注和点赞。您的坚持是我不断创作的动力。欢迎关注同名微信公众号获取更多技术干货!