前言
SpringBoot基本实现了0配置,因为很多东西官网都提供了默认的配置。但是有时候自己写的时候难免还是需要配置一些东西,所以理解她的自动配置原理挺重要的!
一、从问题出发
提出三个问题,以此来明确接下来阅读的目的:- 怎么启动自动装配
- 自动装配的内容
- 怎么筛选这些配置内容(提供的肯定是一推配置,怎么筛选掉不用的配置内容)
二、阅读源码来解决问题
问题1.怎么启动自动装配
神奇从这个启动类的@SpringBootApplication开始
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
点开这个SpringBootApplication注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// .......
}
只看@SpringBootConfiguration注解和@EnableAutoConfiguraton注解
对于@SpringBootConfiguration注解,往下继续点开,发现@Component注解。也就是说这个类最后是被当做一个组件被加到spring容器。
下面看@EnableAutoConfiguration注解 其实是开启自动配置的意思
看@Import注解,点开对应的AutoConfigurationImportSelector.class 其中有一个selectImports方法。
首先这个方法是被下面图片的类加载,我就把这个图片中的类当做一个具体加载配置的类来看,不再往上研究,而是往下看加载的内容。
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// ...
}
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
返回给上层的内容就是AutoConfigurationEntry中的getConfigurations()。
接着往下看,去看configurations对应的是什么?
// return 应该导入的自动配置
protected AutoConfigurationEntry getAutoConfigurationEntry(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 = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
其中的isEnabled方法判断的是自动导入配置是否生效,是由于加了@EnableAutoCongruation注解,所以这个自动配置就生效,具体怎么实现的,我没继续往下看。
现在是否启动自动配置问题告一段落,接着看自动配置的内容。
结论:因为用了有@EnableAutoCongruation注解,所以isEnabled()方法判断自动配置生效。
2.配置内容?
从上图继续,这个获取候选配置方法
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
这句话字面意思获得预选配置。所以现在关键是configurations。
直接说结论configurations是spring.factories中配置的组件全类名形成的list
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()
看到这里基本没啥耐心了。。。。
接下来这段的意思就是加载"META-INF/spring.factories"这个下面的所有配置的组件的全限定类名,然后形成一个list返回。
也就是说现在我们自动配置的内容,在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);
}
}
那么具体自动配置的内容在哪呢?
我们不妨以springMvc的配置举例。
首先打开spring.properties文件,其中有一个mvc自动配置组件的全限定类名。点开
其中可以看到下面还有@Bean注解,@ConditionalOnProperty。
其实这个组件对应着一个pom.xml中的一个启动器,这个启动器需要的环境由一个个bean组成,把这个bean配置好之后放入到ioc容器中就是实现了环境的自动配置。
而bean真是的内容来自于@ConditionalOnProperty对应的一个XXXProperties文件,其中提供了官网提供的约定配置。
**总结一下前俩个问题:
- 带有@EnableAutoConfigruation注解的就开启自动配置
- 装配的内容来自哪里? 在spring-boot-autoConfiguration报下的META-INF/spring.factories文件**
- spring.factories中配置的组件中对应着各种bean,这些bean是一个个具体的类,其中的属性就是需要配置的东西。配置内容在一个个XXXProperties文件中,配置内容和属性对应就通过一个@ConditionalOnProperty来进行关联。
3.剔除不需要的配置
我们打开spring.factories文件下的一个类
发现爆红!
剔除不需要的配置关键在于@ConditionalXXX这个注解
可以看到RabbitTEmplate这个类,所以这个条件没有满足,所以这个配置不生效。
那么怎么让他生效呢?我们看一个生效的类
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-**starter**-web</artifactId>
</dependency>
生效是因为我们在pom.xml文件中导入了web的启动器,那么一些类就被导入,所以这个条件注解生效。
该处使用的url网络请求的数据。
自己配置
上面三个问题基本回答完毕,那么我们该如何自己配置呢? 我们可以写一个application.yaml文件,如果这个对应的启动器被加载,那么我们要配置的内容必定对应一个已经配置好的xxx.properties文件,并且已经有@ConditionalOnProperty对应到具体的实体类中来覆盖固定的值。举个例子:
这个是web启动器下的一个文件下的。他加了ConditionalOnProperty注解,而且对应着server.forward-headers-strateg,那么一定有一个实体类配置对应。
可以看到自己写的时候有提示!
@Bean
@ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
@ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
对应的实体类:
完结收工!!!