Spring Boot Starters启动器
Spring Boot Starters启动器
Starters是什么?
Starters可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式集成Spring及其他技术,而不需要到处找示例代码和依赖包。如你想使用Spring JPA访问数据库,只要加入spring-boot-starter-data-jpa启动器依赖就能使用了。
Starters包含了许多项目中需要用到的依赖,它们能快速持续的运行,都是一系列得到支持的管理传递性依赖。
Starters命名
Spring Boot官方的启动器都是以spring-boot-starter-命名的,代表了一个特定的应用类型。
第三方的启动器不能以spring-boot开头命名,它们都被Spring Boot官方保留。一般一个第三方的应该这样命名,像mybatis的mybatis-spring-boot-starter。
Starters分类
1. Spring Boot应用类启动器
启动器名称 | 功能描述 |
---|---|
spring-boot-starter | 包含自动配置、日志、YAML的支持。 |
spring-boot-starter-web | 使用Spring MVC构建web 工程,包含restful,默认使用Tomcat容器。 |
... | ... |
2. Spring Boot生产启动器
启动器名称 | 功能描述 |
---|---|
spring-boot-starter-actuator | 提供生产环境特性,能监控管理应用。 |
3. Spring Boot技术类启动器
启动器名称 | 功能描述 |
---|---|
spring-boot-starter-json | 提供对JSON的读写支持。 |
spring-boot-starter-logging | 默认的日志启动器,默认使用Logback。 |
... | ... |
4. 其他第三方启动器
更多可以参考下面链接。
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-starters/README.adoc
用过 Spring Boot 的都知道在 Spring Boot 中有以下两种配置文件
-
bootstrap (.yml 或者 .properties)
-
application (.yml 或者 .properties)
为什么会有这两种配置文件呢?大家都清楚它们的区别和具体使用场景吗?
bootstrap/ application 的区别
特意去翻了下 Spring Boot 的官方文档,没有找到关于这两种文件的具体定义,然后再翻了下 Spring Cloud 的官方文档找到了它们的区别。
http://cloud.spring.io/spring-cloud-static/Edgware.SR3/single/spring-cloud.html#thebootstrapapplicationcontext
认真阅读了下文档,原文大概意思是这样。
Spring Cloud 构建于 Spring Boot 之上,在 Spring Boot 中有两种上下文,一种是 bootstrap, 另外一种是 application, bootstrap 是应用程序的父上下文,也就是说 bootstrap 加载优先于 applicaton。bootstrap 主要用于从额外的资源来加载配置信息,还可以在本地外部配置文件中解密属性。这两个上下文共用一个环境,它是任何Spring应用程序的外部属性的来源。bootstrap 里面的属性会优先加载,它们默认也不能被本地相同配置覆盖。
因此,对比 application 配置文件,bootstrap 配置文件具有以下几个特性。
-
boostrap 由父 ApplicationContext 加载,比 applicaton 优先加载
-
boostrap 里面的属性不能被覆盖
bootstrap/ application 的应用场景
application 配置文件这个容易理解,主要用于 Spring Boot 项目的自动化配置。
bootstrap 配置文件有以下几个应用场景。
-
使用 Spring Cloud Config 配置中心时,这时需要在 bootstrap 配置文件中添加连接到配置中心的配置属性来加载外部配置中心的配置信息;
-
一些固定的不能被覆盖的属性
-
一些加密/解密的场景;
以下这个截图是一个国外网友问了一个 Spring Cloud 工程师得到的回答。
做过 Spring Cloud 微服务的朋友应该对 bootstrap 的应用十分清楚,我们也有 Spring Cloud 的实战教程,在 Spring 专题中都能看到。
使用 Spring Boot 会涉及到各种各样的配置,如开发、测试、线上就至少 3 套配置信息了。Spring Boot 可以轻松的帮助我们使用相同的代码就能使开发、测试、线上环境使用不同的配置。
在 Spring Boot 里面,可以使用以下几种方式来加载配置。本章内容基于 Spring Boot 2.0 进行详解。
1、properties文件;
2、YAML文件;
3、系统环境变量;
4、命令行参数;
等等……
配置属性加载的顺序如下:
1、开发者工具 `Devtools` 全局配置参数;
2、单元测试上的 `@TestPropertySource` 注解指定的参数;
3、单元测试上的 `@SpringBootTest` 注解指定的参数;
4、命令行指定的参数,如 `java -jar springboot.jar --name="Java技术栈"`;
5、命令行中的 `SPRING_APPLICATION_JSONJSON` 指定参数, 如 `java -Dspring.application.json='{"name":"Java技术栈"}' -jar springboot.jar`
6、`ServletConfig` 初始化参数;
7、`ServletContext` 初始化参数;
8、JNDI参数(如 `java:comp/env/spring.application.json`);
9、Java系统参数(来源:`System.getProperties()`);
10、操作系统环境变量参数;
11、`RandomValuePropertySource` 随机数,仅匹配:`ramdom.*`;
12、JAR包外面的配置文件参数(`application-{profile}.properties(YAML)`)
13、JAR包里面的配置文件参数(`application-{profile}.properties(YAML)`)
14、JAR包外面的配置文件参数(`application.properties(YAML)`)
15、JAR包里面的配置文件参数(`application.properties(YAML)`)
16、`@Configuration`配置文件上 `@PropertySource` 注解加载的参数;
17、默认参数(通过 `SpringApplication.setDefaultProperties` 指定);
数字小的优先级越高,即数字小的会覆盖数字大的参数值,我们来实践下,验证以上配置参数的加载顺序。
1、在主应用程序中添加 Java 系统参数。
@Bean
public CommandLineRunner commandLineRunner() {
return (args) -> {
System.setProperty("name", "javastack-system-properties");
};
}
2、在 application.properties 文件中添加属性。
name = javastack-application
3、在 application-dev.properties 文件中添加属性。
name = javastack-application-dev
4、添加测试类
@RunWith(SpringRunner.class)
@SpringBootTest(value = { "name=javastack-test", "sex=1" })
@ActiveProfiles("dev")
public class SpringBootBestPracticeApplicationTests {
@Value("${name}")
private String name;
@Test
public void test() {
System.out.println("name is " + name);
}
}
运行 test 单元测试,程序输出:
name is javastack-test
根据以上参数动态调整,发现参数会被正确被覆盖。了解了 Spring Boot 各种配置的加载顺序,如果配置被覆盖了我们就知道是什么问题了。
在Spring Boot实现代码热部署是一件很简单的事情,代码的修改可以自动部署并重新热启动项目。
引用devtools依赖
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-devtools</artifactId>
-
<optional>true</optional>
-
</dependency>
这样,当修改一个java类时就会热更新。
自定义配置热部署
以下配置用于自定义配置热部署,可以不设置。
-
# 热部署开关,false即不启用热部署
-
spring.devtools.restart.enabled: true
-
-
# 指定热部署的目录
-
#spring.devtools.restart.additional-paths: src/main/java
-
-
# 指定目录不更新
-
spring.devtools.restart.exclude: test/**
Intellij Idea修改
如果是idea,需要改以下两个地方:
1、勾上自动编译或者手动重新编译
File > Settings > Compiler-Build Project automatically
2、注册
ctrl + shift + alt + / > Registry > 勾选Compiler autoMake allow when app running
注意事项
1、生产环境devtools将被禁用,如java -jar方式或者自定义的类加载器等都会识别为生产环境。
2、打包应用默认不会包含devtools,除非你禁用SpringBoot Maven插件的 excludeDevtools
属性。
3、Thymeleaf无需配置 spring.thymeleaf.cache:false
,devtools默认会自动设置,参考完整属性。
https://github.com/spring-projects/spring-boot/blob/v1.5.7.RELEASE/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java
下面是devtools自动配置的部分源码:
-
@Order(Ordered.LOWEST_PRECEDENCE)
-
public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostProcessor {
-
-
private static final Map<String, Object> PROPERTIES;
-
-
static {
-
Map<String, Object> properties = new HashMap<String, Object>();
-
properties.put("spring.thymeleaf.cache", "false");
-
properties.put("spring.freemarker.cache", "false");
-
properties.put("spring.groovy.template.cache", "false");
-
properties.put("spring.mustache.cache", "false");
-
properties.put("server.session.persistent", "true");
-
properties.put("spring.h2.console.enabled", "true");
-
properties.put("spring.resources.cache-period", "0");
-
properties.put("spring.resources.chain.cache", "false");
-
properties.put("spring.template.provider.cache", "false");
-
properties.put("spring.mvc.log-resolved-exception", "true");
-
properties.put("server.jsp-servlet.init-parameters.development", "true");
-
PROPERTIES = Collections.unmodifiableMap(properties);
-
}
4、devtools会在windows资源管理器占用java进程,在开发工具里面杀不掉,只能手动kill掉,不然重启会选成端口重复绑定报错。
关于boot-devtools更多详细用法,参考官方文档。
https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-devtools.html
Spring Boot依赖
使用Spring Boot很简单,先添加基础依赖包,有以下两种方式
1. 继承spring-boot-starter-parent项目
-
<parent>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-starter-parent</artifactId>
-
<version>1.5.6.RELEASE</version>
-
</parent>
2. 导入spring-boot-dependencies项目依赖
-
<dependencyManagement>
-
<dependencies>
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-dependencies</artifactId>
-
<version>1.5.6.RELEASE</version>
-
<type>pom</type>
-
<scope>import</scope>
-
</dependency>
-
</dependencyManagement>
Spring Boot依赖注意点
1. 属性覆盖只对继承有效
This only works if your Maven project inherits (directly or indirectly) from spring-boot-dependencies. If you have added spring-boot-dependencies in your own dependencyManagement section withimportyou have to redefine the artifact yourself instead of overriding the property.
Spring Boot依赖包里面的组件的版本都是和当前Spring Boot绑定的,如果要修改里面组件的版本,只需要添加如下属性覆盖即可,但这种方式只对继承有效,导入的方式无效。
-
<properties>
-
<slf4j.version>1.7.25<slf4j.version>
-
</properties>
如果导入的方式要实现版本的升级,达到上面的效果,这样也可以做到,把要升级的组件依赖放到Spring Boot之前。
-
<dependencyManagement>
-
<dependencies>
-
<!-- Override Spring Data release train provided by Spring Boot -->
-
<dependency>
-
<groupId>org.springframework.data</groupId>
-
<artifactId>spring-data-releasetrain</artifactId>
-
<version>Fowler-SR2</version>
-
<scope>import</scope>
-
<type>pom</type>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework.boot</groupId>
-
<artifactId>spring-boot-dependencies</artifactId>
-
<version>1.5.6.RELEASE</version>
-
<type>pom</type>
-
<scope>import</scope>
-
</dependency>
-
</dependencies>
-
</dependencyManagement>
Each Spring Boot release is designed and tested against a specific set of third-party dependencies. Overriding versions may cause compatibility issues.
需要注意,要修改Spring Boot的依赖组件版本可能会造成不兼容的问题。
2. 资源文件过滤问题
使用继承Spring Boot时,如果要使用Maven resource filter过滤资源文件时,资源文件里面的占位符为了使${}和Spring Boot区别开来,此时要用@...@包起来,不然无效。另外,@...@占位符在yaml文件编辑器中编译报错,所以使用继承方式有诸多问题,坑要慢慢趟。
Spring Boot日志框架
Spring Boot支持Java Util Logging,Log4j2,Lockback作为日志框架,如果你使用starters启动器,Spring Boot将使用Logback作为默认日志框架。无论使用哪种日志框架,Spring Boot都支持配置将日志输出到控制台或者文件中。
spring-boot-starter启动器包含spring-boot-starter-logging启动器并集成了slf4j日志抽象及Logback日志框架。
属性配置日志
Spring Boot支持属性配置日志参数,这个不是很灵活,不细讲。
参考配置:
-
# LOGGING
-
logging.config= # Location of the logging configuration file. For instance `classpath:logback.xml` for Logback
-
logging.exception-conversion-word=%wEx # Conversion word used when logging exceptions.
-
logging.file= # Log file name. For instance `myapp.log`
-
logging.level.*= # Log levels severity mapping. For instance `logging.level.org.springframework=DEBUG`
-
logging.path= # Location of the log file. For instance `/var/log`
-
logging.pattern.console= # Appender pattern for output to the console. Only supported with the default logback setup.
-
logging.pattern.file= # Appender pattern for output to the file. Only supported with the default logback setup.
-
logging.pattern.level= # Appender pattern for log level (default %5p). Only supported with the default logback setup.
-
logging.register-shutdown-hook=false # Register a shutdown hook for the logging system when it is initialized.
如:
-
logging.level.root=DEBUG
-
logging.level.org.springframework.web=DEBUG
-
logging.level.org.hibernate=ERROR
自定义日志文件
根据不同的日志框架,默认加载的日志配置文件的文件名,放在资源根目录下,其他的目录及文件名不能被加载。
Logging System | Customization |
---|---|
Logback | logback-spring.xml, logback-spring.groovy, logback.xml or logback.groovy |
Log4j2 | log4j2-spring.xml or log4j2.xml |
JDK (Java Util Logging) | logging.properties |
既然默认自带了Logback框架,Logback也是最优秀的日志框架,往资源目录下创建一个logback-spring.xml即可,下面是一个参考配置文件。
-
<?xml version="1.0" encoding="UTF-8"?>
-
<configuration debug="false">
-
-
<springProperty scope="context" name="APP_NAME" source="spring.application.name"/>
-
<springProperty scope="context" name="APP_PORT" source="server.port"/>
-
<springProperty scope="context" name="DEFAULT_APP_PORT" source="spring.application.port"/>
-
-
<property name="OS_NAME" value="${os.name}"/>
-
<if condition='property("OS_NAME").contains("Windows")'>
-
<then>
-
<property name="LOG_PATH" value="${LOG_PATH:-E:/logs}" />
-
</then>
-
<else>
-
<property name="LOG_PATH" value="${LOG_PATH:-/log}" />
-
</else>
-
</if>
-
-
<property name="LOG_DIR" value="${APP_NAME:-system}" />
-
<property name="APP_PORT" value="${APP_PORT:-${DEFAULT_APP_PORT:-0}}" />
-
<if condition='!property("APP_PORT").equals("0")'>
-
<then>
-
<property name="LOG_DIR" value="${LOG_DIR}-${APP_PORT}" />
-
</then>
-
</if>
-
-
-
<!-- 控制台输出 -->
-
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
-
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
-
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度,%msg:日志消息,%n是换行符 -->
-
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
-
</encoder>
-
</appender>
-
-
<!-- 按照每天生成日志文件 -->
-
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
-
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
-
<level>INFO</level>
-
</filter>
-
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
-
<!--日志文件输出的文件名 -->
-
<FileNamePattern>${LOG_PATH}/${LOG_DIR}/info.log.%d{yyyy-MM-dd}.log</FileNamePattern>
-
-
<!--日志文件保留天数 -->
-
<MaxHistory>30</MaxHistory>
-
</rollingPolicy>
-
-
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
-
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
-
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
-
</encoder>
-
-
<!--日志文件最大的大小 -->
-
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
-
<MaxFileSize>10MB</MaxFileSize>
-
</triggeringPolicy>
-
-
</appender>
-
-
<!-- 按照每天生成日志文件 error级别 -->
-
<appender name="FILE-ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
-
<filter class="ch.qos.logback.classic.filter.LevelFilter">
-
<level>ERROR</level>
-
<onMatch>ACCEPT</onMatch>
-
<onMismatch>DENY</onMismatch>
-
</filter>
-
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
-
<!--日志文件输出的文件名 -->
-
<FileNamePattern>${LOG_PATH}/${LOG_DIR}/error.log.%d{yyyy-MM-dd}.log</FileNamePattern>
-
-
<!--日志文件保留天数 -->
-
<MaxHistory>30</MaxHistory>
-
</rollingPolicy>
-
-
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
-
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
-
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
-
</encoder>
-
-
<!--日志文件最大的大小 -->
-
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
-
<MaxFileSize>10MB</MaxFileSize>
-
</triggeringPolicy>
-
-
</appender>
-
-
<!--myibatis log configure -->
-
<logger name="com.apache.ibatis" level="TRACE" />
-
<logger name="java.sql.Connection" level="DEBUG" />
-
<logger name="java.sql.Statement" level="DEBUG" />
-
<logger name="java.sql.PreparedStatement" level="DEBUG" />
-
-
<!-- 日志输出级别 -->
-
<root level="INFO">
-
<appender-ref ref="STDOUT" />
-
<appender-ref ref="FILE" />
-
<appender-ref ref="FILE-ERROR" />
-
</root>
-
-
</configuration>
强烈推荐使用logback-spring.xml作为文件名,因为logback.xml加载太早。
日志初始化在ApplicationContext创建之前,所以@PropertySources加载的配置是读取不到的,系统环境变量、Spring Environment及application,bootstrap配置文件中的信息可以读取到。
读取系统环境属性:
-
<property name="LOG_PATH" value="${LOG_PATH:-E:/logs}" />
读取当前应用Environment中的属性:
-
<springProperty scope="context" name="fluentHost" source="myapp.fluentd.host"
-
defaultValue="localhost"/>
Spring Boot也支持通过springProfile来加载不同profiles下的配置。
-
<springProfile name="staging">
-
<!-- configuration to be enabled when the "staging" profile is active -->
-
</springProfile>
-
-
<springProfile name="dev, staging">
-
<!-- configuration to be enabled when the "dev" or "staging" profiles are active -->
-
</springProfile>
-
-
<springProfile name="!production">
-
<!-- configuration to be enabled when the "production" profile is not active -->
-
</springProfile>