简介
springboot是由Pivotal团队提供的全新框架。spring的出现是为了解决企业级开发应用的复杂性,spring的通过注册bean的方式来管理类,但是随着业务的增加,使用xml配置bean的方式也显得相当繁琐,所以springboot就是为了解决spring配置繁琐的问题而诞生的。
核心注解
@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@Conditional
核心思想
约定大于配置
自动装配源码解析
启动项
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class,args);
}
}
@SpringBootApplication注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
@SpringBootApplication注解主要由这三个注解构成,我们先看第一个
@SpringBootConfiguration
主要就是一个@Configuration,一个JavaConfig配置类
@ComponentScan
扫描包路径的注解这个不用过多阐述了
@EnableAutoConfiguration
@EnableAutoConfiguration上有一个@Import(AutoConfigurationImportSelector.class) 导入注解,我们着重看一下他导入了什么内容
AutoConfigurationImportSelector
实现了一个spring提供的导入选择器,就会去重写selectImports方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//获取自动配置文件的全包名
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
这里其实就是读取spring.factories文件的信息
先看看spring.factories里的信息格式
spring.factories里得信息是k-v的格式,自动装配呢解析的是k为EnableAutoConfiguration注解的全类名,我们继续回头看源码
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
//@EnableAutoConfiguration注解的信息
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//获取spring.factories中k为EnableAutoConfiguration注解的全类名下的类名
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//去重
configurations = this.removeDuplicates(configurations);
//获取要排除的类
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
//移除排除项
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
我们着重看this.getCandidateConfigurations(annotationMetadata, attributes)方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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的方法,我们继续看
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
//类加载器
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
//这里拿到的是@EnableAutoConfiguration全类名
String factoryTypeName = factoryType.getName();
//核心方法去获取spring.factories文件并过滤获取k为@EnableAutoConfiguration全类名的集合
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
HashMap result = new HashMap();
try {
//加载META-INF/spring.factories里的资源
Enumeration urls = classLoader.getResources("META-INF/spring.factories");
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
这个方法的返回值是一个Map<String, List>的结构,验证了我们刚刚所说的spring.factories中的kv 并且v是集合,我们debug来看一下这个返回值。
其中有我们要的@EnableAutoConfiguration全类名的集合,但是他的集合容量多达183个,其实我们用不着这么多,我们继续看后续springboot如何帮我们过滤的。
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
return loadMetadata(classLoader, PATH);
}
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
try {
Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path)
: ClassLoader.getSystemResources(path);
Properties properties = new Properties();
while (urls.hasMoreElements()) {
properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
}
return loadMetadata(properties);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
}
}
spring还会去解析spring-autoconfigure-metadata.properties的文件(这个文件在项目编译时会生成)获取自动装配类上面的条件注解,然后会帮我们把不符合条件的过滤掉。
自动装配本质
- SpringBoot自动装配的本质就是通过Spring去读取META-INF/spring.factories中保存的配置类文件然后加载bean定义的过程。
- 如果是标了@Configuration注解,就是批量加载了里面的bean定义
- 如何实现”自动“:通过配置文件获取对应的批量配置类,然后通过配置类批量加载bean定义,只要有写好的配置文件spring.factories就实现了自动。