自动配置主要是同过@SpringBootApplication这个类注解实现的,这个注解是一个合成注解,里面由多个注解组成。
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
一、 @SpringBootConfiguration
使用@Configuration修饰这个注解。代表当前这个注解是一个配置类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
二、@ComponentScan
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
excludeFilters :排除指定的组件
FilterType.CUSTOM:按照自定义规则排除
TypeExcludeFilter.class:自定义规则组件
三、@EnableAutoConfiguration(开启自动配置)
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
四、@AutoConfigurationPackage
自动导包配置注解,里面是@Import(AutoConfigurationPackages.Registrar.class)
1、AutoConfigurationPackages.Registrar.class
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
metadata:注解元信息,也就是这个注解修饰的类的全路径类名
register:扫描注解所在的类的包下的所有组件,并注册进容器,也就是SpringAnnotateApplication所在的包。所以springboot,默认自动导入SpringAnnotateApplication所在的包下的所有组件。
metadata:注解元信息,也就是这个注解修饰的类的全路径类名
register:扫描注解所在的类的包下的所有组件,并注册进容器,也就是SpringAnnotateApplication所在的包。所以springboot,默认自动导入SpringAnnotateApplication所在的包下的所有组件。
五、@Import({AutoConfigurationImportSelector.class}
主要是AutoConfigurationImportSelector类中getAutoConfigurationEntry方法给容器进行批量导入组件。

1、进入getAutoConfigurationEntry方法,
找到List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
这行代码,这里就把指定路径下文件中的配置类加载进入容器了。
2、进入getCandidateConfigurations,只有一行
List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());是主要代码,从这里进入可以看到具体加载逻辑
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 先从缓存中获取配置文件
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
// 如果没有从指定classLoader,则默认加载系统下所有META-INF/spring.factories位置中的文件
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
MultiValueMap<String, String> result = new LinkedMultiValueMap();
// 循环处理,然后放入缓存
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()) {
Map.Entry<?, ?> entry = (Map.Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
spring-boot-autoconfigure这个包里面也有META-INF/spring.factories文件,文件里面写死了spring-boot启动就要给容器中加载的所有配置类。

虽然springboot启动时,就默认加载META-INF/spring.factories下的所有配置文件,但是这些配置文件并不是所有都会启动,他们只会按条件装配,只有符合某些条件,才会开启。以下是几个springboot底层按条件开启配置的栗子:
1、springboot底层给容器中加入了文件上传解析器
@Bean
@ConditionalOnBean(MultipartResolver.class) //容器中有这个类型组件
@ConditionalOnMissingBean(name = DispatcherServlet.) //容器中没有这个名字 multipartResolver 的组件
public MultipartResolver multipartResolver(MultipartResolver resolver) {
//给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
//SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范
// 有些用户可能配置了MultipartResolver这个类型的组件,但是组件名字不规范
return resolver;
}
2、优先使用用户配置
@Bean
@ConditionalOnMissingBean // 如果容器中没有CharacterEncodingFilter 这个bean才开启,相当于优先使用用户的,用户没有会配置,将会开启这个兜底
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));
return filter;
}
参考文章:
Comments NOTHING