上一篇 Spring Boot 之 Starter 加入几个依赖以后,就完成了一个简单的 Web 项目。没有配置 web.xml,也没有手动声明 DispatcherServlet、数据源和 Thymeleaf 的模板解析器,这些工作是怎么完成的?
普通 Spring 项目通常需要自己配置 ContextLoaderListener、DispatcherServlet、数据源、JSON 序列化和静态资源等内容。Spring Boot 则准备了一套默认配置,例如 Web 服务默认监听 8080 端口,静态资源可以放在 static 目录,Thymeleaf 模板默认放在 templates 目录。
配置并没有消失,只是由 Spring Boot 根据当前项目的依赖和已有 Bean 自动装配了。
配置属性
在项目依赖中找到 spring-boot-autoconfigure-1.5.4.RELEASE.jar,可以看到很多自动配置类。先看 Thymeleaf 的属性类,下面保留几个主要字段:
@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING = Charset.forName("UTF-8");
private static final MimeType DEFAULT_CONTENT_TYPE = MimeType.valueOf("text/html");
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private String prefix = DEFAULT_PREFIX;
private String suffix = DEFAULT_SUFFIX;
private String mode = "HTML5";
private Charset encoding = DEFAULT_ENCODING;
private MimeType contentType = DEFAULT_CONTENT_TYPE;
private boolean cache = true;
private boolean enabled = true;
}@ConfigurationProperties 会把以 spring.thymeleaf 开头的配置绑定到这个类。模板目录、文件后缀、编码和缓存等默认值都能在这里看到。
需要修改时,可以在 application.properties 中覆盖对应属性:
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false开发时关闭模板缓存比较方便,正式运行时通常保留默认值。
条件化配置
项目启动时并不会无条件创建所有组件。Spring 4 提供了 @Conditional,Spring Boot 又在它的基础上定义了一组更容易使用的条件注解:
| 条件注解 | 配置生效条件 |
|---|---|
@ConditionalOnBean | 容器中存在指定的 Bean |
@ConditionalOnMissingBean | 容器中不存在指定的 Bean |
@ConditionalOnClass | Classpath 中存在指定的类 |
@ConditionalOnMissingClass | Classpath 中不存在指定的类 |
@ConditionalOnWebApplication | 当前是 Web 应用 |
继续看 ThymeleafAutoConfiguration 的部分代码:
@Configuration
@EnableConfigurationProperties(ThymeleafProperties.class)
@ConditionalOnClass(SpringTemplateEngine.class)
@AutoConfigureAfter(WebMvcAutoConfiguration.class)
public class ThymeleafAutoConfiguration {
@Configuration
@ConditionalOnJava(ConditionalOnJava.JavaVersion.EIGHT)
@ConditionalOnClass(Java8TimeDialect.class)
protected static class ThymeleafJava8TimeDialect {
@Bean
@ConditionalOnMissingBean
public Java8TimeDialect java8TimeDialect() {
return new Java8TimeDialect();
}
}
@Configuration
@ConditionalOnWebApplication
protected static class ThymeleafResourceHandlingConfig {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledResourceChain
public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
return new ResourceUrlEncodingFilter();
}
}
}最外层的 @ConditionalOnClass(SpringTemplateEngine.class) 表示 Classpath 中存在 Thymeleaf 的 SpringTemplateEngine 时,才考虑这组配置。@EnableConfigurationProperties 负责启用前面的 ThymeleafProperties。
内部配置又有各自的条件。例如 Java 8 时间方言必须同时满足 Java 版本和类依赖条件,并且容器中没有同类型 Bean 时才创建。这样既能按需配置,也给手动配置留出了覆盖默认行为的位置。
自动配置从哪里加载
启动类上的 @SpringBootApplication 包含 @EnableAutoConfiguration。在 Spring Boot 1.5.4 中,自动配置类记录在 META-INF/spring.factories 里,启动时会被加载,再逐个判断条件是否成立。
如果想看当前项目启用了哪些自动配置,可以使用 debug 模式启动:
java -jar target/spring-boot-hello-1.0-SNAPSHOT.jar --debug日志中的自动配置报告会列出匹配和未匹配的配置及原因。这样再看 Starter、Classpath、条件注解和最终创建的 Bean,它们之间的关系就比较清楚了。
自动配置的目标是根据项目当前条件给出合适的默认值。自己声明 Bean 时,很多默认配置会自动退让;需要排查时,条件报告比单纯猜测某个配置是否生效更直接。相关机制可以对照 Spring Boot 1.5.4 自动配置文档。