Spring Boot自动装配原理源码分析
1.环境准备
使用IDEA Spring Initializr快速创建一个Spring Boot项目
添加一个Controller类
@RestController
public class HelloController {
@RequestMapping("hello")
public String hello() {
return "hello";
}
}
主配置类如下
@SpringBootApplication
public class SpringbootQuickstartApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootQuickstartApplication.class, args);
}
}
2.注解分析
Spring Boot规定,项目的主配置类必须放在最外层包,也就是说,所有的类都必须放在主配置类的同级包或者子包里,这么做的用意是什么?我们点开@SpringBootApplication
注解慢慢分析(下面代码中省略元注解)…
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
})
public @interface SpringBootApplication {
@SpringBootApplication
内部标注了三个注解:
-
@SpringBootConfiguration
进入源码中可以看见,
@SpringBootConfiguration
其实就是Spring中的@Configuration
,用于标注配置类@Configuration public @interface SpringBootConfiguration {
-
@ComponentScan
这个注解也是Spring中的,它用来将指定包下需要装配的组件注册到容器中
-
@EnableAutoConfiguration
接下来才是今天的重头戏,Spring Boot自动配置的主角!
@AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {
3.自动装配的主角
进入@EnableAutoConfiguration
源码你会发现这个注解中标注了两个注解@AutoConfigurationPackage
和@Import
(1)、@AutoConfigurationPackage
点进该注解
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
在点进Register,这是一个静态内部类
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));
}
}
我们在第一个方法处打一个断点debug一下,发现new PackageImport(metadata).getPackageName()
的结果其实就是一个包名,这时我们很容易的可以想到,这个包就是Spring Boot主配置类所在的包
再看一眼metadata,果然,就是主配置类
因此,这个注解的作用就是将主配置类所在的包作为自动配置包进行管理
(2)、@Import(AutoConfigurationImportSelector.class)
@Import
的作用就是导入一个类到IOC容器,我们先来看一下导入的这个类:自动配置导入选择器
源码里有一个方法selectImports
,选择导入
@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());
}
在点进getAutoConfigurationEntry
,获取自动配置类
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
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);
}
在getCandidateConfigurations
方法处打一个断点,发现configurations的结果是所有的xxxAtuoConfiguration
类,一共124个,请记住这个数字。
那么这些自动配置类是如何获取的呢,从哪里获取的呢?
我们继续点进getCandidateConfigurations
,获取候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations =
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "... ...");
return configurations;
}
继续点,loadFactoryNames
,加载工厂名,方法所在类中有一个常量FACTORIES_RESOURCE_LOCATION
,看代码可以清晰的看到,这方法加载classpath下的所有jar包的META-INF/spring.factories
文件,结果用一个HashMap存储
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
打开spring-boot-autoconfigure-2.2.4.RELEASE.jar/META-INF/spring.factories
,文件部分类容如下,你可以点进去看看第22~145行,确实是124个全类名
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration
... ...
4.类的加载时机
加载了这么多类我们又不一定全都用得到,设计师肯定会想办法让类在我们需要的时候才生效,我们随便点进一个类,可以看到一片飘红,因为我们并没有引入RabbitMQ相关依赖,再看一个注解ConditionalOnClass
,意思就是存在某个指定的类才生效,类似的注解还有很多,都是@ConditionaOn xxx
,在一定条件下类才会生效。
由于引入了web模块,WebMvcAutoConfiguration
正常显示