SpringBoot自动配置浅析

自动配置是什么?

引入SpringBoot官方文档解释

Spring Boot自动配置尝试根据您添加的jar依赖项自动配置Spring应用程序。例如,如果HSQLDB 在您的类路径上,并且您尚未手动配置任何数据库连接bean,则Spring Boot会自动配置内存数据库。

自动配置的作用

自动配置是SpringBoot的一大特性,这特性使得SpringBoot受许多开发者喜爱,也是SpringBoot能够快速发展起来的原因之一。根据官方文档解释可知道,自动配置能够减少xml繁琐的配置,让开发者更加关注于业务逻辑的开发,用JavaConfig替代xml,使得开发变得更加的灵活。比如以前用SpringMVC需要配置组件扫描、调度器、视图解析器等,用了SpringBoot以后,只需要引入对应的spring-boot-autoconfigure依赖,就能自动配置好所需的bean。当然可以修改自动配置注入bean的参数。

如何实现自动配置

SpringBoot自动配置无需各种复杂的pom坐标,也无需配置Web容器,只需在添加@SpringBootApplication所在的类中执行mian方法,就能跑起来。所以我们首先先拨开@SringBootApplcation的真实面纱,来一探究竟。

1
2
3
4
5
6
7
8
9
10
11
12
@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:作用其实跟我们平常用来定义配置类@Configuration的注解是一样的,可以点进去这个注解进去看是含有@Configuration注解的。
  • @EnableAutoConfiguration:从字面上的意思是,开启自动配置的注解,也是本文的重点要讲的注解。
  • @ComponentScan: 这个相信大家在spring配置mvc中见过,就是对MVC中@Controller/@Service/@Component/@Repository等注解加载到IOC容器中

那下面我们就再来看看@EnableAutoConfiguration到底里面有什么东东

1
2
3
4
5
6
7
8
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
  • @AutoConfigurationPackage: 所有自动配置类所在的包

除了上面还有一个注解是@Import(AutoConfigurationImportSelector.class),引入一个叫AutoConfigurationImportSelector的类,这个类主要是读取自动配置类,并将其引入到容器中来。那接下来就来看看该类是如何读取并引入的?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@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());
}

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);
}

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
// 关键就是这一步,通过SpringFactories加载器加载自动配置类
// 其中内部调用classLoader.getResources(FACTORIES_RESOURCE_LOCATION) 方法获取
// FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
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;
}

实际是如何获取到自动配置的类呢? 看上面源码的最后一个方法中,可以发现其实是通过SpringFactoriesLoader加载Spring-boot-starter-*依赖对应的jar包下META-INF/spring.factories文件,这个文件以key=value形式,与.properties配置文件一样的格式均为键值对。

举个实例验证

当我们在pom文件中引入starter某些依赖后,就会自动触发该依赖的自动配置。现在我们就来试下引入spring-boot-starter-web依赖。在pom.xml引入web依赖

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

引入完以后在Spring-boot-autoconfigure的jar包中找到web下的自动配置类ServletWebServerFactoryAutoConfiguration

1
2
3
4
5
6
7
8
9
10
11
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
//省略..
  • AutoConfigureOrder:定义自动配置的顺序
  • ConditionalOnClass: 只有满足在ServletRequest类存在的条件才能加载该bean
  • ConditionalOnWebApplication:只有满足应用web应用类型为Servlet条件下才加载该bean

ConditionalOnXXX是Spring4中新的一类注解,为了解决某bean需要在某些条件下才能够被加载的需求。当然除了上面两个条件注解之外,还定义了很多bean,具体可以看附录中的介绍。

建议读者可以利用debug打断点的方式,一步一步跟踪下自动配置发生在启动流程哪些步骤中。可以在SpringFactoriesLoader类中loadFactoryNames方法中打断点

附录

  • @ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean
  • @ConditionalOnClass:某个class位于类路径上,才会实例化一个Bean
  • @ConditionalOnJava:基于JVM版本作判断
  • @ConditionalOnProperty:指定属性中是否有指定的值
  • @ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean
  • @ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean
  • @ConditionalOnMissingClass:某个class类路径上不存在的时候,才会实例化一个Bean
  • @ConditionalOnNotWebApplication:不是web应用

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×