序言
不管在工作中还是在求职面试中,SpringBoot成为了我们必会的知识技能之一。当然,作为Spring Boot的精髓,自动配置原理首当其冲。在本文章的下面,也是自己在源代码中挣扎的一些简单理解。
思维导图
原理分析
-
首先打开一个基本的springboot项目,我们可以看到
@SpringBootApplication
,说明这个类是SpringBoot的应用。SpringBoot就会运行这个类的main方法来启动SpringBoot项目:
-
点进去@SpringBootApplication注解,我们可以看到:
-
首先我们点进去看一下
@SpringBootConfiguration
注解,表明该类是一个Spring的配置类:
在点进去@Configuration看一下,可以看到Spring的配置类也是Spring的一个组件:
-
记下来我们点开
@EnableAutoConfiguration
,进入自动配置的注解:
我们首下看一下@AutoConfigurationPackage
注解:
可以看到在这里主要是使用的@Impor
t来给Spring容器中导入一个组件 ,这里导入的是Registrar.class
,我们点进去看一下:/** * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing * configuration. */ static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImport(metadata).getPackageName()); } @Override public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImport(metadata)); } }
就是通过
registerBeanDefinitions
这个方法获取扫描的包路径,我们可以debug看看:
从上图我们可以看到new PackageImport(metadata).getPackageName()
可以获取到扫描的包路径,那么我们再看一下这个metadata
是什么?
可以看到是标注在@SpringBootApplication注解上的DemosbApplication,也就是我们的主配置类,说白了就是将主配置类(即@SpringBootApplication标注的类)的所在包及子包里面所有组件扫描加载到Spring容器。所以再这里包名一定要注意。 -
到现在,包扫描路径获取到了,那具体加载哪些组件呢,我们看一下
@Import(AutoConfigurationImportSelector.class)
,通过@Import注解就是给Spring容器中导入组件AutoConfigurationImportSelector
,我们可以点进去看一下:@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
上面代码中
loadmetadata
的方法是加载项目的基本配置数据信息,而getAutoConfigurationEntry
方法则是自动装配的逻辑,继续点进去:/** * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata} * of the importing {@link Configuration @Configuration} class. * @param autoConfigurationMetadata the auto-configuration metadata * @param annotationMetadata the annotation metadata of the configuration class * @return the auto-configurations that should be imported */ protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { //自动装配的开关,如果没有返回null if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); //获取候选配置 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
这时我们可以在getAutoConfigurationEntry中打一个DeBug,看看:
可以看到通过getCandidateConfigurations
这个方法是获取候选的配置,我们点进去看看:/** * Return the auto-configuration class names that should be considered. By default * this method will load candidates using {@link SpringFactoriesLoader} with * {@link #getSpringFactoriesLoaderFactoryClass()}. * @param metadata the source metadata * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation * attributes} * @return a list of candidate configurations */ protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; }
我们看一下loadFactoryNames,点进去:
到这里基本清楚了,springboot的自动装配就是通过自定义实现ImportSelector接口,从而导致项目启动时会自动将所有项目META-INF/spring.factories路径下的配置类注入到spring容器中,从而实现了自动装配。
J2EE的整体整合解决方案和自动配置都在spring-boot-autoconfigure-2.2.12.RELEASE.jar
小结
多看源码,debug调试