Part III. Using Spring Boot
文档说明:
- 文档对应的版本为 2.1.0.M3
- 这不是文档的完整中文翻译,也有可能跟原文文字不一一对应,只是我阅读文档时候做的简单笔记
- 如果对应的章节没有任何中文,有可能是文档内容比较少,建议直接看原文,或者是我不感兴趣的部分
- 目录标题没有做翻译,首先标题一般一眼就能看懂什么意思,不做翻译还能保证原文意思,其次也方便对应到原文位置
这个篇章主要介绍 Spring Boot 更详细的特性,包括构建系统,自动配置和部署,最后介绍一些最佳实践。
13. Build Systems
强烈建议使用构建工具:Maven 或者 Gradle
13.1 Dependency Management
每个 Spring Boot 的发行版本都会提供一个可支持的依赖列表。在实际处理依赖的时候,你不需要提供版本号,版本号也由 Spring Boot 管理了。当你更新了 Spring Boot 的版本,相关的依赖也会更新对应的版本。
你仍然可以覆盖 Spring Boot 的推荐版本来实现依赖指定的版本,但是 Spring Framework 的版本号就不要随意更改了。
13.2 Maven
如果 Maven 用户配置依赖了 spring-boot-starter-parent 项目,那么会继承其默认约束:
- Java 1.8 的编译器
- UTF-8 源代码文件的编码格式
- 一个依赖管理的片段:管理各依赖版本的默认配置
- repackage 的 Maven 任务
- 智能的 resource 过滤
- 智能的插件配置
- 智能地处理多环境的配置文件:例如 application-dev.properties 和 applicaton-dev.yml
13.2.1 Inheriting the Starter Parent
配置 pom.xml 时候,Spring Boot 需要指定版本,如果是其他的 starter 和依赖,则可以忽略版本号。
<!-- Inherit defaults from Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.M3</version>
</parent>
[Spring Boot dependencies pom][spring-boot-dependencies-pom] 支持的属性配置和依赖的默认版本查看
13.2.2 Using Spring Boot without the Parent POM
并不是每个人都喜欢继承 spring-boot-starter-parent POM,或者你已经有自己的 parent POM,这种情况你需要显示配置你的 Maven 配置。
在不继承 spring-boot-starter-parent POM 的情况下,你可以通过 scope=import 依赖来获取 Spring Boot 的依赖管理。
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.0.M3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
上面例子里面,如果你在 dependency 下面继续添加你的个人依赖,你配置的版本号是不会生效的。为了达到自己配置第三方库版本号的效果,你需要在配置 spring-boot-dependencies 之前就配置好第三方库版本。例如,指定 Spring Data release train 库的版本号如下所示:
<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>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.0.M3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
13.2.3 Using the Spring Boot Maven Plugin
Spring Boot 提供了一个打包成可执行的 jar 包的 Maven 插件,无论你是否继承 spring-boot-starter-parent,都需要添加如下配置
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
13.3 Gradle
详情可查看《Spring Boot Gradle Plugin Reference Guide》文档
Spring Boot 还写了一个 jar 包,详情可查看其 API
13.4 Ant
可以通过 Apache Ant 和 Lvy 来构建 Spring Boot 项目。spring-boot-antlib 也可以帮助 Ant 来构建出可执行的 jar。
13.5 Starters
Startes 是依赖的描述器,代表了一组依赖,使用的时候非常方便。通过 Starters,你可以得到 SPring 和其相关技术的一站式服务,而不需要自己翻阅代码和配置详细的依赖。例如你想使用 Spring 和 JPA 来实现数据库访问,只需要配置 spring-boot-starter-data-jpa 依赖即可。
Spring Boot Starters 的官方命名都有一定的格式 spring-boot-starter-*
,这可以和 IDE 的自动补全结合,方便开发者编码。由于你可以创建自己的 Starter,你的 Starter 名称建议命名的时候项目名称放在前面,例如 yourproject-spring-boot-starter。
org.springframework.boot 可配置的 Starters 列表。
其他社区贡献的 Starters 可以参看 GitHub 的 README file。
14. Structuring Your Code
运行 Spring Boot 不需要特定的代码结构,然而,有些组织代码的建议。
14.1 Using the "default" Package
你的所有类都建议放到一个特定的 package 里面,没有指定 package 的类会归置到 default package,这会在使用 @Component,@EntityScan,@SpringBootApplication 的时候会触发一些问题。
14.2 Locating the Main Application Class
我们一般建议你把启动类放在 package 的根目录,启动类一般是带有 main() 方法和 @SpringBootApplication 修饰的类。
如果不想使用 @SpringBootApplication,可以用 @EnableAutoConfiguration 和 @ComponentScan 来替代。
下面是一个代码结构的例子
com
+- example
+- myapplication
+- Application.java
|
+- customer
| +- Customer.java
| +- CustomerController.java
| +- CustomerService.java
| +- CustomerRepository.java
|
+- order
+- Order.java
+- OrderController.java
+- OrderService.java
+- OrderRepository.java
15. Configuration Classes
Spring Boot 偏向于使用 Java-based 配置。虽然有可能在使用 SpringApplication 的过程中使用 XML 配置,但是我们一般建议你的主配置类是单独的 @Configuration 类。通常来说,这个主配置类一般还会定义 main 方法。
Tip:很多已经发布到网上的 Spring 应用例子都是使用 XML 配置的,如果可以的话,尽量使用对应的 Java-based 配置。可以尝试查找 Enable* 的注解。
15.1 Importing Additional Configuration Classes
你没有必要把所有的 @Configuration 都放到一个类里面,通过 @Import 可以引入其他的 @Configuration 类。或者,你也可以使用 @ComponentScan 来自动扫描所有 Spring 组件,包括 @Configuration 类。
15.2 Importing XML Configuration
如果你一定要使用 XML 配置,我们建议你的主配置类还是 @Configuration 类,然后通过 @ImportResource 注解来加载 XML 配置文件。
16. Auto-configuration
Spring Boot 自动装配会根据你应用的 jar 依赖来自动配置你的 Spring 应用。例如,如果 HSQLDB 在 classpath 上,而你还没有手动配置任何的数据库连接类,那么 Spring Boot 会自动装配一个嵌入式的数据库。
你需要选择是否需要这个自动装配功能,在你其中的一个 @Configuration 类是否添加 @EnableAutoConfiguration 或者 @SpringBootApplication 注解。
16.1 Gradually Replacing Auto-configuration
Auto-configuration 是非侵入性的。在任何时候,你可以定义自己的配置来替代 auto-configuration 的特定配置。例如,你添加了自己的 DataSource bean,那么默认的嵌入式数据库就不会再启用。
如果你需要了解 auto-configuration 都做了哪些默认配置,你可以在启动应用的时候加上 --debug 参数,这样就可以在日志或者控制台上查看了。
16.2 Disabling Specific Auto-configuration Classes
如果你不想使用某个 auto-configuration 类,可以在 @EnableAutoConfiguration 注解添加 exclude 属性来禁用这个类,代码示例如下:
import org.springframework.boot.autoconfigure.*;
import org.springframework.boot.autoconfigure.jdbc.*;
import org.springframework.context.annotation.*;
@Configuration
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class MyConfiguration {
}
如果这个类不在 classpath 上,可以配置其 excludeName 属性,内容就填写这个类的完整类名。最后,你还可以通过 spring.autoconfigure.exclude 配置项来配置一个禁用类的列表。
17. Spring Beans and Dependency Injection
你可以使用任意 Spring 的特性来定义你的 beans 和它们的注入依赖。为简单起见,我们经常发现使用 @ComponentScan 和 @Atuowired 都没有问题。
如果你的代码如上面建议一样,可以在根目录找到主配置类,你在添加 @ComponentScan 的时候可以不用设置参数。你应用的所有组件 (@Component,
@Service, @Repository, @Controller etc.) 都会被自动注册为 Spring Beans。
给个 @Service 的例子:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class DatabaseAccountService implements AccountService {
private final RiskAssessor riskAssessor;
@Autowired
public DatabaseAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
}
// ...
}
如果你的类只有一个构造方法,你还可以省略 @Autowired 注解,如下所示:
@Service
public class DatabaseAccountService implements AccountService {
private final RiskAssessor riskAssessor;
public DatabaseAccountService(RiskAssessor riskAssessor) {
this.riskAssessor = riskAssessor;
}
// ...
}
18. Using the @SpringBootApplication Annotation
很多 Spring Boot 开发者都愿意他们的应用使用到自动装配,组件扫描和定义更多额外的属性。一个 @SpringBootApplication 注解可以被用来启用这三个特性:
- @EnableAutoConfiguration:启用 Spring Boot 的自动装配机制
- @ComponentScan:启用组件扫描
- @Configuration:允许在 context 注册 bean 或者引入额外的配置类
这个 @SpringBootApplication 注解就等于 @Configuration, @EnableAutoConfiguration 和 @ComponentScan 注解的效果,如下所示:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication // same as @Configuration @EnableAutoConfiguration @ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication 也提供了别名来配置 @EnableAutoConfiguration 和 @ComponentScan 相关的属性。
这些特性都不是强制需要配置的,你可以其他相同功能的注解来替换 @SpringBootApplication 注解来实现你的需求。例如,你不想要组件扫描的功能,代码如下:
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@EnableAutoConfiguration
@Import({ MyConfig.class, MyAnotherConfig.class })
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
19. Running Your Application
把你的应用打包成 jar 和内嵌的 HTTP 服务,其中一个最大的好处就是你可以很方便地运行你的应用,而不需要特殊的 IDE 或其他插件。
当然,你也可以打包成 war 文件,你需要查阅其他的文档。
19.1 Running from an IDE
找到 "Run as a Java Application" 的菜单并选择执行
19.2 Running as a Packaged Application
如果你有使用 Maven 或者 Gradle 构建工具生成了一个可执行的 jar 文件,你可以通过 java -jar 来运行应用。
$ java -jar target/myapplication-0.0.1-SNAPSHOT.jar
也可以启用远程调试
$ java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n -jar target/myapplication-0.0.1-SNAPSHOT.jar
19.3 Using the Maven Plugin
Spring Boot Maven 插件包含了一个 run 程序,用来快速编译和运行你的应用。
$ mvn spring-boot:run
你也可以使用 MAVEN_OPTS 来操作系统环境变量
$ export MAVEN_OPTS=-Xmx1024m
19.4 Using the Gradle Plugin
Spring Boot Gradle 插件包含了一个 bootRun 程序。
$ gradle bootRun
你也可以使用 JAVA_OPTS 来操作系统环境变量
$ export JAVA_OPTS=-Xmx1024m
19.5 Hot Swapping
由于 Spring Boot 应用只是普通的Java应用程序,所以 JVM Hot Swapping 可以实现。JVM Hot Swapping 在某种程度上只能做到替换字节码文件,如果需要一个完整的解决方案,可以参考一下 JRebel。
spring-boot-devtools 模块也提供了一些可以快速重启应用的支持,可以查看 Chapter 20,或者 Chapter 89 的 Hot Swapping。
20. Developer Tools
Spring Boot 提供了一个可以用来提升应用开发体验的工具集 spring-boot-devtools,这个工具集默认是不启用的,有需要可以添加如下配置
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
20.1 Property Defaults
缓存在生产环境是很有用的,但是在开发环境却相反,因为你有可能不能及时看到你修改代码后的效果,所以 spring-boot-devtools 默认不启用缓存。
可以配置 application.properties 的 spring.thymeleaf.cache 项来控制缓存。
spring.http.log-request-details:可以记录请求内容,处理的控制器和响应内容。
spring.devtools.addproperties:取消 DevTools 的默认配置。
更多默认的属性可以参看 DevToolsPropertyDefaultsPostProcessor
20.2 Automatic Restart
当 classpath 上面的文件发生改动时,DevTools 会自动重启应用。一般情况下,这对于快速获取反馈很有帮助。
DevTools 会监控 classpath 所有的资源文件,保存一个类文件或者在IDE操作 Build Project 都会触发 restart。
DevTools 和 LiveReload 搭配使用效果更好,LiveReload 在 20.3 节有详细介绍。如果有使用 JRebel,自动重启机制会不启用,DevTools 的其他功能,例如 LiveReload 和 property default 仍会生效。
DevTools 在重启应用的时候是通过 ApplicationContext's shutdown hook 来实现关闭应用,如果你有 SpringApplication.setRegisterShutdownHook(false)
的代码,重启机制会运行异常。
Restart vs Reload
Restart 机制是由 Spring Boot 通过两个 ClassLoader 来实现的,不需要依赖其他的组件。一些不会改动的类,例如第三库的 jars,会被加载到 Base ClassLoader。那些你正在开发的代码类文件,会被加载到 Restart ClassLoader。当触发 Restart 时,Restart ClassLoader 会被重新创建,Base ClassLoader 不做改动。这个机制会让其 Restart 操作比冷启动会快很多。
如果你觉得上面的机制还是不够快,你可以借助其他组件(例如 JRebel)来实现 Reload。替换其对应的 class 字节码文件来实现。
20.2.1 Logging changes in condition evaluation
每次重启后,DevTools 默认会记录增量变化的报告,可通过下面的配置项来禁用
spring.devtools.restart.log-condition-evaluation-delta=false
20.2.2 Excluding Resources
想对某些文件修改后不触发重启操作,可以参看下面的配置
# 修改静态文件的默认配置
spring.devtools.restart.exclude=static/**,public/**
# 在原有的基础上增加例外文件的配置
spring.devtools.restart.additional-exclude=filePath*
20.2.3 Watching Additional Paths
配置 spring.devtools.restart.additionalpaths 项
一般会和 spring.devtools.restart.exclude 配置项搭配使用
20.2.4 Disabling Restart
配置 spring.devtools.restart.enabled 项来不启用重启机制,这种情况下,Restart ClassLoader 还是会被初始化,只是他不会在监控任何文件的改动。
如果想完全禁用重启机制,那么在调用 SpringApplication.run() 方法前设置其变量为 false。
public static void main(String[] args) {
System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(MyApp.class, args);
}
20.2.5 Using a Trigger File
你经常使用 IDE 来编码,可能更希望在特定的时机重启,可以通过监控一个特殊文件来实现。
配置 spring.devtools.restart.trigger-file 项
20.2.6 Customizing the Restart Classloader
上面的章节有描述 Restart ClassLoader 和 Base ClassLoader,正常情况下,你在 IDE 的代码都会被加载到 Restart ClassLoader,如果你在 IDE 里面有多个模块,而有些模块是不经常改动,那么你可以通过添加 META-INF/spring-devtools.properties 文件来配置特殊的逻辑。
spring-devtools.properties 文件的配置内容都必须以 restart.exclude 或者 restart.include 开头,restart.exclude 表示加载到 Base ClassLoader,restart.exclude 表示加载到 Restart ClassLoader。
restart.exclude.companycommonlibs=/mycorp-common-[\\w-]+\.jar
restart.include.projectcommon=/mycorp-myproj-[\\w-]+\.jar
20.2.7 Known Limitations
重启功能在某些场景下运行不是很好,例如在用 ObjectInputStream 做对象转换的时候。
20.3 LiveReload
spring-boot-devtools 模块有一个嵌入式的 LiveReload 服务,这个服务可以用来在资源文件改变后触发浏览器刷新页面。LiveReload 浏览器插件可以从 livereload.com 获取,支持 Chrome,Firefox 和 Safari。
如果不想启用 LiveReload 服务,把 spring.devtools.livereload.enabled 属性配置为 false 即可。
同一时间你只能运行一个 LiveReload 服务,所以在启动你的应用之前,确认是否有其他的 LiveReload 服务在运行。如果你在 IDE 启动多个应用,只有第一个启动的应用可以正常使用 LiveReload 服务。
20.4 Global Settings
在用户目录添加 ~/.spring-boot-devtools.properties
文件,里面的配置会对这台机器上面所有的 Spring Boot 应用都会生效。
20.5 Remote Applications
spring-boot-devtools 并不只是限制在本地使用,还有一些功能是针对远程应用的。远程支持的功能是可选的,若要启用这个功能,依赖配置如下:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludeDevtools>false</excludeDevtools>
</configuration>
</plugin>
</plugins>
</build>
还需要设置 spring.devtools.remote.secret 属性,例如:
spring.devtools.remote.secret=mysecret
Warning: 启用 spring-boot-devtools 的远程功能会有安全风险,生产环境不能开启该功能。
远程功能包含两个部分:一个是接收连接的服务端和一个在你 IDE 运行的客户端。当 spring.devtools.remote.secret 属性被设置有值的时候,服务端会自动启用该功能,客户端需要手动启动。
20.5.1 Running the Remote Client Application
远程功能的客户端就是被设计在 IDE 运行的。在客户端,你需要使用和远程项目相同的 classpath 来运行org.springframework.boot.devtools.RemoteSpringApplication。客户端应用唯一需要的参数就是它要连接远程项目的 URL。
例如,你在使用 Eclipse 或者 STS,有一个项目 my-app ,部署到了 Cloud Foundry,那你需要进行如下操作:
- Select Run Configurations… from the Run menu.
- Create a new Java Application “launch configuration”.
- Browse for the my-app project.
- Use org.springframework.boot.devtools.RemoteSpringApplication as the main class.
- Add https://myapp.cfapps.io to the Program arguments (or whatever your remote URL is).
一个运行远程功能的客户端启动日志大概如下:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ ___ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | | _ \___ _ __ ___| |_ ___ \ \ \ \
\\/ ___)| |_)| | | | | || (_| []::::::[] / -_) ' \/ _ \ _/ -_) ) ) ) )
' |____| .__|_| |_|_| |_\__, | |_|_\___|_|_|_\___/\__\___|/ / / /
=========|_|==============|___/===================================/_/_/_/
:: Spring Boot Remote :: 2.1.0.M3
2015-06-10 18:25:06.632 INFO 14938 --- [ main] o.s.b.devtools.RemoteSpringApplication :
spring-boot-devtools/target/classes started by pwebb in /Users/pwebb/projects/spring-boot/code/springboot-
samples/spring-boot-sample-devtools)
2015-06-10 18:25:06.671 INFO 14938 --- [ main] s.c.a.AnnotationConfigApplicationContext :
Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2a17b7b6: startup
date [Wed Jun 10 18:25:06 PDT 2015]; root of context hierarchy
2015-06-10 18:25:07.043 WARN 14938 --- [ main] o.s.b.d.r.c.RemoteClientConfiguration : The
connection to http://localhost:8080 is insecure. You should use a URL starting with 'https://'.
2015-06-10 18:25:07.074 INFO 14938 --- [ main] o.s.b.d.a.OptionalLiveReloadServer :
LiveReload server is running on port 35729
2015-06-10 18:25:07.130 INFO 14938 --- [ main] o.s.b.devtools.RemoteSpringApplication :
Started RemoteSpringApplication in 0.74 seconds (JVM running for 1.105)
Note: 因为客户端使用的 classpath 和真正的应用是一样的,所以它能直接读取到应用的属性配置。这就是 spring.devtools.remote.secret 配置项如何被读取并传给服务端认证的原理。
Tip: 建议使用 https 协议作为传输协议,那么传输内容是加密的,所以密码等信息就不会被拦截。
Tip: 如果你通过代理来访问服务端,那么需要配置 spring.devtools.remote.proxy.host 和 spring.devtools.remote.proxy.port 这两个属性。
20.5.2 Remote Update
客户端会监控你应用 classpath 的文件变化,这个本地重启(20.2节 Automatic Restart)的机制一样。所有的文件更新都会推送到服务端,必要时会触发重启操作。这在云服务上迭代开发一个功能的场景下会非常有帮助,因为通常来说,服务端的更新和重启会比全部重启和循环部署快很多。
Note: 文件只有在客户端运行时才会被监控,如果你在启动客户端前修改的文件,这个变化便不会推送到服务端。
21. Packaging Your Application for Production
Executable jar 可以用来发布到生产环境。由于他们都自带容器,所以非常适用于基于云平台的部署。
更多关于可用于生产的功能,可以考虑添加 spring-boot-actuator
库,可参看文档 Part V, “Spring Boot Actuator: Production-ready features” 。
22. What to Read Next
你现在应该知道如何使用 Spring Boot 和应该遵循的一些最佳实践,可以继续深入了解 Spring Boot 特定的功能,或者你跳过这部分去了解关于生产部署方面的功能。