本节将深入介绍Spring Boot的细节。在这里,你可以了解你想要使用和定制的关键特性。如果还没有,你可能需要阅读第2部分,“开始”和第3部分,“使用Spring引导”部分,这样你就可以很好地了解基础知识。

SpringApplication

SpringApplication类提供了一种方便的方法来引导一个从main()方法启动的Spring应用程序。在许多情况下,你可以将其委托给静态SpringApplication.run方法:

1
2
3
public static void main(String[] args) {
SpringApplication.run(MySpringConfiguration.class, args);
}

当你的应用程序启动时,你应该会看到类似的东西:

1
2
3
4
5
6
7
8
9
10
11
12
  .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: v2.0.0.BUILD-SNAPSHOT

2013-07-31 00:08:16.117 INFO 56603 --- [ main] o.s.b.s.app.SampleApplication : Starting SampleApplication v0.1.0 on mycomputer with PID 56603 (/apps/myapp.jar started by pwebb)
2013-07-31 00:08:16.166 INFO 56603 --- [ main] ationConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6e5a8246: startup date [Wed Jul 31 00:08:16 PDT 2013]; root of context hierarchy
2014-03-04 13:09:54.912 INFO 41370 --- [ main] .t.TomcatServletWebServerFactory : Server initialized with port: 8080
2014-03-04 13:09:56.501 INFO 41370 --- [ main] o.s.b.s.app.SampleApplication : Started SampleApplication in 2.992 seconds (JVM running for 3.658)

默认INFO日志信息将显示出来,包括启动应用程序的用户等相关的启动细节。

启动失败

如果你的应用程序无法启动,注册的FailureAnalyzers就有机会提供一个专门的错误消息和一个具体的操作来解决这个问题。例如,如果你在端口8080上启动一个web应用程序,并且该端口已经在使用,你应该会看到类似的东西:

1
2
3
4
5
6
7
8
9
10
11
***************************
APPLICATION FAILED TO START
***************************

Description:

Embedded servlet container failed to start. Port 8080 was already in use.

Action:

Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.

Spring Boot提供了无数的FailureAnalyzer实现,并且你可以很容易地添加自己的实现。

如果没有失败分析程序能够处理这个异常,你仍然可以显示完整的自动配置报告,以更好地理解出错的地方。所以你需要启用debug属性或为org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer启用DEBUG日志记录。

例如,如果你正在使用java-jar运行你的应用程序,你可以启用以下的debug属性:

1
$ java -jar myproject-0.0.1-SNAPSHOT.jar --debug

自定义Banner

在启动时打印的banner可以通过添加一个banner.txt文件到类路径或者通过设置banner.location指向该文件的位置来改变。如果这个文件有不同寻常的编码,你可以设置banner.charset(默认为UTF-8)。除了文本文件之外,你还可以添加一个banner.gif,banner.jpgbanner.png图像文件到类路径,或者设置一个banner.image.location属性。图像将被转换成ASCII字符,并在任何文本banner上打印。

在你的banner.txt文件中你可以使用以下占位符:

变量 描述
${application.version} MANIFEST.MF中声明的应用程序的版本号。例如,Implementation-Version: 1.0被打印为1.0
${application.formatted-version} MANIFEST.MF中声明的应用程序的格式化版本号(用括号括起来,用v作前缀)。例如(v1.0)
${spring-boot.version} 你正在使用的Spring Boot版本。例如2.0.0.BUILD-SNAPSHOT
${spring-boot.formatted-version} 你正在使用的Spring Boot版本格式化输出形式(用括号括起来,用v作前缀)。例如(v2.0.0.BUILD-SNAPSHOT)
${Ansi.NAME} (or ${AnsiColor.NAME}, ${AnsiBackground.NAME}, ${AnsiStyle.NAME}) NAME是ANSI转义码的名字。有关详细信息,请参阅AnsiPropertySource
${application.title} MANIFEST.MF中声明的应用程序的标题。例如,Implementation-Title: MyApp被打印为MyApp

SpringApplication.setBanner(…)方法可以用来以编程方式生成一个banner 。使用org.springframework.boot.Banner接口,并实现您自己的printBanner()方法。

你还可以使用spring.main.banner-mode属性,以确定是否必须在System.outconsole)打印banner,使用已配置的日志记录器(log)或根本不打印(off)。

打印的banner将以springBootBanner的名字注册为一个单例bean。

YAML映射offfalse,因此如果你想禁用应用程序中的banner,请确保添加引号。

1
2
3
spring:
main:
banner-mode: "off"

自定义SpringApplication

如果SpringApplication默认值不符合你的胃口,你可以创建一个本地实例并自定义它。例如,关闭banner:

1
2
3
4
5
public static void main(String[] args) {
SpringApplication app = new SpringApplication(MySpringConfiguration.class);
app.setBannerMode(Banner.Mode.OFF);
app.run(args);
}

传递给SpringApplication的构造函数参数是spring bean 的配置源。在大多数情况下,这些都是对@Configuration类的引用,但是它们也可以是对XML配置的引用,或者是对应该被扫描的包的引用。

还可以使用application.properties文件配置SpringApplication。请参阅第24章,扩展配置以获得详细信息。

关于配置选项的完整列表,请参阅SpringApplication Javadoc

流式builder API

如果你需要构建一个ApplicationContext层次结构(具有父/子关系的多个上下文),或者您更喜欢使用“流式的”构建器API,那么您可以使用SpringApplicationBuilder

SpringApplicationBuilder允许你将多个方法调用链接在一起,并包含允许你创建层次结构的parentchild方法。

例如:

1
2
3
4
5
new SpringApplicationBuilder()
.sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);

在创建ApplicationContext层次结构时,会有一些限制,例如,Web组件必须包含在子上下文中,并且父子上下文将使用相同的环境变量Environment。请参阅SpringApplicationBuilder Javadoc来获得完整的详细信息。

事件和监听器

除了通常的Spring Framework 事件,如 ContextRefreshedEvent,一个SpringApplication会发送一些额外的应用事件。

有些事件实际上是在创建ApplicationContext之前触发的,因此你不能将这些监听器注册为一个@Bean。你可以通过SpringApplication.addListeners(…)SpringApplicationBuilder.listeners(…)方法来注册。如果你希望这些侦听器能自动注册,不管应用程序是如何创建的,可以添加一个META-INF/spring.factories文件到项目中并使用org.springframework.context.ApplicationListener key来引用它们。

1
org.springframework.context.ApplicationListener=com.example.project.MyListener

应用事件按照以下顺序发送:

  1. 一个ApplicationStartingEvent是在开始运行时发送的,但是在任何处理之前,除了监听器和初始化器的注册。
  2. 在上下文中使用的Environment已知但在创建上下文之前,将发送ApplicationEnvironmentPreparedEvent
  3. 一个ApplicationPreparedEvent是在刷新启动之前发送的,但是在bean定义被加载之后。
  4. 一个ApplicationReadyEvent是在刷新之后发送的,并且已经处理了任何相关的回调,以表明应用程序已经准备好处理请求。
  5. 如果在启动时出现异常,就会发送ApplicationFailedEvent

通常不需要使用应用程序事件,但是知道它们的存在可以做到得心应手。在内部,Spring Boot使用事件来处理各种任务。

Web 环境

SpringApplication将尝试为你创建合适的ApplicationContext。默认情况下,将使用一个AnnotationConfigApplicationContextAnnotationConfigServletWebServerApplicationContext ,这取决于你是否正在开发一个web应用程序。

用于确定“web environment”的算法相当简单(基于是否存在几个类)。如果需要覆盖缺省值,可以使用setWebEnvironment(boolean webEnvironment)

也可以完全控制将通过调用setApplicationContextClass(...)使用的ApplicationContext类型。

在JUnit测试中使用SpringApplication时,通常需要调用setWebEnvironment(false)

访问应用参数

如果需要访问传递给SpringApplication.run(...)的应用程序参数,则可以注入org.springframework.boot.ApplicationArguments bean。 ApplicationArguments接口提供对原始String []参数以及解析过的optionnon-option参数的访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.boot.*
import org.springframework.beans.factory.annotation.*
import org.springframework.stereotype.*

@Component
public class MyBean {

@Autowired
public MyBean(ApplicationArguments args) {
boolean debug = args.containsOption("debug");
List<String> files = args.getNonOptionArgs();
// if run with "--debug logfile.txt" debug=true, files=["logfile.txt"]
}

}

Spring Boot还将向Spring Environment注册一个CommandLinePropertySource。 这允许你也可以使用@Value注解注入单个应用参数。

使用ApplicationRunner,CommandLineRunner

当你需要在SpringApplication启动时运行一些特定的代码,你可以实现ApplicationRunnerCommandLineRunner接口。 这两个接口都以相同的方式工作,并提供一个单独的run方法,并在SpringApplication.run(...)完成之前调用。

CommandLineRunner接口提供对应用程序参数的访问,并将参数作为一个简单的字符串数组,而ApplicationRunner使用上述的ApplicationArguments接口。

1
2
3
4
5
6
7
8
9
10
11
import org.springframework.boot.*
import org.springframework.stereotype.*

@Component
public class MyBean implements CommandLineRunner {

public void run(String... args) {
// Do something...
}

}

如果几个CommandLineRunnerApplicationRunner bean 必须在一个特定的顺序被调用,你可以额外实现org.springframework.core.Ordered接口,也可以使用org.springframework.core.annotation.Order注解。

应用退出

每个SpringApplication将在JVM上注册一个关闭钩子,以确保ApplicationContext在退出时优雅地关闭。所有标准的Spring生命周期回调(例如DisposableBean ,或者@PreDestroy)都可以使用。

此外,如果希望在SpringApplication.exit()被调用时返回特定的退出代码,bean 可以实现org.springframework.boot.ExitCodeGenerator接口。这个退出代码可以传递给System.exit()以作为状态代码返回它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootApplication
public class ExitCodeApplication {

@Bean
public ExitCodeGenerator exitCodeGenerator() {
return () -> 42;
}

public static void main(String[] args) {
System.exit(SpringApplication
.exit(SpringApplication.run(ExitCodeApplication.class, args)));
}

}

此外,异常类可以实现ExitCodeGenerator接口。 遇到这样的异常时,Spring Boot将返回由它实现的getExitCode()方法提供的退出码。

管理员功能

可以通过指定spring.application.admin.enabled属性来为应用程序启用与管理相关的功能。 这会在MBeanServer平台上暴露SpringApplicationAdminMXBean。 你可以使用此功能远程管理你的Spring Boot应用程序。 这对于任何服务包装器实现也是有用的。

如果你想知道应用程序在哪个HTTP端口上运行,请使用local.server.port key来获取该属性。

启用此功能时请小心,因为MBean公开了关闭应用程序的方法。

扩展配置

Spring Boot 允许扩展配置,因此我们的应用可以在不同的环境中运行。可以使用properties文件,YAML文件,环境变量和命令行参数来扩展配置。属性值可以通过@Value注解直接注入到bean中,通过Spring环境变量Environment来访问或者通过@ConfigurationProperties注解绑定到结构化对象中。

Spring Boot用特定的PropertySource顺序来对属性值进行覆盖。顺序如下:

  1. 当开发者工具启用时,开发者工具全局设置(${home}/.spring-boot-devtools.properties,windows用户在C:\Users\qinzaizhen\目录中,qinzaizhen为用户名)
  2. 测试用例中@TestPropertySource注解添加进来的属性
  3. @SpringBootTest#properties添加的属性
  4. 命令行参数
  5. SPRING_APPLICATION_JSON添加的参数(环境变量或系统属性中嵌入的json)
  6. ServletConfig初始化参数
  7. ServletContext初始化参数
  8. java:comp/env JNDI属性
  9. Java系统属性System.getProperties()
  10. 操作系统环境变量
  11. random.*形式的RandomValuePropertySource
  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指定的默认属性

例如下面的一个类,需要注入name的值:

1
2
3
4
5
6
7
8
9
10
11
12
import org.springframework.stereotype.*
import org.springframework.beans.factory.annotation.*

@Component
public class MyBean {

@Value("${name}")
private String name;

// ...

}

在程序的classpath中可以用application.properties文件提供name的默认值。当在新环境中运行时,jar包外的application.properties可以覆盖name的值;在一次性的测试中,可以通过在控制台中指定个一个name的值(如:java -jar app.jar --name="Spring")。

SPRING_APPLICATION_JSON可以在控制台输入之后变成一个环境变量,比如在Unix shell中:

1
$ SPRING_APPLICATION_JSON='{"foo":{"bar":"spam"}}' java -jar myapp.jar

在这个例子中,Spring的Environment中将会是foo.bar=spam。也可以通过设置一个系统属性$ java -Dspring.application.json='{"foo":"bar"}' -jar myapp.jar,或者是控制台参数

$ java -jar myapp.jar –spring.application.json=’{“foo”:”bar”}’ 未成功,找不到这个属性,而且会导致上面的SPRING_APPLICATION_JSON环境变量失效

或者通过JNDI变量:java:comp/env/spring.application.json

比如环境变量的方式:

先设置环境变量,然后再运行

1
2
3
4
E:\springboot-demo\demo4\target>set SPRING_APPLICATION_JSON={"developer.name":"a
aaaaaaaaaa"}

E:\springboot-demo\demo4\target>java -jar springboot.demo4-1.0-SNAPSHOT.jar

控制台打印出:

1
2017-08-08 21:46:24.783 DEBUG 44644 --- [           main] l.q.springboot.demo.domain.AppListener   : 属性值(此处中文有乱码): aaaaaaaaaaa

配置随机值

RandomValuePropertySource用来注入随机值非常方便(比如注入到秘钥或测试用例中)。可以产生int,long,uuid和字符串。比如:

1
2
3
4
5
6
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number.less.than.ten=${random.int(10)}
my.number.in.range=${random.int[1024,65536]}

random.int*格式是OPEN value (,max) CLOSE,这里的OPEN,CLOSE可以是任何的字符,value,max是整数。如果max有值,那么value就是最小值,而max就是最大值(不包括max)。**OPEN,CLOSE可以是任何字符,但是如果是上面一样作为属性值,不可使用{,}。可以是my.number.in.range=${random.int[1024,65536)},意义是一样的。如果是my.number.in.range=${random.int[1024,65536}},那么传到RandomValuePropertySource中的表达式为random.int[1024,65536,这样在算范围的时候就成了1024,6553**。

访问控制台参数

SpringApplication默认会将控制台选项参数(以--开头,如:--server.port=9000)转换为一个属性并且将它添加到Spring的环境变量中去。上面提到过控制台参数的优先级比其他的属性源高。
如果不想添加控制台属性添加到Spring环境变量中,可以通过SpringApplication.setAddCommandLineProperties(false)来禁用这个功能。

应用属性文件

SpringApplication会按照以下顺序加载application.properties文件,并且将它他们添加到Spring环境变量中。

  1. 当前目录的/config子目录
  2. 当前目录
  3. classpath下/config
  4. classpath根目录

如果不想用application.properties作为配置文件名,可以通过配置spring.config.name环境变量来更改。还可以明确通过spring.config.location环境变量指定配置文件的位置(多个目录或者文件路径用,隔开)。

1
java -jar myproject.jar --spring.config.name=myproject

或者:

1
java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties

spring.config.namespring.config.location在很早的时候用来决定哪些文件需要加载,因此它们必须得在环境变量中声明(常见的是操作系统的环境变量,系统属性或者控制台参数)。

如果spring.config.location包含目录(相对文件来说),需要以/结尾(并且在开始加载之前,会拼接spring.config.name指定的名称,包括特定profile的文件名)。spring.config.location中指定的文件还是跟原来一样,不支持特定profile变体,并且会被任何的特定profile属性覆盖。

配置文件是按照反方向的顺序查找的。配置文件的位置默认是classpath:/,classpath:/config/,file:./,file:./config/。查找的顺序是反的:

  1. file:./config/:jar包所在目录创建config目录和application.properties
  2. file:./:jar目录中创建application.properties
  3. classpath:/config/:resources目录中创建config目录和application.properties
  4. classpath:/:resources根目录创建application.properties

测试file:./config/,file:./这两个时,先打包成jar包,然后再在jar包同目录下创建config目录,里面新建application.properties文件,同理在jar包同目录下新建application.properties文件,两种情况分别测试。跟@SpringBootApplication注解的类放在同目录是无法测试这类情况的。
如:

1
2
myapp.jar
/config/application.properties

结果:

1
2
2017-08-12 10:37:45.677  INFO 17459 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@18ef96: startup date [Sat Aug 12 10:37:45 CST 2017]; root of context hierarchy
属性值:current directory /config

当使用自定义位置时,会添加到默认位置中。自定义位置会在默认位置之前查找。例如,如果自定义位置是classpath:/custom-config/,file:./custom-config/,那么查找的顺序是:

  1. file:./custom-config/: 在jar包目录创建对应的目录和文件(成功)
  2. classpath:/custom-config/ :在resources目录创建对应的目录和文件(未成功)
  3. file:./config/
  4. file:./
  5. classpath:/config/
  6. classpath:/

这种查找顺序允许你在某个文件中指定一些默认值,然后有在其他文件中有选择性地覆盖这些值。你可以在其中一个默认位置的application.properties(或者其他通过spring.config.name指定的名称)文件中指定默认值。这些默认值可以在运行的时候被其他任一自定义位置的不同文件覆盖。

如果使用环境变量而不是系统属性,大多数操作系统禁止点分隔的名称,但是可以使用下划线来代替(如:STRING_CONFIG_NAME代替spring.config.name (windows下测试有效))。

设置环境变量STRING_CONFIG_LOCATIONspring.config.location无效。

如果在容器中运行,JDNI属性(在java:comp/env中的)或者servlet上下文初始化参数可以代替环境变量或系统属性。

Profile-specific属性

application.properties文件之外,还可以定义application-{profile}.properties格式的特定profile的属性。当没有指定profile时,Environment会使用一些默认的profiles(默认为[default]),比如没有明确指定profile时,将会加载application-default.properties

特定profile的属性和标准的application.properties一样从相同的位置加载进来,同时特定profile会覆盖非特定profile的文件,不管这个特定profile的文件是不是在jar包中。

如果指定了多个profile,最后一个将会生效。例如:通过spring.profiles.active属性来指定的profile在那些通过SpringApplicationAPI 配置的profile后面被添加进来,因此会获取优先权。

如果在spring.config.location设置的任何文件中的特定profile文件将不会生效。如果spring.profiles.active指定的profile名称和spring.config.location中的profile名称一样,是会生效的,不一样的话则不会生效。
比如:spring.profiles.active=dev,则java -jar myapp.jar --spring.config.location=application-dev.properties会生效,但是java -jar myapp.jar --spring.config.location=application-pro.properties则不会生效。
如果想让它生效,可以在spring.config.location中使用目录(实验结果如上)。

属性文件中的Placeholders

application.properties文件中的值可以用Environment中存在的值过滤,因此可以引用之前定义过的值(比如:系统属性)。

1
2
app.name=MyApp
app.description=${app.name} is a Spring Boot application

可以使用这个特性来对一些存在的Spring Boot属性定义短变量。具体查看

YAML 代替属性文件

YAML是JSON的一个超集,也是一个非常方便设置分层配置数据的格式。当在classpath中发现了Snake YAML包时,SpringApplication类会自动支持YAML作为属性的一种可选方案。

如果使用spring-boot-starter,Snake YAML会自动加载进来。

加载YAML

Spring Framework 提供了两个方便的类用来加载YAML文档。YamlPropertiesFactoryBean将会把YAML加载为propertiesYamlMapFactoryBean将会加载为Map

例如下面的YAML文档:

1
2
3
4
5
6
7
environments:
dev:
url: http://dev.bar.com
name: Developer Setup
prod:
url: http://foo.bar.com
name: My Cool App

将会被转换成下面的属性:

1
2
3
4
environments.dev.url=http://dev.bar.com
environments.dev.name=Developer Setup
environments.prod.url=http://foo.bar.com
environments.prod.name=My Cool App

YAML中的list会表示成带[index]的key,比如这个YAML:

1
2
3
4
my:
servers:
- dev.bar.com
- foo.bar.com

将会被转换成:

1
2
my.servers[0]=dev.bar.com
my.servers[1]=foo.bar.com

使用SpringDataBinder工具(@ConfigurationProperties正好做这个事情)来绑定这样的属性需要在目标对象中定义java.util.List(或Set)属性并且需要提供一个setter方法或者通过一个可变的值实例化它,如下面的例子可以绑定上面的属性:

1
2
3
4
5
6
7
8
@ConfigurationProperties
public class Config{
private List<String> servers = new ArrayList<String>();

public List<String> getServers() {
return this.servers;
}
}

当通过上面的方式配置list覆盖时要格外注意,它不会像预想的那样工作,。在上面的例子中,当my.servers在多个地方重复定义了,每个元素都是被覆盖的对象,而不是该list。为了确保有最高优先级的@PropertySource可以覆盖list,需要将它定义为一个单独的属性。

1
2
my:
servers: dev.bar.com,foo.bar.com

(未明白是什么场景)

将YMAL作为属性暴露到Spring 环境中

YamlPropertySourceLoader类可以用来暴露YAML,将它作为Spring Environment的一个PropertySource。你可以使用熟悉的@Value注解来访问YAML属性。

多个profile 的YAML文件

可以通过spring.profiles key 在一个YAML文档中指定多个特定profile。比如:

1
2
3
4
5
6
7
8
9
10
11
12
server:
address: 192.168.1.100
---
spring:
profiles: development
server:
address: 127.0.0.1
---
spring:
profiles: production
server:
address: 192.168.1.120

在上面的例子中,当profile development激活时server.address属性将是127.0.0.1。如果developmentproduction profile未激活时,那么server.address的值将是192.168.1.100

在应用上下文启动时,如果没有明显地指定profile时,那么默认的profile将会激活。所以在下面的YAML中我们为security.user.password设置了一个值,仅在 “default” profile中可用。

1
2
3
4
5
6
7
8
server:
port: 8000
---
spring:
profiles: default
security:
user:
password: weak

然而在下面这个例子中,password一直都有值,因为它不属于任何profile,并且在需要的时候必须得在其他的profile中显示地进行重设。

1
2
3
4
5
server:
port: 8000
security:
user:
password: weak

Spring profile设计的spring.profiles元素可以使用!字符来否定。如果在一个文档中否定和非否定的profile被指定,至少必须匹配一个非否定的pfofile,否定的profile可以不匹配。

YAML 缺陷

YAML文件无法通过@PropertySource注解加载。如果想通过这种方式加载,需要使用properties文件。

合并YAML列表

在上面已经知道,YAML中的内容将会完全转换成properties。在profile中覆盖list时这个处理有可能与直觉背道而驰。例如,假设MyPojo对象有namedescription两个属性,并且默认为null。从FooProperties对象中暴露出MyPojo的一个list。

1
2
3
4
5
6
7
8
9
10
@ConfigurationProperties("foo")
public class FooProperties {

private final List<MyPojo> list = new ArrayList<>();

public List<MyPojo> getList() {
return this.list;
}

}

有以下配置:

1
2
3
4
5
6
7
8
9
10
foo:
list:
- name: my name
description: my description
---
spring:
profiles: dev
foo:
list:
- name: my another name

如果dev profile没有激活,那么FooProperties.list将会包含一个MyPojo。然而当dev profile 激活时,这个list仍然只有一个实体(name 为“my another name” ,description 为 null)。这样配置不会为这个list增加第二个MyPojo,而且不会合并元素。

当在多个pfofile中声明一个集合时,将会使用最高优先级的那个(并且只有这一个):

1
2
3
4
5
6
7
8
9
10
11
12
foo:
list:
- name: my name
description: my description
- name: another name
description: another description
---
spring:
profiles: dev
foo:
list:
- name: my another name

在这个例子中,考虑到profile dev激活时,FooProperties.list将会包含MyPojo实体(name 为“my another name” 并且 description 为null)。

类型安全的配置属性

在使用@value("${property}")注解注入配置属性时,有可能会很麻烦,尤其是如果使用多个属性或者数据具有层次性时。Spring Boot 提供了一个可选的方法来处理属性,它允许强类型控制并且校验这些配置。

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
45
46
47
48
49
50
package com.example;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("foo")
public class FooProperties {

private boolean enabled;

private InetAddress remoteAddress;

private final Security security = new Security();

public boolean isEnabled() { ... }

public void setEnabled(boolean enabled) { ... }

public InetAddress getRemoteAddress() { ... }

public void setRemoteAddress(InetAddress remoteAddress) { ... }

public Security getSecurity() { ... }

public static class Security {

private String username;

private String password;

private List<String> roles = new ArrayList<>(Collections.singleton("USER"));

public String getUsername() { ... }

public void setUsername(String username) { ... }

public String getPassword() { ... }

public void setPassword(String password) { ... }

public List<String> getRoles() { ... }

public void setRoles(List<String> roles) { ... }

}
}

上面的POJO定义了下面的这些属性:

  • foo.enabled,默认为false
  • foo.remote-address,能够从String强转过来。
  • foo.security.username,一个内部属性,名称由属性的名称决定。特别是返回类型完全不被使用而且可能是SecurityProperties
  • foo.security.password
  • foo.security.rolesString集合

get和set方法通常是必须要有的,因为绑定是通过标准的Java Bean 内省包,就像在Spring MVC里一样。有一些情况下set方法可以省略:

  • Maps,只要他们初始化了,需要一个get方法不需要set方法,因为他们可以被binder修改。
  • Collections和arrays可以通过下标(尤其是和YAML一起时)或者使用单行逗号分隔的值(属性)。在后一种情况下,set方法必须要有。强烈建议一直添加set方法。如果实例化了一个集合,确保可以修改(如上例)
  • 如果内部POJO属性初始化了(如上面例子中的Security属性),不需要set方法。如果想要binder通过它的默认构造函数来实例化,需要提供set方法。
    有人使用Lombok项目来自动生成get和set方法。确保Lombok没有为这些类型生成任何常规的构造方法,因为容器在实例化这种对象时会自动调用构造方法。

查看@Value@ConfigurationProperties之间的区别

还需要在@EnableConfigurationProperties注解中进行注册:

1
2
3
4
@Configuration
@EnableConfigurationProperties(FooProperties.class)
public class MyConfiguration {
}

@ConfigurationProperties bean 通过这种方式注册之后,这个bean会有一个方便的名称:<prefix>-<fqn>,这里的<prefix>@ConfigurationProperties中定义的环境的key 前缀,<fqn>是这个bean的全名称。如果注解没有提供任何的前缀,将会使用这个bean的全名称。上面例子中的bean名称为foo-com.exapmle.FooProperties

尽管上面的配置将会为FooProperties创建一个常规的bean,但是建议@ConfigurationProperties只处理跟环境相关的,特别是不从上下文中注册其他的bean。之前说过,@EnableConfigurationProperties注解也会自动地应用到项目中,因此任何现存的@ConfigurationProperties注解过的bean将会配置到环境中。可以简配上面的MyConfiguration通过确保FooProperties已经是一个bean。

1
2
3
4
5
6
7
@Component
@ConfigurationProperties(prefix="foo")
public class FooProperties {

// ... see above

}

这种配置方式特别适合@SpringApplication外面YAML配置:

1
2
3
4
5
6
7
8
9
10
11
# application.yml

foo:
remote-address: 192.168.1.1
security:
username: foo
roles:
- USER
- ADMIN

# additional configuration as required

要使用@ConfigurationProperties注解的bean,可以直接像其他bean一样将它们注入进来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class MyService {

private final FooProperties properties;

@Autowired
public MyService(FooProperties properties) {
this.properties = properties;
}

//...

@PostConstruct
public void openConnection() {
Server server = new Server(this.properties.getRemoteAddress());
// ...
}

}

使用@ConfigurationProperties还可以生成描述数据文件,这些文件可以被IDE用来自动提示。详情查看

第三方配置

除了使用@configurationproperties来注解一个类,还可以在public的@bean方法上使用它。当你想绑定属性到那些不受控制的第三方组件时这将会非常有用。
要在bean中使用Environment的属性,需要在bean注册的地方添加@ConfigurationProperties

1
2
3
4
5
@ConfigurationProperties(prefix = "bar")
@Bean
public BarComponent barComponent() {
...
}

任何以bar开头的属性都将会绑定到这个BarComponent bean 上,就像上面的FooProperties一样。

松散绑定

Spring Boot使用一些松散的规则来绑定Environment属性到@ConfigurationProperties bean上,因此Environment属性名称和bean的属性名称没有必要完全匹配。这些有用的例子包括虚线(如:context-path绑定到contextPath)和大写(如:PORT绑定到port)环境属性。

例如下面给出的@ConfigurationProperties类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@ConfigurationProperties(prefix="person")
public class OwnerProperties {

private String firstName;

public String getFirstName() {
return this.firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

}

下面所有的属性都可以使用:

属性名 解释
person.firstName 标准的驼峰形式
person.first-name 短横线隔开,在.properties.yml文件中推荐使用这种形式
person.first_name 下划线符号,.properties.yml文件中可选的一种形式
PERSON_FIRSTNAME 大写形式。推荐在系统环境变量中使用

注解的prefix值必须是短横线隔开的形式,如:小写并且以-分隔。这里如果只有person就没有必要隔开了。

松散绑定规则
属性来源 简单值 List值
Properties文件 驼峰形式,短横线隔开,下划线符号 []的标准list形式或者逗号隔开的值
YAML文件 驼峰形式,短横线隔开,下划线符号 标准的YAML list形式或者逗号隔开的值
环境变量 以下划线作为分隔符的大写形式。_不要在属性名中使用 下划线包围的数字形式。如:MY_FOO_1_BAR = my.foo[1].bar
系统属性 驼峰形式,短横线隔开,下划线符号 []的标准list形式或者逗号隔开的值

建议尽可能地用小写短横线格式来存储属性。如:my.property-name=foo

属性转换

在属性绑定到@configurationProperties bean的过程中,Spring会尝试强制转换为正确的类型。如果需要自定义类型转换,可以提供一个ConversionService bean(bean的id为conversionService)或者自定义属性编辑器(通过一个CustomEditorConfigurer bean),或者是自定义Converters(通过@ConfigurationPropertiesBinding注解)。

由于ConversionService在应用的生命周期中使用的非常早,因此需要确保减少它的依赖。典型地,你需要的任何依赖可能在创建时间没有完全初始化。如果ConversionService在配置key强转的过程中不需要,并且仅仅依赖于@ConfigurationPropertiesBinding限制的自定义转换器,你有可能想对它重命名。

@ConfigurationProperties 校验

Spring Boot会尝试校验那些使用了Spring的@Validated注解的@ConfigurationProperties类。可以直接在配置类上使用JSR-303 javax.validation约束注解。只需要确保在classpath中有一个兼容的JSR-303实现,然后再在类属性上加上约束注解就可以了。

如:

1
2
3
4
5
6
7
8
9
10
@ConfigurationProperties(prefix="foo")
@Validated
public class FooProperties {

@NotNull
private InetAddress remoteAddress;

// ... getters and setters

}

为了校验内部属性,必须在属性上使用Valid注解来触发校验。例如,下面的FooProperties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@ConfigurationProperties(prefix="connection")
@Validated
public class FooProperties {

@NotNull
private InetAddress remoteAddress;

@Valid
private final Security security = new Security();

// ... getters and setters

public static class Security {

@NotEmpty
public String username;

// ... getters and setters

}

}

还可以通过创建一个bean定义为configurationPropertiesValidator的方式增加一个自定义Spring Validator@Bean方法应该声明为static。配置属性校验器创建的时机在应用 的生命周期中非常早,因此将@Bean方法声明为static可以让这个bean的创建不需要实例化@Configuration。这避免了饥饿实例化引起的一些问题。

spring-boot-actuator模块包含了一个端点,暴露了所有的@ConfigurationProperties bean。在浏览器中访问/configprops或者使用相同的JSX端点。详情查看

@ConfigurationProperties 与 @Value对比

@Value是核心容器的一个功能,不提供@ConfigurationProperties 类似的类型安全的功能。下面的表格总结了两个注解支持的功能。

功能 @ConfigurationProperties @Value
不严格绑定 支持 不支持
元数据支持 支持 不支持
SpEL 表达式 不支持 支持

如果为自己的组件定义了一些配置键值,建议将他们放在一个POJO对象中,并且用@ConfigurationProperties注解。同时要注意由于@Value不支持松散绑定,因此当需要提供环境变量的值时最好不用@Value

最后,虽然可以用@Value编写SpEL表达式,但这些从应用程序属性文件中的表达式不会处理。

Profiles

Spring Profile提供了一种隔离部分应用配置的方式,同时使它只在某种确定的环境中可用。任何@Component@Configuration可以标记@Profile注解来限制它们被加载。

1
2
3
4
5
6
7
@Configuration
@Profile("production")
public class ProductionConfiguration {

// ...

}

在Spring常规的方式中,可以使用spring.profile.active Environment属性来指定激活哪个profile。可以在任何常规方式中指定这个属性,比如可以在application.properties文件中指定:

1
spring.profiles.active=dev,hsqldb

或者在控制台指定:--spring.profiles.active=dev,hsqldb

增加激活的profile

spring.profiles.active属性跟其他属性一样遵循相同的顺序规则,最高优先级的将会生效。意思是可以指定在application.properties文件中指定激活的profile,然后在控制台中替换掉它。

有时增加激活的profile而不是替换掉它们将会对特定profile属性非常有用。spring.profiles.include属性可以用来无条件地增加激活的profile。SpringApplication入口也拥胡一个API来设置附加的profile(如在那些通过spring.profiles.active属性设置的profile之上):setAdditionalProfiles()方法。

例如:当一个应用通过--spring.profiles.active=prod开关来运行时,proddbprodmq profile也会被激活:

1
2
3
4
5
6
7
---
my.property: fromyamlfile
---
spring.profiles: prod
spring.profiles.include:
- proddb
- prodmq

记住一点:spring.profiles属性可以定义在YAML文档中,用来决定什么时候这个特殊的文档被包含在配置中。更多查看 Change configuration depending on the environment

以编程方式设置profile

在应用启动之前可以通过调用SpringApplication.setAdditionalProfiles()方法以编程的方式来设置激活的profile。用Spring ConfigurableEnvironment接口也可以激活profile。

特定Profile配置文件

application.properties(或application.yml)和通过@ConfigurationProperties引用的文件中的特定profile变量都会被加载。详细查看Section 24.4, “Profile-specific properties”

日志

Spring Boot 内部使用 Commons Log记录日志,但是放开了日志实现。为Java Util LoggingLog4J2Logback提供了默认的配置。每个logger都预先配置了输出到控制台和选择输出到文件。

如果使用了”Starters”,默认会使用Logback来记录日志。还包括适当的Logback路由,以确保使用Java Util Logging、Commons Logging、Log4J或SLF4J的依赖库都能正常工作。

Java 有很多日志框架可选。不要对上面的选择很困惑。一般不需要改变日志的依赖,并且Spring Boot 默认就会工作地很好。

日志格式

Spring Boot默认的日志输出格式类似下面这种:

1
2
3
4
5
2014-03-05 10:57:51.112  INFO 45469 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/7.0.52
2014-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2014-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1358 ms
2014-03-05 10:57:51.698 INFO 45469 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2014-03-05 10:57:51.702 INFO 45469 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]

输出了以下内容:

  • 日期和时间:毫秒级精度,容易排序
  • 日志级别-ERROR,WARN,INFO,DEBUGTRACE
  • 进程ID
  • 用一个---分隔符来区分真正的日志内容起始
  • 线程名称- 用方括号包裹起来(在控制台输出时有可能被截断)
  • 日志记录器名称-这个通常是类名称(通常是短小的)
  • 日志内容

Logback 没有FATAL这个级别(映射到ERROR)。

控制台输出

默认地的日志配置会将日志打印到控制台。默认会记录ERROR,WARNINFO级别的日志。可以在启动的时候通过--debug来开启”debug”模式。

1
$ java -jar myapp.jar --debug

也可以在application.properties文件指定debug=true

当debug模式启用时,选择的核心日志记录器(嵌入式容器,Hibernate和Spring Boot)将会记录更多更多信息。启用debug模式并不会配置应用程序以DEBUG级别来记录所有消息。

可以选择通过--trace标记(或者application.properties文件中trace=true)来启用”trace”模式。这将为选择的核心日志记录器(嵌入式容器,Hibernate schema生成和整个的Spring框架)启用trace日志记录。

彩色编码输出

如果终端支持ANSI,将会以彩色输出来帮助阅读。可以设置spring.output.ansi.enabled为一个受支持的值来覆盖自动探测。

使用%clr转换词来配置彩色编码。在它最简单的形式中,转换器将以日志级别来为输出涂色。比如:

1
%clr(%5p)

日志级别对应的颜色如下:

级别 颜色
FATAL Red
ERROR Red
WARN Yellow
INFO Green
DEBUG Green
TRACE Green

或者,可以指定应该使用的颜色或样式,以便将其作为转换的选项。例如,将文字的颜色变为黄色:

1
%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){yellow}

支持下面的颜色和样式:

  • blue
  • cyan
  • faint
  • green
  • magenta
  • red
  • yellow

文件输出

Spring Boot 默认只会记录到控制台而不会记录到文件。如果还想记录到文件中需要设置logging.file或者logging.path属性值(例如在application.properties中)。

下面的表格指出了logging.*属性值是如何一起工作的:
日志属性

logging.file|logging.path|示例|描述
–|–|–
(无)|(无)| |只在控制台记录日志
指定文件|(无)|my.log|记录到指定的日志文件。名称可以是一个绝对路径或者相对于现在的目录的路径
(无)|指定目录|/var/log|记录日志到指定目录中spring.log文件中。名称可以是绝对路径或者是相对现在目录的路径

当日志文件达到10MB 时将会被自动切分,并且同控制台输出一样,默认会将ERROR,WARNINFO级别的日志记录下来。

日志系统在应用的生命周期中初始化的时机比较早,因此不会发现通过@propertySource注解加载的文件中的这些日志属性。

日志属性与实际的日志实现相独立的。因此特定的配置属性(例如针对Logback的logback.configurationFile)不归Spring Boot管理。

日志级别

所有支持的日志系统都可以通过使用logger.level.*=LEVEL在Spring 环境(例如application.properties)中设置日志级别,这里的LEVELTRACE,DEBUG,INFO,WARN,ERROR,FATAL,OFF中其中之一。root记录器可以通过logging.level.root来配置。例如application.properties

1
2
3
logging.level.root=WARN
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=ERROR

Spring Boot默认会重新映射Thymeleaf INFO日志到DEBUG级别。这个有助于减少标准日志输出的干扰。想要知道如何在自己的配置中应用重新映射可以查看LevelRemappingAppender了解更多细节。

自定义日志配置

在classpath中加入适当的依赖包就可以激活不同的日志系统,并且root classpath中提供一个配置文件来进行深度自定义,或者在Spring的Environment中指定logging.config属性。

可以通过org.springframework.boot.logging.LoggingSystem系统属性来强制Spring Boot使用常规的日志系统。这个值应该是实现了LoggingSystem接口的类的全名称。也可以使用none值来完全禁用掉Spring Boot的日志配置。

由于日志系统在ApplicationContext创建之前实例化,不可能通过Spring @Configuration文件中的@PropertySources来控制日志系统。系统属性和常规的Spring Boot扩展配置文件可以正常工作。

根据日志系统的不同下面的配置文件将会被加载:

日志系统 自定义
Logback logback-spring.xml,logback-spring.groovy,logback.xmllogback.groovy
Log4j2 log4j2-spring.xmllog4j2.xml
JDK(Java Util Logging) logging.properties

建议尽可能使用-spring变体日志配置文件(比如logback-spring.xml)而不是logback.xml。如果使用标准的配置位置,Spring无法完全控制日志初始化。

这里有一些关于Java Util Logging 已知的类加载问题,当使用“可执行jar包” 的时候可能引起问题。建议尽可能避免使用它。

为了帮助自定义,下面是一些其他的从Spring Environment变量转换为系统属性的属性:

Spring Environment 系统属性 注释
logging.exception-conversion-word LOG_EXCEPTION_CONVERSION_WORD 当出现异常时使用的转换词
logging.file LOG_FILE 如果定义了会用在默认的日志配置中
logging.path LOG_PATH 如果定义了会用在默认的日志配置中
logging.pattern.console CONSOLE_LOG_PATTERN 用在控制台中的日志格式(stdout)。(只支持默认的logback设置)
logging.pattern.file FILE_LOG_PATTERN 日志文件中的日志格式(如果LOG_FILE启用了)。(只支持默认的logback设置)
logging.pattern.level LOG_LEVEL_PATTERN 渲染日志级别的格式(默认为%5p)。(只支持默认的logback设置)
PID PID 当前进程的ID(如果可能,并且还没有被定义为操作系统环境变量)

所有支持的日志系统都可以在解析配置文件时参考系统属性。可以在spring-boot.jar的默认配置中找到例子。(类似的路径spring-boot-2.0.0.BUILD-SNAPSHOT.jar\org\springframework\boot\logging\logback\defaults.xml

如果想在日志属性中使用placeholder,可以使用Spring Boot 的语法而不是底层框架的语法。尤其是使用Logback时,需要使用:作为属性名和默认值之间的分隔符,而不是:-

可以通过覆盖LOG_LEVEL_PATTERN(或logging.pattern.level)来添加MDC和其他专门的内容到日志行中。例如如果使用logging.pattern.level=user:%X{user} %5p,然后如果user存在的话默认的日志格式将会包含一个MDC user 实体,例如:

1
2
2015-09-30 12:30:04.031 user:juergen INFO 22174 --- [  nio-8080-exec-0] demo.Controller
Handling authenticated request

Logback扩展

Spring Boot包含许多对Logback的扩展,可以帮助高级配置。可以在logback-spring.xml文件中使用这些扩展。

不能在标准的logback.xml文件中使用扩展,因为它加载的时机太早。要么使用logback-spring.xml或者定义logging.config属性。

这些扩展不能与Logback的配置扫描一起使用。如果尝试这么做,修改配置文件会引起下面的类似的问题:

1
2
ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProperty], current ElementPath is [[configuration][springProperty]]
ERROR in ch.qos.logback.core.joran.spi.Interpreter@4:71 - no applicable action for [springProfile], current ElementPath is [[configuration][springProfile]]

特定Profile配置

<springProfile>标签可以通过激活Spring profile来选择性地包含或排除配置块。Profile块在<configuration>元素中w任意位置都受支持。使用name属性来指定哪个profile 接受这个配置。通过逗号分隔的列表可以指定多个profile。

1
2
3
4
5
6
7
8
9
10
11
<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>

环境属性

<pringProperty>标签可以从Spring环境中获得属性,以便在Logback中使用。如果你想在logback的配置中访问application.properties文件中的值这个功能会非常有用。这个标签的工作方式和Logback的标准<property>标签类似,但不是指定一个直接的值,你指定了属性的来源(从Environment中)。如果需要将属性存储在local范围以外的地方,那么可以使用scope属性。如果需要一个后备值以防这个属性没有在Environment中指定,可以使用defaultValue属性。

1
2
3
4
5
6
<springProperty scope="context" name="fluentHost" source="myapp.fluentd.host"
defaultValue="localhost"/>
<appender name="FLUENT" class="ch.qos.logback.more.appenders.DataFluentAppender">
<remoteHost>${fluentHost}</remoteHost>
...
</appender>

source值必须通过使用短横线形式(my.property-name)。但是添加到Environment中的属性值可以使用松散绑定的规则。

开发web应用

Spring Boot非常适合用来开发web应用。通过嵌入的Tomcat,Jetty,Undertow或者Netty,可以轻松地创建一个自包含的HTTP服务器。大多数web应用可以通过使用spring-boot-starter-web模块来建立和快速启动。也可以选择通过spring-boot-starter-webflux模块来创建响应式web应用。

如果还没有开发过Spring Boot web应用于,可以跟着快速开始章节中的例子学习。

Spring Web MVC 框架

Spring WEB MVC 框架(经常简称为’Spring MVC’)是一个富”model view controller” web框架。Spring MVC 让你创建特殊的@Controller@RestController bean 来处理到来的HTTP请求。controller中的方法通过@RequestMapping注解映射到HTTP。

下面是一个典型的产生JSON数据的@RestController例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RestController
@RequestMapping(value="/users")
public class MyRestController {

@RequestMapping(value="/{user}", method=RequestMethod.GET)
public User getUser(@PathVariable Long user) {
// ...
}

@RequestMapping(value="/{user}/customers", method=RequestMethod.GET)
List<Customer> getUserCustomers(@PathVariable Long user) {
// ...
}

@RequestMapping(value="/{user}", method=RequestMethod.DELETE)
public User deleteUser(@PathVariable Long user) {
// ...
}

}

Spring MVC 是Spring Framework核心的一部分,查看详情。这里有一些关于Spring MVC的指导

Spring MVC自动配置

Spring Boot为Spring MVC提供了自动配置功能,可以和大多数应用工作地很好。

Spring Boot自动配置在默认的基础上添加了以下功能:

  • 包括ContentNegotiatingViewResolverBeanNameViewResoler bean。
  • 支持服务静态资源,包括支持WebJars。
  • 自动注册Converter,GenericConverter,Formatter bean。
  • HttpMessageConverter支持。
  • 自动注册MessageCodesResolver
  • 静态index.html支持。
  • 自定义Favicon支持。
  • 自动使用ConfigurableWebBindingInitializer bean。

如果想要保持Spring Boot MVC的功能,并且只想要添加额外的MVC 配置(interceptor, formatter, view controller等等),可以添加自己的类型为WebMvcConfigurer@Configuration类,但是不要@EnableWebMvc。如果希望提供自定义的RequestMappingHandlerMappingRequestMappingHandlerAdapter或者ExceptionHandlerExceptionResolver实例,可以声明一个WebMvcRegistrationsAdapter实例,并且提供这些组件。

如果想要完全控制Spring MVC,可以添加自己的@Configuration类,并以@EnableWebMvc注解。

HttpMessageConverter

Spring MVC 使用HttpMessageConverter接口来转换HTTP请求和响应。包括了一些合理的开箱即用的默认功能,例如Object可以自动转换为JSON(使用Jackson库)或者XML(如果可用的话使用Jackson XML扩展,否则使用JAXB)。String 默认使用UTF-8编码。

如果需要添加或者自定义converter可以使用Spring Boot 的HttpMessageConverters类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.*;

@Configuration
public class MyConfiguration {

@Bean
public HttpMessageConverters customConverters() {
HttpMessageConverter<?> additional = ...
HttpMessageConverter<?> another = ...
return new HttpMessageConverters(additional, another);
}

}

任何传递给context的HttpMessageConverter bean 都会添加到converter列表中。也可以通过这种方式覆盖缺省的converter。

自定义JSON序列化和反序列化

如果使用Jackson来序列化和反序列化JSON数据,可能想要编写自己的JsonSerializerJsonDeserializer类。自定义serializer通常通过一个模块注册到Jackson,但是Spring Boot提供了一替代的@jsoncomponent注解,它使直接注册Spring bean变得更加容易。

可以直接在JsonSerializer或者JsonDeserializer实现类上使用@JsonComponent。也可以在包含内部serializers/deserializers类的类上使用。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import org.springframework.boot.jackson.*;

@JsonComponent
public class Example {

public static class Serializer extends JsonSerializer<SomeObject> {
// ...
}

public static class Deserializer extends JsonDeserializer<SomeObject> {
// ...
}

}

ApplicationContext中所有的@JsonComponent bean都将会自动注册到Jackson,并且由于@JsonComponent@Component元注解注解,常规的组件扫描规则也将适用于这些组件。

Spring Boot也提供了JsonObjectSerializerJsonObjectDeserializer基类,它们在序列化对象时为标准的Jackson版本提供了有用的替代方法。

MessageCodesResolver

Spring MVC有一个用于生成错误代码的策略,用于从绑定错误中呈现错误消息:MessageCodesResolver。如果设置了spring.mvc.message-codes-resolver.format属性PREFIX_ERROR_CODE或者POSTFIX-ERROR_CODE(可以查看DefaultMessageCodesResolver.Format枚举),Spring Boot会为你创建一个。

静态内容

Spring Boot默认会在classpath中的/static(或/public/resources/META-INF/resources)目录提供静态服务,或者从ServlectContext的根目录。它使用Spring MVC中的ResourceHttpRequestHandler因此可以通过添加自己的WebMvcConfigurer并且重载addResourceHandlers方法来修改它的行为。

在独立的web应用程序中,也启用了来自容器的默认servlet,并充当备用服务器,如果Spring决定不处理它,则从ServletContext的根中提供内容。大多数情况下,这种情况不会发生(除非修改了默认的MVC配置)因为Spring会一直可以通过DispatcherServlet来处理请求。

resource默认映射到/**,但是可以通过spring.mvc.static-path-pattern来调整。例如重定向所有的resource到/resources/**可以向下面这样:

1
spring.mvc.static-path-pattern=/resources/**

也可以通过spring.resources.static-locations(通过一个目录列表替换掉默认值)来自定义静态resource位置。如果这么做默认的欢迎页面侦测将会切换到自定义的位置,因此如果启动时在定义的位置中有任意的index.html,它将会是应用的主页。

除了上面的标准静态resource位置之外,Webjar内容是一个特殊的例子。任何/webjars/**路径的资源都将从jar文件中得到服务,如果它们被打包成webjar格式。

如果应用会被打包成jar,不要使用src/main/webapp目录。尽管这个目录是一个公用的标准,但是它仅仅是在打包成war的时候生效,并且在生成jar时它会被大多数构建工具默默地忽略掉。

Spring Boot 也支持Spring MVC提供的高级resource处理功能,允许使用诸如静态资源缓存破坏或使用Webjar的版本无关url。

要使用Webjar的版本无关url,添加webjars-locator依赖就可以了。然后申明Webjar,以jQuery举例,“/webjars/jquery/dist/jquery.min.js”会变成“/webjars/jquery/x.y.z/dist/jquery.min.js”,这里的x.y.z就是Webjar版本。

如果使用的是JBoss,需要声明sebjars-locator-jboss-vfs依赖代替webjars-locator,否则所有的Webjars会解析为404

要使用缓存破坏,下面的配置将会为所有静态resource配置一个缓存破坏策略,有效地添加了一个hash值到URL中,例如<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>

1
2
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**

自动配置了在运行时对Thymeleaf和FreeMarker模板中的资源链接重新编写,这得益于ResourceUrlEncodingFilter。当使用JSP时应该手动声明这个filter。其他的模板引擎目前还不能自动支持,但是可以使用自定义模板宏/helper,以及使用ResourceUrlProvider

当动态加载resource时,如JavaScript模块加载器,重命名文件不是一个选项。这也是为什么其他的策略依旧支持并且可以相互组合。“fixed” 策略将会在URL中添加一个静态的版本号,无需修改文件名:

1
2
3
4
5
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
spring.resources.chain.strategy.fixed.enabled=true
spring.resources.chain.strategy.fixed.paths=/js/lib/
spring.resources.chain.strategy.fixed.version=v12

在上面的配置中,JavaScript加载位于"/js/lib/"的模块时将会使用”fixed” 版本策略"/v12/js/lib/mymodule.js",然而其他的resource将仍然使用<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>

可以查看ResourceProperties了解更多支持的选项

这个功能在这个专用的博客和Spring Framework的文档上有完整的说明。

自定义Favicon

Spring Boot在配置的静态内容位置和classpath根目录(按此顺序)中查找favicon.ico。如果找到了,将自动作为应用的favicon。

ConfigurableWebBindingInitializer

Spring MVC使用WebBindingInitializer来为特殊请求初始化一个WebDataBinder。如果创建了自己的ConfigurableWebBindingInitializer @Bean,Spring Boot将自动配置Spring MVC使用它。

模板引擎

跟REST服务一样,也可以使用Spring MVC来提供动态HTML内容。Spring MVC 支持一系列模板技术,包括Thymeleaf,FreeMarker和JSP。许多其他的模板引擎也发布了他们自己的Spring MVC集成方案。

Spring Boot包括针对以下模板引擎的自动配置功能:

有可能的话应该尽量避免使用JSP ,在使用嵌入式servlet容器时有一些已知的限制

当你在使用其中一种模板引擎并使用默认配置时,将会从src/main/resources/templates目录中自动发现模板。

IntelliJ IDEA根据运行应用程序的方式不同classpath排序不一样。在IDE中通过main方法启动应用和使用Maven和Gradle或者打包的jar来运行会导致不同的顺序。这会导致Spring Boot在classpath中查找模板失败。如果你碰到了这个问题,可以在IDE中重新对classpath排序,将模块的class和resource放在首位。或者可以配置模板前缀来查找classpath中的每个模板目录:classpath*:/templates/

错误处理

Spring Boot提供了一个默认的/error映射,以一种合理的方式处理所有错误,并且作为一个全局的错误页面注册到servlet容器中。对于机器客户端,它将生成一个JSON响应,其中包括错误的详细信息、HTTP状态和异常消息。对于浏览器客户端有一个“whitelabel”错误视图,它以HTML格式(或者添加一个View解析到error来自定义)呈现相同的数据。要完全替换掉默认的行为,可以实现ErrorController然后注册一个这种类型的bean定义,或者只需添加类型ErrorAttributes的bean,就可以使用现有的机制,但可以替换内容。

BasicErrorController可以用作自定义ErrorController的基类。当你需要添加一个handler来处理新的content type(默认专门处理text/html并为其他所有内容提供一个后路)时会非常有用。要达到这个目的只需要继承BasicErrorController然后添加一个拥有produces属性的@RequestMapping的公共方法,然后创建一个这个类型的bean。

也可以定义一个@ControllerAdvice为特定的controller或者异常类型返回自定义的JSON内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@ControllerAdvice(basePackageClasses = FooController.class)
public class FooControllerAdvice extends ResponseEntityExceptionHandler {

@ExceptionHandler(YourException.class)
@ResponseBody
ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
HttpStatus status = getStatus(request);
return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status);
}

private HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
return HttpStatus.valueOf(statusCode);
}

}

在上面的例子中,如果一个与FooController在同一个包中的Controller抛出YourException,那么将使用一个CustomerErrorType POJO的json,而不是ErrorAttributes。

自定义错误页面

如果要为给定的状态码显示自定义的HTML错误页面,可以在/error文件夹中添加一个文件。错误页面可以是静态HTML(例如在何意静态resource目录下添加的文件)或使用模板。文件名应该是确定的状态码或者一系列。

例如,将404映射到一个静态文件,文件夹结构应该像下面这样:

1
2
3
4
5
6
7
8
9
src/
+- main/
+- java/
| + <source code>
+- resources/
+- public/
+- error/
| +- 404.html
+- <other public assets>

要映射所有5xx的错误,并且使用FreeMarker模板,目录应该是下面这样:

1
2
3
4
5
6
7
8
9
src/
+- main/
+- java/
| + <source code>
+- resources/
+- templates/
+- error/
| +- 5xx.ftl
+- <other templates>

更复杂的映射可以添加实现了ErrorViewResolver接口的bean。

1
2
3
4
5
6
7
8
9
10
public class MyErrorViewResolver implements ErrorViewResolver {

@Override
public ModelAndView resolveErrorView(HttpServletRequest request,
HttpStatus status, Map<String, Object> model) {
// Use the request or status to optionally return a ModelAndView
return ...
}

}

也可以使用常规的Spring MVC特性比如@ExceptonHandler方法和@ControllerAdviceErrorController会处理任何未处理的异常。

在Spring MVC之外映射错误页面

对那些没有使用Spring MVC的应用,可以使用ErrorPageRegister接口来直接注册ErrorPages。这个抽象概念直接与底层的嵌入式servlet容器一起工作即使没有Spring MVC DispatcherServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Bean
public ErrorPageRegistrar errorPageRegistrar(){
return new MyErrorPageRegistrar();
}

// ...

private static class MyErrorPageRegistrar implements ErrorPageRegistrar {

@Override
public void registerErrorPages(ErrorPageRegistry registry) {
registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
}

}

注意:如果注册的ErrorPage的路径被一个Filter处理而结束了(比如和一些非Spring的web框架一样,比如Jersey和Wicket),然后这个Filter必须得明确地注册为ERROR dispatcher。比如:

1
2
3
4
5
6
7
8
@Bean
public FilterRegistrationBean myFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new MyFilter());
...
registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
return registration;
}

(默认FilterRegistrationBean 不包含ERROR dispatcher类型)。

WebSphere 应用服务器中错误处理

当部署到一个servlet容器时,Spring Boot使用它的错误页面过滤器来转发请求到适当的错误页面,并携带错误码。如果response还没有提交,这个请求只能转发到正确的错误页面。WebSphere 8.0或者以上版本默认会根据正确完成servlet的service方法来提交response。应该设置com.ibm.ws.webcontainer.invokeFlushAfterServicefalse来禁止这个行为。

Spring HATEOAS

如果正在开发的RESTful API 使用多媒体,Spring Boot 为Spring HATEOAS提供了自动配置,能够与大多数应用一起工作。自动配置替换了使用@EnableHypermediaSupport的必要性并且注册了一些bean使构建多媒体应用变得简单,这些bean包括一个LinkDiscoverers(为了应用端支持)和一个为了正确整理response到需要的表现形式而配置的ObjectMapper。这个ObjectMapper将基于spring.jackson.*属性或者可能存在的Jackson2ObjectMapperBuilder bean 进行自定义。

可以通过使用@EnableHypermediaSupport来控制Spring HATEOAS的配置。要注意的是这将会禁用上面提到的ObjectMapper自定义。

CORS支持

跨域资源共享是一被大多数浏览器实现的W3C标准,它允许你以一种灵活的方式来指定哪种类型的跨域请求是被授权的,代替使用一些不安全和不强大的方式比如IFRAME 和JSONP。

从4.2开始,Spring MVC跨域支持开箱即用。在Spring Boot应用中与@CrossOrigin注解一起使用controller方法CORS配置不需要使用任何特定的配置。可以通过注册拥有一个自定义的addCorsMappings(CorsRegistry)的WebMvcConfigurer` bean 来定义全局的CORS配置

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class MyConfiguration {

@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**");
}
};
}
}

Spring WebFlux框架

Spring WebFlux自动配置

HttpMessageReaders 和 HttpMessageWriters HTTP编码

静态内容

模板引擎

JAX-RS 和Jersey

如果你更喜欢REST端点的jax-rs编程模型,可以使用一个可用的实现来代替Spring MVC。Jersey 1.x和 Apache CXF在将他们的ServletFilter作为@Bean注册到应用上下文中的情况下就已经工作的很好了。Jersey 2.x有一些本地Spring支持,所以我们也在Spring Boot 中通过一个starter 提供了自动配置支持。

开始开发Jersey 2.x只需要添加spring-boot-starter-jersey依赖然后写一个ResourceConfig类型的@Bean并在这里注册所有的端点就可以了:

1
2
3
4
5
6
7
8
@Component
public class JerseyConfig extends ResourceConfig {

public JerseyConfig() {
register(Endpoint.class);
}

}

Jersey对扫描可执行档案的支持是相当有限的。例如在运行一个可执行的war包时它不能扫描WEB-INF/classes目录的包中发现的endpoint。为了避免这个限制,不应该使用packages方法并且endpoint应该像上面一样通过register方法单独注册。

也可以注册任意数量的实现ResourceConfigCustomizer接口的bean来进行更高级的自定义。

所有注册的endpoint都应该有@Component和HTTP resource注解(如@GET),例如:

1
2
3
4
5
6
7
8
9
10
@Component
@Path("/hello")
public class Endpoint {

@GET
public String message() {
return "Hello";
}

}

由于Endpoint是一个Spring @Component,因此它的生命周期由Spring来管理,并且你可以@Autowired 依赖并且通过@Value注入外部的配置。Jersey servlet默认会注册并映射到/*。可以通过添加@ApplicationPathResourceConfig修改这个映射。

Jersey默认将会作为@ServletRegistrationBean类型的@Bean中的一个Servlet,这个@ServletRegistrationBean名称为jerseyServletRegistration。默认情况下这个servlet会延迟初始化,但是你可以通过spring.jersey.servlet.load-on-startup来自定义。可以创建一个自己的相同名称的bean来禁用或者覆盖这个bean。也可以使用一个Filter通过设置spring.jersey.type=filter来代替这个Servlet(在这种情况下,要替换或覆盖的@Bean是jerseyFilterRegistration)。这个servlet有个@Order注解,可以通过spring.jersey.filter.order来设置。注册Servlet和Filter可以给定初始化参数,使用spring.jersey.init.*来指定一个属性map。

这里有一个Jersey例子可以看到如何设置。还有一个Jersey 1.x 例子。注意在Jersey 1.x例子中spring-boot maven插件配置了不打包某些Jersey的jar,这样他们可以被JAX-RS的实现扫描到(因为这个例子要求在Filter注册中对它们进行扫描)。如果你有任何JAX-RS resource打包成了内部jar时,可能也需要这么做。

嵌入式servlet容器支持

Spring Boot 支持嵌入式Tomcat,Jetty,Undertow服务器。大多数开发者只需要使用对应的”Starter”来获取完整配置的实例。嵌入式服务器默认会在8080端口监听HTTP请求。

如果你选择在CentOS上使用Tomcat则要注意,默认情况下会使用一个临时目录来存储编绎的JSP和上传的文件等。当你的应用程序运行导致失败时,该目录有可能被tmpwatch删除。要避免这样你可能想要自定义tmpwatch配置,这样tomcat.*目录不会删除,或者配置server.tomcat.basedir这样的话嵌入式Tomcat会使用不同的目录。

Servlet,Filter和listerner

当使用嵌入式servlet容器时既可以使用Spring Bean又可以扫描Servlet组件来注册Servlet,Filter和来自servlet规范的所有listener(例如HttpSessionListener)。

Servlet上下文初始化

任何一个Spring bean的的ServletFilter或者Serlvet*Listener的实例都将在嵌入式容器中注册。如果想从application.properties中引用一个值,这将非常方便。

默认情况下如果context只包含一个Servlet,它将会映射到/。在有多个Servlet Bean的情况下,bean的名称将会作为path的前缀。Filter会映射到/*。如果基于约定的映射不足够灵活,可以使用ServletRegistrationBean,FilterRegistrationBeanServletListenerRegistrationBean类来完全控制。

ServletWebServerApplicationContext

嵌入式servlet容器不会直接执行Servlet 3.0以上的javax.servlet.ServletContainerInitializer接口,或者是Spring的org.springframework.web.WebApplicationInitializer接口。这是一个刻意的设计,目的是为了减少在war中运行的第三方库会破坏Spring Boot应用程序的风险。

如果你需要在Spring Boot应用中执行servlet上下文初始化,需要注册一个实现了org.springframework.boot.web.servlet.ServletContextInitializer接口的bean。唯一的onStartup方法提供了访问ServletContext的能力,并且可以在必要的情况下轻松地用来作为已知WebApplicationInitializer的桥接器。

扫描Servlet,Filter和lisenter

当使用嵌入式容器时,可以使用@ServletComponentScan来启用对注解了@WebServlet@WebFilter@WebListener类进行自动注册。

@ServletComponentScan在独立容器中时没有效果,在这里将会使用容器的自有发现机制。

ServletWebServerApplicatonContext

Spring Boot为嵌入式容器支持使用了一个新的ApplicationContext类型。ServletWebServerApplicationContext是一个专门的WebApplicationContext类型,通过搜索一个单独的ServletWebServerFactory bean来引导自己。通常是一个TomcatServletWebServerFactoryJettyServletWebServerFactory或者是UndertowServletWebServerFactory将被自动配置。

通常不需要感知这些实现类。大多数应用会自动配置并且将为你创建合适的ApplicationContextServletWebServerFactory

自定义嵌入式servlet容器

可以通过使用Spring Environment属性来配置常见的servlet容器设置。通常你将在application.properties文件中定义这些属性。

常见的服务器设置包括:

  • 网络设置:侦听HTTP请求的端口(server.port),接口地址绑定到server.address等等。
  • Session设置:session是否执久化(server.session.persistence),session超时时间(server.session.timeout),session数据的位置(server.session.store-dir)和session-cookie配置(server.session.cookie.*)。
  • 错误管理:错误页面的位置(server.error.path)等。
  • SSL
  • HTTP压缩

Spring Boot尽可能多地暴露通用设置,但这并不总是可行的。对于那些情况下,专用的命名空间提供特定服务器的自定义(查看server.tomcatserver.undertow)。例如可以使用嵌入式servlet容器的特定功能配置访问日志

查看ServerProperties类获取详细清单。

编程式自定义

如果需要通过编程来自定义嵌入式servlet容器,可以注册一个实现了WebServerFactoryCustomizer 接口的Spring bean。WebServerFactoryCustomizer提供了访问ConfigurableServletWebServerFactory的方法,ConfigurableServletWebServerFactory包含了大量定制setter方法。在Tomcat,Jetty和Undertow中存在专门的变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;

@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

@Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}

}
直接自定义ConfigurableServletWebServerFactory

如果上面的自定义方式太局限,可以注册自己的TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactory bean。

1
2
3
4
5
6
7
8
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setPort(9000);
factory.setSessionTimeout(10, TimeUnit.MINUTES);
factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html"));
return factory;
}

set方法提供了许多配置选项。如果你需要做一些更独特的事情,还提供了几个受保护的方法“钩子”。更多详情请查看源代码文档。

JSP限制

当使用嵌入式servlet容器(并且被打包成可执行包)运行Spring Boot应用时,对JSP的支持有一些限制。

  • 对于Tomcat,如果使用war包,它就可以工作,即可执行的war将工作,并且也可以部署到一个标准容器(不限于,但包括Tomcat)。一个可执行的jar不能工作,因为在Tomcat中有一个硬编码的文件模式。
  • 对于Jetty,如果使用war包,它就可以工作,即可执行的war将工作,并且也可以部署到一个标准容器。
  • Undertow不支持JSP。
  • 创建自定义的error.jsp页面不会覆盖默认的错误处理视图,而是应该使用自定义错误页面。

这里有一个JSP例子可以看到如何设置。

安全

如果在classpath中发现了Spring Security,那么web应用所有的HTTP端点会默认使用”basic”认证。可以添加@EnableGlobalMethodSecurity来添加方法级别的安全。更多信息可以查看Spring Security Reference

默认的AuthenticationManager有一个单独的用户(“user” 用户名和随机密码,密码在应用程序启动时打印在INFO级别)

1
Using default security password: 78fa095d-3f4c-48b1-ad50-e24c31d5cf35

如果你调整了日志配置,确保org.springframework.boot.autoconfigure.security类型设置为INFO级别,否则默认密码不会打印。

可以通过提供security.user.password属性来修改密码。这个和其他有用的属性通过SecurityProperties(属性前缀为”security”)扩展。

OAuth2

授权服务器

资源服务器

User信息中的Token类型

自定义User信息 RestTemplate

Client

单点登录

Actuator 安全

SQL数据库

Spring Framework对SQL数据库提供了大量支持。从使用JdbcTemplate直接的JDBC访问到完全的“对象关系映射”技术如Hibernate。Spring Data提供了额外的功能级别,直接从接口创建Repository实现,并使用约定从方法名称生成查询。

配置数据源

Java的javax.sql.DataSource接口提供了与数据库连接工作的标准方法。传统上,数据源使用URL和一些凭证来建立数据库连接。

还可以查看“如何操作”的部分,以获得更高级的示例,尤其是要对数据源的配置进行完全控制。

嵌入式数据库支持

使用内存中嵌入式数据库开发应用程序通常很方便。明显地内存数据库不提供持久化;你需要当应用程序启动时填充数据库,并准备在应用程序结束时抛出数据。

这里有如何初始化数据库

Spring Boot可以自动配置嵌入式H2HSQLDerby数据库。不需要提供任何连接URL,只需要简单地包含想要使用的嵌入式数据库的构建依赖。

如果在测试用例中使用这个功能,你可能注意到了整个测试用命重用了相同的数据库,而不管使用了多少个应用上下文。如果你想确保每个上下文使用独立的嵌入式数据库,你应该设置spring.datasource.generate-unique-nametrue

例如典型的POM依赖是:

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>

需要对spring jdbc的依赖,以便自动配置嵌入式数据库。在这个例子中它通过spring-boot-starter-data-jpa来传递这个依赖。

如果出于某种原因,你确实为嵌入式数据库配置了连接URL,那么应该注意确保数据库的自动关闭是禁用的。如果你正在使用H2,你应该使用DB_CLOSE_ON_EXIT=FALSE来禁用。如果使用HSQLDB,应该确保没有使用shutdown=true。禁用数据库的自动关闭功能可以让Spring Boot在数据库关闭时进行控制,从而确保在不再需要访问数据库时发生这种情况。

连接生产库

还可以使用DataSource池自动配置生产数据库连接。下面是选择具体实现的算法:

  • 我们更喜欢HikariCP因为它的性能和并发性,所以如果它可用,我们总是选择它。
  • 否则如果TomcatDataSource连接池可用,就会使用它。
  • HikariCP 和TomcatDataSource连接池都不可用并且 Commons DBCP2可用则使用它。

如果你使用spring-boot-starter-jdbc或者spring-boot-starter-data-jpa “starter” 则会自动依赖HikariCP

可以通过设置spring.datasource.type属性来完全绕开这个算法并且指定连接池。如果你在Tomcat容器中运行你的应用程序,那么这一点尤为重要,因为默认提供了tomcat-jdbc

可以手动配置额外的连接池。如果你定义了自己的DataSource bean,则不会发生自动配置。

spring.datasource.*中的扩展配置属性可以控制数据源配置。例如,你可以在application.properties中声明以下块:

1
2
3
4
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

你至少应该使用spring.datasource.url属性来指定url或者Sprig Boot 会尝试自动配置一个嵌入式数据库。

你通常不需要指定driver-class-name,因为对于大多数数据库Spring boot 可以从url中推断出来。

对于创建DataSource池,我们需要能够验证一个有效的Driver类是否可用,所以我们在做任何事情之前都要检查它。例如,如果你设了spring.datasource.driver-class-name=com.mysql.jdbc,那么这个类就必须是可加载的。

更多受支持的选项,请参见DataSourceProperties。这些是标准的选项,不管实际是什么实现都可以工作。也可以通过它们各自的前缀(spring.datasource.hikari.*,spring.datasource.tomcat.*,和spring.datasource.dbcp2.*)微调特定实现设置。请参阅你正在使用的连接池实现的文档获取更多细节。

例如如果你正在使用Tomcat连接池,可以自定义许多额外的设置:

1
2
3
4
5
6
7
8
# Number of ms to wait before throwing an exception if no connection is available.
spring.datasource.tomcat.max-wait=10000

# Maximum number of active connections that can be allocated from this pool at the same time.
spring.datasource.tomcat.max-active=50

# Validate the connection before borrowing it from the pool.
spring.datasource.tomcat.test-on-borrow=true

连接JNDI数据库

如果你正在将Spring Boot应用程序部署到应用程序服务器,那么你可能需要使用应用程序服务器的内置特性来配置和管理数据源,并使用JNDI访问它。

spring.datasource.jndi-name属性可以用作spring.datasource.url,spring.datasource.usernamespring.datasource.password属性的另一种选择从特定的JNDI位置访问DataSource。例如下面applicaion.properties中的块展示了如何访问JBoss定义的DataSource

1
spring.datasource.jndi-name=java:jboss/datasources/customers

使用JdbcTemplate

Spring 的JdbcTemplateNamedParameterJdbcTemplate类是自动配置的并且可以直接@Autowire他们到你自己的bean中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

private final JdbcTemplate jdbcTemplate;

@Autowired
public MyBean(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

// ...

}

你可以通过spring.jdbc.template.*来自定义template的一些属性:

1
spring.jdbc.template.max-rows=500

NamedParameterJdbcTemplate在背后重用相同的JdbcTemplate实例。如果定义了多个JdbcTemplate并且不存在主要的候选者,不会自动配置NamedParameterJdbcTemplate

JPA和Spring Data

Java Persistence API 是一种允许你映射对象到关系型数据的技术。spring-boot-starter-data-jpa POM提供了一种快速开始的方式。它提供了下面关键的依赖:

  • Hibernate - 最流行的JPA实现之一
  • Spring Data JPA - 使实现基于JPA的repositories变得容易
  • Spring ORM - Spring Framework的核心ORM支持

在这里不涉及到太多JPA和Spring Data细节。可以访问Accessing Data with JPA指南和阅读Spring Data JPAHibernate 文档。

实体类

传统上,JPA “Entity”的类是在persistence.xml文件中指定的。在Spring Boot 中这个文件不是必需的,而是使用“Entity Scanning”。默认情况下在主配置类(某个注解了@EnableAutoConfiguration@SpringBootApplication的类)下面的所有包都会被搜索到。

任何注解了@Entity,@Embeddable@MappedSuperclass的类都是被考虑的对象。一个典型的实体类应该是这样的:

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
package com.example.myapp.domain;

import java.io.Serializable;
import javax.persistence.*;

@Entity
public class City implements Serializable {

@Id
@GeneratedValue
private Long id;

@Column(nullable = false)
private String name;

@Column(nullable = false)
private String state;

// ... additional members, often include @OneToMany mappings

protected City() {
// no-args constructor required by JPA spec
// this one is protected since it shouldn't be used directly
}

public City(String name, String state) {
this.name = name;
this.country = country;
}

public String getName() {
return this.name;
}

public String getState() {
return this.state;
}

// ... etc

}

可以使用@EntityScan注解来自定义扫描位置。更多查看Section 78.4, “Separate @Entity definitions from Spring configuration”

Spring Data JPA仓库

Spring Data JPA repository是你可以定义来访问数据的接口。JPA查询会从你的方法名自动创建查询。例如,CityRepository接口可能声明了findAllByState(String state)方法来根据给定状态查找所有的城市。

对于更复杂的查询你可以使用Spring Data的Query注解来注解你的方法。

Spring Data repository通常继承自Repository或者CrudRepository接口。如果使用自动配置,将会从包含主配置类(注解了@EnableAutoConfiguration@SpringBooApplication的类)的包中往下查找repository。

这里有一个典型的Spring Data repository:

1
2
3
4
5
6
7
8
9
10
11
12
package com.example.myapp.domain;

import org.springframework.data.domain.*;
import org.springframework.data.repository.*;

public interface CityRepository extends Repository<City, Long> {

Page<City> findAll(Pageable pageable);

City findByNameAndCountryAllIgnoringCase(String name, String country);

}

我们仅仅触及Spring Data JPA的表面。可以查看文档获取完整的细节。

创建和删除JPA数据库

默认情况下,JPA数据库在使用嵌入式数据库(H2,HSQL或Derby)时才会自动创建。你也可以使用spring.jpa.*属性明确地配置JPA。例如可以在application.properties`中添加下面的配置来创建和删除表。

1
spring.jpa.hibernate.ddl-auto=create-drop

Hibernate自有内部属性名称是hibernate.hbm2ddl.auto(如果你能记住它更好)。你可以和其他Hibernate原生属性一样设置它,使用spring.jpa.properties.*(这个前缀在添加到entity manager之前被剥离出来)。例如:

1
spring.jpa.properties.hibernate.globally_quoted_identifiers=true

传给Hibernate entity manager的是hiberbate.globally_quoted_identifiers

默认情况下DDL执行(或验证)推迟到ApplicationContext启动之后。还有一个spring.jpa.generate-ddl标志,但是在Hibernate autoconfig启用的情况下不会使用因为ddl-auto设置更细粒度。

在View中打开EntityManager

如果正在运行一个web应用,Spring Boot默认会注册OpenEntityManageerInViewInterceptor来应用“Open EntityManager in View” 模式,例如来在web视图允许延迟加载。如果你不想要这种行为,你应用在applicaiont.properties中设置spring.jpa.open-in-viewfalse

使用H2的web控制台

修改H2控制台的路径

加密H2的控制台

使用jOOQ

代码生成

使用DSLContext

jOOQ SQL方言

自定义jOOQ

NoSQL

Spring Data提供额外的项目来帮助你访问一些NoSQL技术,包括MongoDBNeo4JElasticsearchSolrRedisGemfireCassandraCouchbaseLDAP。Spring Boot 对Redis,MongoDB,Neo4j,Elastcisearch,Solr,Cassandra,Couchbase和LDAP提供了自动配置;你可以使用其他项目,但是你需要自己配置它们。

Redis

Redis 是一个缓存,消息代理和功能丰富的键值存储。Spring Boot为JedisLettuce 客户端库提供基本的自动配置并由Spring Data Redis在它们之上提供抽象。

默认有一个spring-boot-starter-data-redis “Starter” 以方便的方式收集依赖并且默认使用 Jedis。如果你正在构建一个响应式应用程序,那么spring-stardata-data-redis-reactive “Starter” 将会让你继续前进。

连接到Redis

你可以像任何其他Spring Bean一样注入自动配置的RedisConnectionFactoryStringRedisTemplate或者RedisTemplate 实例。默认情况下这些实例会尝试使用localhost:6379来连接到Redis 服务器。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class MyBean {

private StringRedisTemplate template;

@Autowired
public MyBean(StringRedisTemplate template) {
this.template = template;
}

// ...

}

你也可以注册任意数量的实现JedisClientConfigurationBuilderCustomizer 的bean 来实现更高级的定制。如果你正在使用Lettuce,可以使用LettuceClientConfigurationBuilderCustomizer

如果你在任何自己的自动配置类型上加了@Bean,它会替代默认的(除了在RedisTemplate的情况下,排除是基于bean名称“redisTemplate”而不是其类型)。如果commons-pool2在classpath中,则默认情况下你将获得一个连接池工厂。

MongoDB

连接到MongoDB数据库

MongoTemplate

Spring Data MongoDB仓库

嵌入式Mongo

Neo4j

连接到Neo4j数据库

使用嵌入式模式

Neo4jSession

Spring Data Neo4j仓库

仓库示例

Gemfire

Solr

连接到Solr

Spring Data Solr仓库

Elasticsearch

使用Jest连接Elasticsearch

使用Spring Data连接Elasticsearch

Spring Data Elasticsearch 仓库

Cassandra

连接到Cassandra

Spring Data Cassandra仓库

Couchbase

连接到Couchbase

Spring Data Couchbase仓库

LDAP

连接到LDAP服务器

Spring Data LDAP仓库

嵌入式内存LDAP服务器

InfluxDB

连接到InfluxDB

缓存

Spring Framework为透明地向应用程序添加缓存提供了支持。在其核心,抽象层将缓存应用于方法,从而减少基于缓存中可用信息的执行数量。缓存逻辑是透明地应用的,不会对调用程序产生任何干扰。只要通过@Enablecaching注解启用了缓存支持,Spring Boot就会自动配置缓存基础结构。

查看Spring Framework文件相关章节获取更多信息。

简而言之,将缓存添加到服务的操作中就像向其方法添加相关注释一样简单:

1
2
3
4
5
6
7
8
9
10
11
12
import org.springframework.cache.annotation.Cacheable
import org.springframework.stereotype.Component;

@Component
public class MathService {

@Cacheable("piDecimals")
public int computePiDecimal(int i) {
// ...
}

}

此示例演示如何在可能代价高昂的操作中使用缓存。在调用computePiDecimal之前,抽象层会在piDecimals缓存中查找匹配参数i的实体。如果找到了,缓存中的内容会立即返回给调用方,并且不会调用这个方法。否则这个方法会被调用并且在返回值之前会更新缓存。

也可以明确地使用标准的JSR-107(JCache)注解(比如@CacheResult)。我们强烈建议你不要把它们混在一起。

如果没有添加具体的缓存库,Spring Boot将会自动配置一个简单的实现,它使用内存中的并发Map。当需要缓存时(如上面例子中的piDecimals),这个provider将为你即时创建它。简单的提供者并不推荐用于生产,但是对于入门并确保您了解这些特性是非常好的。当你已经决定要使用的缓存提供程序时,请一定要阅读它的文档,以确定如何配置你的应用程序使用的缓存。实际上,所有的提供者都要求你显式地配置应用程序中使用的每个缓存。有些提供了自定义spring.cache.cache-names属性定义的默认缓存的方法。

也可以透明地更新或从缓存中删除数据。

如果你使用的缓存基础设施不是基于接口的,那么确保启用了@EnableCachingproxyTargetClass属性。

支持的缓存实现

缓存抽象层没有提供实际的存储,依赖于org.springframework.cache.Cacheorg.springframework.cache.CacheManager接口实现。

如果你没有定义CacheManager类型的或名为cacheResolver(查看CachingConfigurer)的CacheResolver bean,Spring Boot尝试加载下面的实现(按此顺序):

也可以使用spring.cache.type属性强制使用缓存实现。如果您需要在某些环境中完全禁用缓存(例如测试),那么可以使用该属性。

使用spring-boot-starter-cache “Starter”来快速添加基本的缓存依赖,这个starter引入了spring-context-support:如果是手动添加的依赖,为了使用JCache,EhCache 2.x或Guava支持,你必须得包含spring-context-support

如果Spring Boot自动配置了CacheManager,可以通过暴露一个实现了CacheManagerCustomizer接口的bean在CacheManager完全初始化之前进一步优化它的配置。下面的代码设置了一个标志,表示null值应该被传递到底层的map。

1
2
3
4
5
6
7
8
9
@Bean
public CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
return new CacheManagerCustomizer<ConcurrentMapCacheManager>() {
@Override
public void customize(ConcurrentMapCacheManager cacheManager) {
cacheManager.setAllowNullValues(false);
}
};
}

在上面的例子中,预计会自动配置一个ConcurrentMapCacheManager。如果不是这种情况(你提供了自己的配置,或者是自动配置了不同的缓存实现),则不会调用customizer了。你可以提供很多个customizer并且可以使用常规的@Order或者Ordered来排序。

通用

如果上下文中定义了不止一个org.springframework.cache.Cache bean,则使用通用缓存。CacheManager包装所有创建的这个类型的bean。

JCache(JSR-107)

JCache是通过类路径中存在javax.cache.spi.CachingProvider来加载的,spring-boot-starter-cache “starter” 提供了JCacheCacheManager。有很多兼容的库并且Spring Boot 提供了对Ehcache 3、Hazelcast和Infinispan的依赖管理。还可以添加任何其他兼容的库。

有可能会出现有多个提供者的情况,在这种情况下必须明确指定提供者。即使JSR-107标准没有强制定义配置文件的位置,但是Spring Boot尽最大可能来适应具体实现。

1
2
3
# Only necessary if more than one provider is present
spring.cache.jcache.provider=com.acme.MyCachingProvider
spring.cache.jcache.config=classpath:acme.xml

由于缓存库有可能提供本地实现和JSR-107支持,Spring Boot将会优先选择JSR-107支持,因此如果你切换到不同的JSR-107实现,Spring Boot还是可以支持相同的功能。

Spring Boot 对Hazelcast有一个通用的实现。如果有一个单独的HazelcastInstance可用,它会自动地为CacheManager重用,除非指定spring.cache.jcache.config属性。

还有一些方式来自定义底层的javax.cache.cacheManager:

  • 缓存可以在启动的时候通过spring.cache.cache-names属性来创建。如果定义了自已的javax.cache.configuration.Configuration bean ,将会用它来自定义它们。
  • CacheManager引用调用org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer bean 来进行完全定制。

如果定义了一个标准的javax.cache.CacheManager bean,它会自动被抽象层期望的org.springframework.cache.CacheManager实现包装。不进行进一步的定制。

EhCache 2.x

如果在classpath的根目录上找到了ehcache.xml文件,那么将会使用EhCache 2.x。如果是EhCache 2.x,spring-boot-starter-cache “Starter”提供的EhCacheCacheManager和它提供的这个文件用来启动缓存管理器。也可以使用这种方式来提供可选的配置文件:

1
spring.cache.ehcache.config=classpath:config/another-config.xml

Hazelcast

Infinispan

Couchbase

Redis

如果Redis可用并且配置了,RedisCacheManager将会自动配置。还可以使用spring.cache.cache-names属性在启动时创建额外的缓存。

在缺省情况下,添加了一个key的前缀,以防止如果两个单独的缓存使用相同的密钥,Redis将会有重叠的key,并可能会返回无效值的情况。如果你创建了自己的RedisCacheManager,我们强烈建议保持开启这个设置。

Caffeine

Simple

如果没有发现其他的提供者,将会配置一个使用ConcurrentHashMap作为缓存存储的简单实现。如果应用没有提供缓存库,这将是默认配置。缓存默认是实时创建的,但是你可以使用cache-names属性限制可用的缓存列表。例如,如果你只想要foobar缓存:

1
spring.cache.cache-names=foo,bar

如果你这样做,你的应用程序使用一个未列出的缓存那么当需要缓存时,它会在运行时失败,但在启动时不会。如果你使用未声明的缓存,这与“真实”缓存提供者的行为方式类似。

None

当你的配置中出现了@EnableCache时,也可以预期适当的缓存配置。如果在某些环境中需要禁用缓存,那么强制缓存类型为none,以使用无操作实现:

1
spring.cache.type=none

消息

Spring Framework 提供了集成消息系统的扩展支持:从简单地使用JMS API JmsTemplate 到完整的接收异步消息的架构。Spring AMQP为“Advanced Message Queuing Protocol”提供了一个类似的特性集并且Spring Boot也为RabbitTemplate和RabbitMQ提供配置自动配置选项。在Spring WebSocket中还原生支持STOMP的消息传递,Spring Boot通过starter和少量的自动配置支持这一点。Spring Boot 也支持Apache Kafka。

JMS

javax.jms.ConnectionFactory接口提供了创建与JMS代理进行交互的javax.jms.Connection的标准方法。尽管Spring需要一个ConnectionFactory来与JMS一起工作,但是你通常不需要直接使用它并且可以依赖更高层次的消息抽象层(查看Spring Framework的文档获取更多细节)。Spring Boot 也自动配置必要的组件来发送与接收消息。

ActiveMQ

当Spring Boot在classpath中发现ActiveMQ时,也会配置一个ConnectionFactory。如果提供了代理,将启动一个嵌入式代理并且自动配置它(只要没有通过配置指定代理URL)。

如果你使用了spring-boot-starter-activemq,则提供连接或嵌入ActiveMQ实例的必要依赖项,以及与JMS集成的Spring基础设施。

ActiveMQ配置由spring.activemq.*中的外部配置属性控制。例如,你可以在application.properties中声明下面的语句:

1
2
3
spring.activemq.broker-url=tcp://192.168.1.210:9876
spring.activemq.user=admin
spring.activemq.password=secret

你还可以通过添加org.apache.activemq:activemq-pool的依赖来使用JMS资源池,并相应地配置PooledConnectionFactory

1
2
spring.activemq.pool.enabled=true
spring.activemq.pool.max-connections=50

查看ActiveMQProperties获取更多地的支持属性。

默认情况下,如果不存在destination ,ActiveMQ会创建一个,因此,destination 是根据它们提供的名称来解析的。

Artemis

如果Spring Boot 在classpath中发现了Artemis,则会自动配置ConnectionFactory。如果代理存在,则将自动启动并配置嵌入式代理(除非模式属性已显式设置)。支持的模式有:embedded(显式地指出需要使用嵌入式代理,并且在代理在类路径中不可用时导致错误)和native使用netty传输协议来连接代理。当配置后者时,Spring Boot 配置一个ConnectionFactory,使用默认设置连接到在本地机器上运行的代理。

如果你使用了spring-boot-starter-artemis,将会提供连接已经存在的Artemis实例的必要依赖,以及与JMS集成的Spring基础组件。添加org.apache.activemq:artemis-jms-server到你的应用中允许你使用embedded模式。

Artemis的配置是由在spring.artemis.*的外部配置属性控制的。例如,你可以在application.properties中声明以下代码:

1
2
3
4
5
spring.artemis.mode=native
spring.artemis.host=192.168.1.210
spring.artemis.port=9876
spring.artemis.user=admin
spring.artemis.password=secret

在嵌入代理时,你可以选择是否启用持久性,以及应该提供的destination列表。可以将它们指定为以逗号分隔的列表,以使用默认选项创建它们;或者你可以定义org.apache.activemq.artemis.jms.server.config.JMSQueueConfigurationorg.apache.activemq.artemis.jms.server.config.TopicConfiguration类型的bean(s),分别用于高级队列和主题配置。

查看ArtemisProperties 获取更多支持的属性。

任何JNDI查找都不涉及到,destination都是根据它们的名称来解析的,或者在”Artemis “的配置中使用“name”属性,或者通过配置提供的名称。

使用JNDI ConnectionFactory

如果你在应用程序服务器中运行你的应用程序,Spring Boot将尝试使用JNDI来定位一个JMS ConnectionFactory。默认会检查java:/JmsXAjava:/XAConnectionFactory。你可以使用spring.jms.jndi-name属性来指定其他的位置:

1
spring.jms.jndi-name=java:/MyConnectionFactory

发送消息

Spring的JmsTemplate是自动配置的并且你可以直接注入到你的bean中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

private final JmsTemplate jmsTemplate;

@Autowired
public MyBean(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}

// ...

}

JmsMessagingTemplate也可以以类似的方式注入。如果定义了DestinationResolverMessageConverter bean,它们将自动关联到自动配置的JmsTemplate

接收消息

当JMS 基础组件存在时,任何bean都可以用 @JmsListener 注解来创建监听器端点。如果没有定义JmsListenerContainerFactory bean,会自动配置一个默认的。如果定义了DestinationResolverMessageConverter bean,那么它们将自动与缺省工厂相关联。

默认的工厂默认是事务性的。如果你在一个JtaTransactionManager存在的基础结构中运行,那么默认情况下,它将与监听器容器相关联。如果不是的话,sessionTransacted标志将会启用。在后一种情况下,你可以将本地数据存储事务与传入消息的处理关联起来,方法是在listener方法(或其委托方法)上添加@Transactional。这将确保在本地事务完成后传入消息被确认。这还包括在相同JMS会话中执行的响应消息发送。

下面的组件在someQueue上创建了一个监听端点:

1
2
3
4
5
6
7
8
9
@Component
public class MyBean {

@JmsListener(destination = "someQueue")
public void processMessage(String content) {
// ...
}

}

查看@EnableJms文档查看更多细节。

如果你需要创建更多的JmsListenerContainerFactory实例或者你想要覆盖默认的,Spring Boot提供了DefaultJmsListenerContainerFactoryConfigurer来实例化DefaultJmsListenerContainerFactory并且与自动配置的使用相同的设置。

例如,下面的内容展示了另一个使用特定MessageConverter的工厂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
static class JmsConfiguration {

@Bean
public DefaultJmsListenerContainerFactory myFactory(
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory());
factory.setMessageConverter(myMessageConverter());
return factory;
}

}

然后可以在任何@JmsListener注解的方法中使用:

1
2
3
4
5
6
7
8
9
@Component
public class MyBean {

@JmsListener(destination = "someQueue", containerFactory="myFactory")
public void processMessage(String content) {
// ...
}

}

AMQP

高级消息队列协议(AMQP)是面向消息中间件的一种平台无关的、线级协议(?)。Spring AMQP 项目Spring的核心概念应用到基于AMQP的消息解决方案的开发。Spring Boot提供了多种便利,可以通过RabbitMQ与AMQP进行合作,包括spring-boot-starter-amqp “Starter”。

RabbitMQ

RabbitMQ是一种基于AMQP协议的轻量级的、可靠的、可伸缩的、可移植的消息代理。Spring使用RabbitMQ来使用AMQP协议进行通信。
RabbitMQ配置由spring.rabbitmq.*中的外部配置属性控制。例如,可以在application.properties中声明以下代码:

1
2
3
4
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=secret

请参阅RabbitProperties获得更多受支持的选项。

查看理解AMQP,RabbitMQ使用的协议了解更多细节

发送消息

Spring的 AmqpTemplateAmqpAdmin 是自动配置的,你可以自动装配他们到自己的 bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

private final AmqpAdmin amqpAdmin;
private final AmqpTemplate amqpTemplate;

@Autowired
public MyBean(AmqpAdmin amqpAdmin, AmqpTemplate amqpTemplate) {
this.amqpAdmin = amqpAdmin;
this.amqpTemplate = amqpTemplate;
}

// ...

}

RabbitMessagingTemplate可以以类似的方式注入。如果定义了MessageConverter bean,那么它将自动与自动配置的AmqpTemplate相关联。

任何org.springframework.amqp.core.Queue定义为bean的队列将在必要时自动用于在RabbitMQ实例上声明一个对应的队列。

你可以启用AmqpTemplate上的重试来重试操作,例如在丢失代理连接的事件中。默认情况下,重试是禁用的。

接收消息

当Rabbit组件存在时,任何bean都可以用@RabbitListener注解来创建一个监听器端点。如果没有定义RabbitListenerContainerFactory,默认自动配置SimpleRabbitListenerContainerFactory,并且你可以使用spring.rabbitmq.listener.type属性切换到一个直接的容器。如果定义了MessageConverterMessageRecoverer bean,那么它们将自动与缺省工厂相关联。

下面的组件在someQueue队列上创建一个监听器端点:

1
2
3
4
5
6
7
8
9
@Component
public class MyBean {

@RabbitListener(queues = "someQueue")
public void processMessage(String content) {
// ...
}

}

请查看@EnableRabbit的Javadoc以获得更多详细信息。

如果你需要创建多个RabbitListenerContainerFactory实例或者你想覆盖默认的,Spring Boot提供了SimpleRabbitListenerContainerFactoryConfigurerDirectRabbitListenerContainerFactoryConfigurer,你可以用它来初始化一个SimpleRabbitListenerContainerFactoryDirectRabbitListenerContainerFactory,并且与自动配置的使用相同的设置。

不管选择哪种容器类型,这两个bean都是由自动配置公开的。

例如,下面的内容展示了另一个使用特定MessageConverter的工厂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
static class RabbitConfiguration {

@Bean
public SimpleRabbitListenerContainerFactory myFactory(
SimpleRabbitListenerContainerFactoryConfigurer configurer) {
SimpleRabbitListenerContainerFactory factory =
new SimpleRabbitListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setMessageConverter(myMessageConverter());
return factory;
}

}

然后你可以在任何 @RabbitListener 注解的方法中使用:

1
2
3
4
5
6
7
8
9
@Component
public class MyBean {

@RabbitListener(queues = "someQueue", containerFactory="myFactory")
public void processMessage(String content) {
// ...
}

}

你可以启用重试来处理监听器抛出异常的情况。默认情况下使用RejectAndDontRequeueRecoverer但是你可以定义一个自己的MessageRecoverer。当重试耗尽时,消息将被拒绝,并且被丢弃或者如果代理配置了交换,则消息将路由到死信交换。默认情况下,重试是禁用的。

如果没启用重试,侦听器抛出异常,默认情况下,将为无限重试分发。你可以通过两种方式来修改该行为;将defaultRequeueRejected属性设置为false,并尝试零重新交付;或者,抛出AmqpRejectAndDontRequeueException表示该消息应该被拒绝。这是在启用重试并且达到了最大的交付尝试时所使用的机制

Apache Kafka

通过提供spring-kafka项目的自动配置来支持Apache Kafka

Kafka 的配置由spring.kafka.*的外部配置属性控制。例如,你可以在application.properties中声明以下部分:

1
2
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.group-id=myGroup

更多受支持的选项,请参见KafkaProperties

发送消息

Spring的KafkaTemplate是自动配置的,你可以直接在你自己的bean中注入它们:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class MyBean {

private final KafkaTemplate kafkaTemplate;

@Autowired
public MyBean(KafkaTemplate kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}

// ...

}

接收消息

当Apache Kafka基础组件存在时,任何bean都可以用@卡夫卡式的注解来创建一个监听器端点。如果没有定义KafkaListenerContainerFactory,会自动配置一个默认的,并且key定义在spring.kafka.listener.*中。

以下组件在someTopic主题上创建一个监听器端点:

1
2
3
4
5
6
7
8
9
@Component
public class MyBean {

@KafkaListener(topics = "someTopic")
public void processMessage(String content) {
// ...
}

}

额外的Kafka属性

自动配置支持的属性在附录A,通用应用程序属性中可以查看。请注意,这些属性大部分(连字符或驼峰形式)都直接映射到Apache Kafka的点状属性,请参阅Apache Kafka文档以获得详细信息。

这些属性的前几项都适用于生产者和消费者,但如果希望使用不同的值,则可以在生产者或消费者级别指定。Apache Kafka指定了具有重要性的属性:HIGH,MEDIUM和LOW。Spring Boot自动配置支持所有HIGH重要性的属性,一些选择的MEDIUM和LOW,以及任何没有默认值的属性。

Kafka 所支持的属性中只有一个子集可以通过KafkaProperties类来获得。如果你希望配置没有直接支持的生产者或消费者的附加属性,请使用以下内容:

1
2
3
4
spring.kafka.properties.foo.bar=baz
spring.kafka.consumer.properties.fiz.buz=qux
# 这里应该是.吧?
spring,kafka.producer.properties.baz.qux=fiz

这设置了普通的foo.bar Kafka 属性为baz(适用于生产者和消费者),消费者fiz.buz属性为qux和生产者baz.qux属性为fiz

以这种方式设置的属性将覆盖Spring Boot 显式支持的任何配置项。

使用“RestTemplate” 调用REST 服务

如果需要在应用程序中调用远程REST服务,那么可以使用Spring Framework的RestTemplate类。由于RestTemplate实例通常需要在被使用之前进行定制,所以Spring Boot 不提供任何单独的自动配置的RestTemplate bean。但是,它可以自动配置一个RestTemplateBuilder,当需要时可以使用它创建RestTemplate实例。自动配置的RestTemplateBuilder将确保将合理的HttpMessageConverters应用于RestTemplate实例。

下面是一个典型的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class MyService {

private final RestTemplate restTemplate;

public MyBean(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}

public Details someRestCall(String name) {
return this.restTemplate.getForObject("/{name}/details", Details.class, name);
}

}

RestTemplateBuilder包含许多有用的方法,这些方法可用于快速配置RestTemplate。例如,要添加基本的身份验证支持,你可以使用builder.basicAuthorization("user", "password").build()

定制RestTemplate

RestTemplate定制主要有三种方法,这取决于你希望定制的范围有多大。

要使任何定制的范围尽可能窄,只需插入自动配置的RestTemplateBuilder,然后根据需要调用它的方法就行了。每个方法调用都返回一个新的RestTemplateBuilder实例,因此定制只会影响该构建器的使用。为了在应用层面定制,可以使用额外的定制化定制一个RestTemplateCustomizer bean。所有这些bean都将自动注册到自动配置的RestTemplateBuilder中,并将应用于使用它构建的任何模板。

下面是一个定制化器的例子,它配置了除192.168.0.5之外的所有主机使用代理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static class ProxyCustomizer implements RestTemplateCustomizer {

@Override
public void customize(RestTemplate restTemplate) {
HttpHost proxy = new HttpHost("proxy.example.com");
HttpClient httpClient = HttpClientBuilder.create()
.setRoutePlanner(new DefaultProxyRoutePlanner(proxy) {

@Override
public HttpHost determineProxy(HttpHost target,
HttpRequest request, HttpContext context)
throws HttpException {
if (target.getHostName().equals("192.168.0.5")) {
return null;
}
return super.determineProxy(target, request, context);
}

}).build();
restTemplate.setRequestFactory(
new HttpComponentsClientHttpRequestFactory(httpClient));
}

}

最后,最极端的(也是很少使用的)选择是创建你自己的RestTemplateBuilder bean。这将关闭RestTemplateBuilder的自动配置,并防止使用任何RestTemplateCustomizer bean。

使用“WebClient” 调用REST 服务

定制WebClient

校验

只要在类路径上发现JSR-303的实现(例如Hibernate validator)就会自动启用Bean校验1.1支持的方法校验特性。这允许在bean方法的参数和/或返回值上使用javax.valication约束注解。有这些注解的方法的目标类需要在类级别上用@Validated注解进行注解,因为内联约束注解需要搜索它们的方法。

例如,以下服务触发第一个参数的验证,确保它的大小在8到10之间:

1
2
3
4
5
6
7
8
9
10
@Service
@Validated
public class MyBean {

public Archive findByCodeAndAuthor(@Size(min = 8, max = 10) String code,
Author author) {
...
}

}

邮件发送

Spring框架为使用JavaMailSender接口发送电子邮件提供了一种简单的抽象,Spring Boot 为它提供了自动配置以及启动模块。

查看参考文档以了解如何使用JavaMailSender的详细说明

如果spring.mail.host和相关的库(如spring-boot-starter-mail所定义的)可用,如果不存在,就会创建一个默认的JavaMailSender。sender可以通过spring.mail命名空间中的配置项进一步定制,查看MailProperties以获得更多细节。

特别是,某些默认的超时值是无限的,你可能想要更改它,以避免被无响应的邮件服务器阻塞线程:

1
2
3
spring.mail.properties.mail.smtp.connectiontimeout=5000
spring.mail.properties.mail.smtp.timeout=3000
spring.mail.properties.mail.smtp.writetimeout=5000

JTA 分布式事务

Spring Boot通过使用Atomikos或Bitronix嵌入式事务管理器来支持跨多个XA资源的分布式JTA事务。在部署到合适的Java EE应用服务器时,JTA事务也得到了支持。当检测到JTA环境时,将使用Spring的JtaTransactionManager来管理事务。将对自动配置的JMS、数据源和JPA bean进行升级,以支持XA事务。你可以使用诸如@Transactional之类的标准Spring 方式来参与分布式事务。如果你在一个JTA环境中,并且仍然希望使用本地事务,那么你可以设置spring.jta.enabled属性设置为false来禁用JTA自动配置。

使用Atomikos 事务管理器

Atomikos是一个流行的开源事务管理器,可以嵌入到Spring Boot应用程序中。你可以使用spring-boot-starter-jta-atomikos Starter来获取适当的Atomikos库。Spring Boot将自动配置Atomikos,并确保将适当的depends-on设置应用到你的Spring bean上,以正确的启动和关闭顺序。

在缺省情况下,Atomikos事务日志将被写入到应用程序主目录(应用程序jar文件所在的目录)的transaction-logs目录中。你可以通过在application.properties文件中设置一个spring.jta.log-dir属性来定制这个目录。spring.jta.atomikos.properties开始的属性也可以用来定制于Atomikos UserTransactionServiceImp。请参阅AtomikosProperties Javadoc以获得完整的详细信息。

为了确保多个事务管理器能够安全地协调相同的资源管理器,每个Atomikos实例必须配置一个惟一的ID,默认情况下,这个ID是Atomikos正在运行的机器的IP地址。为了确保产品的唯一性,你应该为应用程序的每个实例配置spring.jta.transaction-manager-id属性使用不同的值。

使用Bitronix 事务管理器

使用Narayana 事务管理器

使用Java EE管理的事务管理器

混合XA和非XA JMS连接

支持另一个嵌入式事务管理器

Hazelcast

Quartz Scheduler

Spring引导为使用Quartz调度器提供了几项便利,包括spring-boot-starter-quartz ‘Starter’。如果可以使用Quartz,则将自动配置一个Scheduler(通过SchedulerFactoryBean抽象层)。

以下类型的bean将被自动获取并与Scheduler关联:

  • JobDetail:定义了一个特定的Job。JobDetail实例可以通过JobBuilder API轻松构建。
  • Calendar
  • Trigger:定义特定job何时触发。

默认情况下,将使用一个内存中的JobStore。但是,如果你的应用程序中有DataSource bean和配置了spring.quartz.job-store-type属性,则可以配置一个基于jdbc的存储:

1
spring.quartz.job-store-type=jdbc

当使用jdbc存储时,可以在启动时初始化相关表:

1
spring.quartz.jdbc.initialize-schema = true

默认情况下会检测到数据库,并使用Quartz库提供的标准脚本进行初始化。还可以使用spring.quartz.jdbc.schema属性提供自定义脚本。

Quartz调度器的配置可以使用Quartz 配置属性(参见spring.quartz.properties.*)和允许编程式定制SchedulerFactoryBeanSchedulerFactoryBeanCustomizer bean。

Job可以定义set方法来注入数据映射属性。常规bean也可以以类似的方式注入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SampleJob extends QuartzJobBean {

private MyService myService;
private String name;

// Inject "MyService" bean
public void setMyService(MyService myService) { ... }

// Inject the "name" job data property
public void setName(String name) { ... }

@Override
protected void executeInternal(JobExecutionContext context)
throws JobExecutionException {
...
}

}

Spring Integration

Spring Boot为使用Spring Integration提供了一些方便,包括spring-boot-starter-integration‘Starter’。Spring Integration为消息传递提供了抽象,还提供了HTTP、TCP等其他传输协议。如果你的类路径上有Spring Integration,它将通过@EnableIntegration注解进行初始化。

Spring Boot还将配置一些由附加的Spring Integration模块所触发的特性。如果“spring-integration-jmx”也在类路径上,消息处理统计信息将在JMX上发布。如果“spring-integration-jdbc”可用,则可以在启动时创建默认的数据库表:

1
spring.integration.jdbc.initializer.enabled=true

查看IntegrationAutoConfigurationIntegrationProperties类了解更多的细节。

Spring Session

Spring Boot为许多存储提供了Spring Session 自动配置:

  • JDBC
  • Redis
  • Hazelcast
  • HashMap

如果可以使用Spring Session ,那么你必须选择你希望使用的StoreType来存储会话。例如,使用JDBC作为后端存储,您可以将应用程序配置为如下:

1
spring.session.store-type=jdbc

你可以通过将store-type设置为none来禁用Spring Session。

每种存储都有特定的附加设置。例如,可以为jdbc存储定制表的名称:

1
spring.session.jdbc.table-name=SESSIONS

JMX监视和管理

Java管理扩展(JMX)提供了一种监视和管理应用程序的标准机制。在默认情况下,Spring Boot将创建一个bean id为‘mbeanServer’的MBeanServer,并公开任何使用Spring JMX注解注解的bean(@ManagedResource@ManagedAttribute@ManagedOperation)。

有关更多详细信息,请参阅JmxAutoConfiguration类。

测试

Spring Boot提供了许多实用程序和注释,以便在测试应用程序时提供帮助。测试支持由两个模块提供;spring-boot-test包含核心项目,spring-boot-test-autoconfigure支持测试的自动配置。

大多数开发人员只会使用spring-boot-starter-test “Starter”,它既引入了Spring Boot测试模块,也引用了JUnit、AssertJ、Hamcrest和其他一些有用的库。

测试范围的依赖

如果你使用spring-boot-starter-test‘Starter(在test``scope),你将会找到以下所提供的库:

  • JUnit - Java应用程序单元测试的实际标准
  • Spring Test和Spring Boot Test - Spring Boot应用程序的实用工具和集成测试支持
  • AssertJ - 流式的断言库
  • Hamcrest - 一个对象匹配库(也称为约束或谓词)。
  • Mockito - 一个Java mocking框架。
  • JSONassert - 一个用于JSON的断言库。
  • JsonPath - JSON的XPath库

测试Spring应用

依赖注入的一个主要优点是,它应该使你的代码更容易进行单元测试。你可以使用new操作符简单地实例化对象,而不需要涉及Spring。还可以使用模拟对象而不是实际依赖项。

通常,你需要跨过“单元测试”,并开始“集成测试”(Spring ApplicationContext实际上涉及到了这个过程)。在不需要部署应用程序或需要连接到其他组件的情况下,能够执行集成测试是很有用的。

Spring Framework包含一个专用的测试模块,用于进行这样的集成测试。你可以直接声明依赖项org.springframework:spring-test或使用spring-boot-starter-test “Starter”来传递依赖。

如果你之前没有使用过Spring-test模块,你应该阅读Spring Framework参考文档的相关部分。

测试Spring Boot应用

Spring Boot 应用程序只是一个Spring ApplicationContext,所以没有什么特别的事情需要做来测试超出了你通常使用的Spring上下文的范围的部分。需要注意的一件事是,如果你使用SpringApplication来创建它,那么Spring Boot的外部属性、日志记录和其他特性在默认情况下是默认安装的。

Spring Boot提供了一个@SpringBootTest注解 ,当你需要Spring Boot特性时,它可以作为标准spring-test``@ContextConfiguration注释的替代品。该注解通过在SpringApplication中创建ApplicationContext 来工作。

你可以使用@SpringBootTestwebEnvironment属性来进一步细化你的测试将如何运行:

  • MOCK - 加载一个WebApplicationContext并提供一个模拟servlet环境。在使用该注解时,不会启动嵌入式servlet容器。如果在你的类路径中不存在servlet api,该模式将透明地返回创建常规的非web ApplicationContext。可以与@AutoConfigureMockMvc 一起使用基于MockMvc的应用程序测试。
  • RANDOM_PORT - 加载一个ServletWebServerApplicationContext和提供了一个真正的servlet环境。嵌入式的servlet容器将被启动并在一个随机的端口上监听。
  • DEFINED_PORT - 加载一个ServletWebServerApplicationContext和提供了一个真正的servlet环境。嵌入的servlet容器将被启动并监听一个已定义的端口(例如在你的application.properties或默认端口8080)。
  • NONE - 使用SpringApplication加载一个ApplicationContext,但是不提供任何servlet环境(模拟或其他)。

如果你的测试是@Transactional,它将在默认情况下在每个测试方法结束时回滚事务。如果你将此设置与RANDOM_PORT DEFINED_PORT结合使用,那么在服务器上启动的任何事务都不会回滚,因为测试运行在不同的线程中,而不是服务器处理。

除了@SpringBootTest 而外,还提供了一些其他的注解,用于测试应用程序的特定部分。详情见下文。

不要忘记将@RunWith(SpringRunner.class)添加到你的测试中,否则注解将被忽略。

检测测试配置

如果你熟悉Spring Test Framework,则可以使用@ContextConfiguration(classes=...)来指定要加载哪个Spring @Configuration。或者,你可能经常在测试中使用嵌套的@Configuration类。

在测试Spring Boot应用程序时,通常不需要这样做。Spring Boot的@*Test注解将自动搜索你的主配置,只要您不显式地定义一个配置。

搜索算法在包含测试的包中起作用,直到找到一个@SpringBootApplication@SpringBootConfiguration 注解的类。只要你以一种合理的方式构造你的代码,你的主配置通常就会被发现。如果你想要定制主配置,你可以使用一个嵌套的@TestConfiguration 类。不像一个嵌套的@Configuration 类,它将代替应用程序的主要配置,一个嵌套的@TestConfiguration 类将被用于应用程序的主要配置。

Spring的测试框架将在测试之间缓存应用程序上下文。因此,只要你的测试共享相同的配置(不管它是如何被发现的),加载上下文的潜在时间消耗过程只会发生一次。

排除测试配置

如果你的应用程序使用组件扫描,例如,如果您使用@SpringBootApplication @ComponentScan,那么你可能会发现仅为特定测试而创建的顶级配置类意外地在各处被发现。

正如我们已经看到的,@TestConfiguration 可以用于测试的内部类以定制主配置。当被放置在顶级类上时,@TestConfiguration 表示src/test/java中的类不应该通过扫描来获取。然后,可以显式地导入该类:

1
2
3
4
5
6
7
8
9
10
11
@RunWith(SpringRunner.class)
@SpringBootTest
@Import(MyTestsConfiguration.class)
public class MyTests {

@Test
public void exampleTest() {
...
}

}

如果你直接使用@ComponentScan(即不是通过@SpringBootApplication),你将需要使用它注册TypeExcludeFilter。请参阅Javadoc以获得详细信息。

使用随机端口

如果你需要为测试启动一个完整的运行服务器,我们建议你使用随机端口。如果你使用的是@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT),你的测试运行时将随机选择一个可用的端口。

可以使用@LocalServerPort注解来将实际的端口注入到你的测试中。为方便起见,需要对启动服务器进行REST调用的测试可以另外使用@Autowire一个TestRestTemplate,它将解析与运行的服务器的相对链接。

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
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.junit4.SpringRunner;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class RandomPortExampleTests {

@Autowired
private TestRestTemplate restTemplate;

@Test
public void exampleTest() {
String body = this.restTemplate.getForObject("/", String.class);
assertThat(body).isEqualTo("Hello World");
}

}

模拟和监视 bean

在运行测试时,有时需要模拟应用程序上下文中的某些组件。例如,在开发期间,你可能会对一些不可用的远程服务有一个facade。当你想要模拟在真实环境中可能很难触发的故障时,mock也会很有用。

Spring Boot包含一个@MockBean 注解,它可用于在ApplicationContext中定义一个bean的Mockito 模拟。你可以使用注解来添加新的bean,或者替换一个现有的bean定义。注解可以直接用于测试类、测试中的字段或`@Configuration``类和字段。当在一个字段中使用时,也将对创建的模拟实例进行注入。模拟bean在每个测试方法之后自动重置。

只要你的测试使用了Spring Boot的一个测试注解(也就是@SpringBootTest),这个特性就会自动启用。为了使用不同的配置,需要显式地添加监听器:

1
@TestExecutionListeners(MockitoTestExecutionListener.class)

这里有一个典型的例子,我们用一个模拟实现来替换一个现有的RemoteService bean。

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
import org.junit.*;
import org.junit.runner.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.context.*;
import org.springframework.boot.test.mock.mockito.*;
import org.springframework.test.context.junit4.*;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {

@MockBean
private RemoteService remoteService;

@Autowired
private Reverser reverser;

@Test
public void exampleTest() {
// RemoteService has been injected into the reverser bean
given(this.remoteService.someCall()).willReturn("mock");
String reverse = reverser.reverseSomeCall();
assertThat(reverse).isEqualTo("kcom");
}

}

此外,你还可以使用@SpyBean 将任何现有的bean包装成一个Mockito spy。请参阅Javadoc以获得完整的详细信息。

自动配置测试

Spring Boot的自动配置系统适用于应用程序,但有时对测试来说有点太大了。只加载需要测试应用程序“切片”的配置部分通常是有帮助的。例如,你可能想要测试Spring MVC控制器是否正确地映射到url,并且你不希望在这些测试中涉及到数据库调用;或者你可能想要测试JPA实体,并且当这些测试运行时,你对web层不感兴趣。

spring-boot-test-autoconfigure模块包含许多注解,可用于自动配置这些“切片”。它们中的每一个都以类似的方式工作,提供了一个@…Test注解,它加载了ApplicationContext和一个或多个可用于定制自动配置设置的@AutoConfigure...注解。

每个切片都加装一组非常受限制的自动配置类。如果你需要排除其中的一个,那么大多数@…Test注解提供了一个excludeAutoConfiguration 属性。或者,你可以使用@ImportAutoConfiguration#exclude

还可以使用@AutoConfigure..注解和标准的@SpringBootTest注解。如果你对“切片”应用程序不感兴趣但是你需要一些自动配置的测试bean,你可以使用这种组合。

自动配置JSON测试

要测试对象JSON序列化和反序列化,你可以使用@JsonTest注释。@JsonTest将自动配置Jackson ObjectMapper、任何@JsonComponent bean和任何Jackson Modules。它也会对Gson进行配置,如果你碰巧用的不是杰克逊。如果你需要配置自动配置的元素,你可以使用@AutoConfigureJsonTesters注解。

Spring Boot包括基于AssertJ 的Helper,与JSONassert和JsonPath库一起工作,以检查JSON是否符合预期。JacksonTesterGsonTesterBasicJsonTester类可以分别用于Jackson、Gson和字符串。在使用@JsonTest时,测试类上的任何helper字段都可以@Autowired

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
import org.junit.*;
import org.junit.runner.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.json.*;
import org.springframework.boot.test.context.*;
import org.springframework.boot.test.json.*;
import org.springframework.test.context.junit4.*;

import static org.assertj.core.api.Assertions.*;

@RunWith(SpringRunner.class)
@JsonTest
public class MyJsonTests {

@Autowired
private JacksonTester<VehicleDetails> json;

@Test
public void testSerialize() throws Exception {
VehicleDetails details = new VehicleDetails("Honda", "Civic");
// Assert against a `.json` file in the same package as the test
assertThat(this.json.write(details)).isEqualToJson("expected.json");
// Or use JSON path based assertions
assertThat(this.json.write(details)).hasJsonPathStringValue("@.make");
assertThat(this.json.write(details)).extractingJsonPathStringValue("@.make")
.isEqualTo("Honda");
}

@Test
public void testDeserialize() throws Exception {
String content = "{\"make\":\"Ford\",\"model\":\"Focus\"}";
assertThat(this.json.parse(content))
.isEqualTo(new VehicleDetails("Ford", "Focus"));
assertThat(this.json.parseObject(content).getMake()).isEqualTo("Ford");
}

}

JSON helper类也可以直接在标准单元测试中使用。如果没有使用@JsonTest,只需在@Before方法中调用helper的initFields方法。

@JsonTest启用的自动配置列表可以在附录中找到。

自动配置Spring MVC测试

为了测试Spring MVC控制器如你预期的工作,你可以使用@WebMvcTest注解。@WebMvcTest将自动配置Spring MVC基础组件和限制扫描@ Controller,@ControllerAdvice,@JsonComponent,Filter,WebMvcConfigurer HandlerMethodArgumentResolver bean。在使用该注解时,不会对常规的@Component bean进行扫描。

通常,@WebMvcTest将局限于单个控制器,并与@MockBean结合使用,为需要的协作者提供模拟实现。@WebMvcTest也自动配置MockMvc。Mock MVC提供了一种强大的方法来快速测试MVC控制器,而不需要启动一个完整的HTTP服务器。

你还可以使用@AutoConfigureMockMvc注解在非@WebMvcTest(如SpringBootTest)中自动配置MockMvc

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
import org.junit.*;
import org.junit.runner.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.web.servlet.*;
import org.springframework.boot.test.mock.mockito.*;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringRunner.class)
@WebMvcTest(UserVehicleController.class)
public class MyControllerTests {

@Autowired
private MockMvc mvc;

@MockBean
private UserVehicleService userVehicleService;

@Test
public void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk()).andExpect(content().string("Honda Civic"));
}

}

如果你需要配置自动配置的元素(例如,servlet过滤器何时生效),你可以在@AutoConfigureMockMvc注解中使用属性。

如果使用HtmlUnit或Selenium,自动配置还将提供一个HtmlUnit WebClient bean和/或一个WebDriver bean。下面是一个使用HtmlUnit的例子:

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
import com.gargoylesoftware.htmlunit.*;
import org.junit.*;
import org.junit.runner.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.boot.test.autoconfigure.web.servlet.*;
import org.springframework.boot.test.mock.mockito.*;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;

@RunWith(SpringRunner.class)
@WebMvcTest(UserVehicleController.class)
public class MyHtmlUnitTests {

@Autowired
private WebClient webClient;

@MockBean
private UserVehicleService userVehicleService;

@Test
public void testExample() throws Exception {
given(this.userVehicleService.getVehicleDetails("sboot"))
.willReturn(new VehicleDetails("Honda", "Civic"));
HtmlPage page = this.webClient.getPage("/sboot/vehicle.html");
assertThat(page.getBody().getTextContent()).isEqualTo("Honda Civic");
}

}

在默认情况下,Spring Boot将把WebDriver bean放入一个特殊的“范围”中,以确保在每次测试之后driver都会结束,并注入一个新的实例。如果你不想要这种行为,您可以将@Scope("singleton")添加到你的WebDriver``@bean定义中。

@WebMvcTest启用的自动配置列表可以在附录中找到

自动配置Spring WebFlux测试

自动配置JPA Data测试

如果你想要测试JPA应用程序,可以使用@DataJpaTest。默认情况下,它将配置一个内存中的嵌入式数据库,扫描@Entity类并配置Spring Data JPA存储库。常规的@Component bean不会被加载到ApplicationContext中。

JPA测试都是事务性的并且在每个测试结束的时候都将回滚,请参阅Spring参考文档中的相关部分了解更多细节。如果这不是你想要的,您可以禁用一个测试或整个类的事务管理,就像下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringRunner.class)
@DataJpaTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class ExampleNonTransactionalTests {

}

JPA测试还可以注入一个TestEntityManager bean,它为专门为测试设计的标准JPA EntityManager提供了另一种选择。如果你想在@DataJpaTests外使用TestEntityManager 还可以使用@AutoConfigureTestEntityManager注解。如果需要的话,还可以使用JdbcTemplate

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
import org.junit.*;
import org.junit.runner.*;
import org.springframework.boot.test.autoconfigure.orm.jpa.*;

import static org.assertj.core.api.Assertions.*;

@RunWith(SpringRunner.class)
@DataJpaTest
public class ExampleRepositoryTests {

@Autowired
private TestEntityManager entityManager;

@Autowired
private UserRepository repository;

@Test
public void testExample() throws Exception {
this.entityManager.persist(new User("sboot", "1234"));
User user = this.repository.findByUsername("sboot");
assertThat(user.getUsername()).isEqualTo("sboot");
assertThat(user.getVin()).isEqualTo("1234");
}

}

内存中嵌入式数据库通常适用于测试,因为它们是快速的,不需要任何开发人员安装。然而,如果你喜欢在一个真正的数据库上运行测试可以使用@AutoConfigureTestDatabase注解:

1
2
3
4
5
6
7
8
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace=Replace.NONE)
public class ExampleRepositoryTests {

// ...

}

附录中可以找到由@DataJpaTest启用的自动配置列表。

自动配置JDBC测试

@JdbcTest 类似于@DataJpaTest,但对于纯jdbc相关的测试来说是类似的。默认情况下,它还将配置一个内存中的嵌入式数据库和一个JdbcTemplate。常规的@Component bean不会被加载到ApplicationContext中。

JDBC测试在缺省情况下是事务性的,并且在每个测试结束时回滚,请参阅Spring参考文档中的相关部分以获得更多详细信息。如果这不是你想要的,你可以禁用一个测试或整个类的事务管理,就像下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringRunner.class)
@JdbcTest
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class ExampleNonTransactionalTests {

}

如果你喜欢你的测试运行在一个真正的数据库,你可以你DataJpaTest一样使用@AutoConfigureTestDatabase 注解。

附录中可以找到由@JdbcTest启用的自动配置列表。

自动配置jOOQ测试

自动配置MongoDB Data测试

自动配置Neo4j Data测试

自动配置Redis Data测试

如果您想测试Redis应用程序,可以使用@DataRedisTest。默认情况下,它将扫描@RedisHash类,并配置Spring Data Redis存储库。常规的@Component bean不会被加载到ApplicationContext中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@DataRedisTest
public class ExampleDataRedisTests {

@Autowired
private YourRepository repository;

//
}

@DataRedisTest启用的自动配置列表可以在附录中找到。

自动配置LDAP Data测试

自动配置REST客户端

如果你想测试REST客户端,可以使用@RestClientTest注释。默认情况下,它将自动配置Jackson和GSON支持,配置RestTemplateBuilder,并添加对MockRestServiceServer的支持。你想要测试的特定bean应该使用@RestClientTestvaluecomponents属性指定:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RunWith(SpringRunner.class)
@RestClientTest(RemoteVehicleDetailsService.class)
public class ExampleRestClientTest {

@Autowired
private RemoteVehicleDetailsService service;

@Autowired
private MockRestServiceServer server;

@Test
public void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails()
throws Exception {
this.server.expect(requestTo("/greet/details"))
.andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
String greeting = this.service.callRestService();
assertThat(greeting).isEqualTo("hello");
}

}

附录中可以找到由@RestClientTest启用的自动配置列表。

自动配置的Spring REST Doc测试

如果您想在测试中使用Spring REST Doc,可以使用@AutoConfigureRestDocs注释。它将自动配置MockMvc,以使用Spring REST Doc,并消除Spring REST Doc的JUnit规则的需求。

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
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringRunner.class)
@WebMvcTest(UserController.class)
@AutoConfigureRestDocs
public class UserDocumentationTests {

@Autowired
private MockMvc mvc;

@Test
public void listUsers() throws Exception {
this.mvc.perform(get("/users").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andDo(document("list-users"));
}

}

@AutoConfigureRestDocs可以用来覆盖默认的输出目录(如果你使用的是Maven则为target/generated-snippets或如果你使用的是Gradle则为build/generated-snippets)。它还可以用于配置在任何文档化的URI中出现的主机、scheme和端口。如果你需要控制更多的Spring REST Doc配置可以使用RestDocsMockMvcConfigurationCustomizer bean:

1
2
3
4
5
6
7
8
9
10
@TestConfiguration
static class CustomizationConfiguration
implements RestDocsMockMvcConfigurationCustomizer {

@Override
public void customize(MockMvcRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}

}

如果你想利用Spring REST Doc的参数化输出目录支持,你可以创建一个RestDocumentationResultHandler bean。自动配置将调用这个结果处理器的alwaysDo,从而导致每个MockMvc调用来自动生成默认的代码片段:

1
2
3
4
5
6
7
8
9
@TestConfiguration
static class ResultHandlerConfiguration {

@Bean
public RestDocumentationResultHandler restDocumentation() {
return MockMvcRestDocumentation.document("{method-name}");
}

}

使用Spock来测试Spring Boot应用程序

如果你想用Spock来测试一个Spring Boot应用程序,你应该增加Spock的spock-spring模块的依赖到你的应用程序构建中。spock-spring将Spring的测试框架集成到Spock中。建议你使用Spock 1.1或更高版本,以便从最近对Spock Spring框架和Spring Boot集成的一些改进中获益。请参考Spock的Spring模块的文档,以了解更多细节。

测试工具

一些测试工具类被打包为spring-oot的一部分,这在测试应用程序时通常是有用的。

ConfigFileApplicationContextInitializer

ConfigFileApplicationContextInitializer是一个 ApplicationContextInitializer,适用于测试加载Spring Boot application.properties文件。当你不需要@SpringBootTest提供的全部功能时,你可以使用它。

1
2
@ContextConfiguration(classes = Config.class,
initializers = ConfigFileApplicationContextInitializer.class)

单独使用ConfigFileApplicationContextInitializer不会提供支持@Value("${…}")注入。它的唯一工作就是确保应用application.properties文件被加载到Spring的环境中。对于@Value支持你需要另外配置一个PropertySourcesPlaceholderConfigurer或使用会自动配置的@SpringBootTest

EnvironmentTestUtils

EnvironmentTestUtils允许你快速为ConfigurableEnvironmentConfigurableApplicationContext添加属性。只需用使用key=value字符串来调用它:

1
EnvironmentTestUtils.addEnvironment(env, "org=Spring", "name=Boot");

OutputCapture

OutputCapture是一个JUnit Rule,你可以使用它来捕获System.outSystem.err输出。只需将捕获声明为一个@Rule,然后使用toString()断言:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;

import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;

public class MyTest {

@Rule
public OutputCapture capture = new OutputCapture();

@Test
public void testName() throws Exception {
System.out.println("Hello World!");
assertThat(capture.toString(), containsString("World"));
}

}

TestRestTemplate

TestRestTemplate是一个方便的Spring RestTemplate替代品,它在集成测试中很有用。你可以获得一个普通的模板,或者一个发送基本HTTP身份验证的模板(带有用户名和密码)。在任何一种情况下,模板都将以一种测试友好的方式运行,不会向服务器端错误抛出异常。建议使用Apache HTTP客户端(版本4.3.2或更高的),这不是强制的,如果在你有类路径中有这个库,那么TestRestTemplate将通过适当地配置客户端来响应。如果你确实使用了Apache的HTTP客户端,将启用一些额外的测试友好特性:

  • 重定向将不会被执行(因此你可以断言响应位置)
  • cookie将被忽略(因此模板是无状态的)

可以在你的集成测试中直接实例化TestRestTemplate:

1
2
3
4
5
6
7
8
9
10
11
public class MyTest {

private TestRestTemplate template = new TestRestTemplate();

@Test
public void testRequest() throws Exception {
HttpHeaders headers = template.getForEntity("http://myhost.com/example", String.class).getHeaders();
assertThat(headers.getLocation().toString(), containsString("myotherhost"));
}

}

或者,如果你正在使用@SpringBootTest注解的WebEnvironment.RANDOM_PORTWebEnvironment.DEFINED_PORT,你只需注入一个完全配置的TestRestTemplate并开始使用它。如果需要,可以通过RestTemplateBuilder来应用额外的定制。任何不指定主机和端口的URL都将自动连接到嵌入式服务器:

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
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTest {

@Autowired
private TestRestTemplate template;

@Test
public void testRequest() throws Exception {
HttpHeaders headers = template.getForEntity("/example", String.class).getHeaders();
assertThat(headers.getLocation().toString(), containsString("myotherhost"));
}

@TestConfiguration
static class Config {

@Bean
public RestTemplateBuilder restTemplateBuilder() {
return new RestTemplateBuilder()
.additionalMessageConverters(...)
.customizers(...);
}

}

}

WebSockets

Spring Boot 为嵌入式Tomcat(8和7)、Jetty 9和Undertow提供WebSockets 自动配置。如果你正在将一个war文件部署到一个独立的容器中,Spring Boot假设容器将负责它的WebSocket支持的配置。

Spring框架提供了丰富的WebSocket支持,可以通过spring-boot-starter-websocket模块轻松访问。

Web Services

Spring Boot提供了Web服务的自动配置,因此所有需要的都是定义你的端点。

Spring Web服务的特性可以通过spring-boot-starter-webservices模块轻松地访问。

创建自己的自动配置

如果你在一家开发共享库的公司工作,或者你在一个开源或商业库公司工作,你可能想要开发你自己的自动配置。自动配置类可以捆绑在外部jar中,还可以被Spring Boot获取。

自动配置可以与一个“starter”相关联,它提供了自动配置代码以及你将使用的典型库。我们将首先介绍你需要了解关于构建你自己的自动配置的内容,然后我们将继续介绍创建自定义starter所需的典型步骤

这里有一个演示项目可以展示如何一步一步地创建一个starter。

理解自动配置bean

在外壳之下,自动配置是用标准的@Configuration类实现的。当自动配置应该应用时,还会使用附加的@Conditional注解来约束。通常,自动配置类使用@ConditionalOnClass@ConditionalOnMissingBean注解。这确保了只有在找到相关类和没有声明自己的@Configuration时才会自动配置。

你可以浏览spring-boot-autoconfigure的源代码,以查看我们提供的@Configuration类(参见META-INF/spring.factories文件)。

定位自动配置候选者

Spring Boot检查在你发布的jar中是否存在META-INF/spring.factories文件。应该在该文件以EnableAutoConfiguration key列出你的配置类:

1
2
3
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.mycorp.libx.autoconfigure.LibXAutoConfiguration,\
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration

如果你的配置需要按照特定的顺序使用,那么你可以使用@AutoConfigureAfter@AutoConfigureBefore注解。例如,如果你提供了web专用的配置,那么你的类可能需要在WebMvcAutoConfiguration之后应用。

如果你想要排序某些自动配置并且它们不应该对彼此有任何直接的了解,那么你也可以使用@AutoconfigureOrder。该注解与常规的@Order注解具有相同的语义,但提供了自动配置类的专用顺序。

自动配置只能以这种方式加载。确保它们是在特定的包空间中定义的,并且不要对它们使用组件扫描。

条件注解

你几乎总是希望在自动配置类中包含一个或多个@Conditional注解。@ConditionalOnMissingBean是一个常见的例子,它允许开发人员在不满意你的默认设置的情况下“覆盖”自动配置。

Spring Boot包含许多@Conditional注解,可以通过注解@Configuration类或单独的@Bean方法在自己的代码中重用。

类条件

@ConditionalOnClass@ConditionalOnMissingClass注解允许基于特定类的存在与否来是否包含该配置。由于使用ASM解析注解元数据,所以实际上可以使用value属性引用真正的类,即使这个类可能不会出现在正在运行的应用程序类路径中。如果你喜欢使用String值指定类名,那么也可以使用name属性。

如果你使用@ConditionalOnClass@ConditionalOnMissingClass作为元注解的一部分来组成你自己的组合注解,你必须使用name来指定类在这种情况下不处理。

Bean条件

@ConditionalOnBean@ConditionalOnMissingBean注解允许根据特定bean的存在或不存在来是否包含bean。你可以使用value属性来按类型指定bean,也可以通过name指定bean。search属性允许你在搜索bean时限制应该考虑的ApplicationContext层次结构。

当被放置在@Bean方法上时,目标类型默认为该方法的返回类型,例如:

1
2
3
4
5
6
7
8
@Configuration
public class MyAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public MyService myService() { ... }

}

在上面的示例中,如果在ApplicationContext中没有包含类型MyService的bean,那么将创建myService bean。

你需要非常谨慎地处理bean定义的顺序,因为这些条件是根据目前处理的内容进行计算的。出于这个原因,我们建议只在自动配置类上使用@ConditionalOnBean@ConditionalOnMissingBean注解(因为在添加了任何用户定义的bean定义之后,这些注解就会被加载)。

@ConditionalOnBean@ConditionalOnMissingBean不会阻止@Configuration类的创建。在类级别上使用这些条件相当于用注解标记每个包含的@Bean方法。

属性条件

@ConditionalOnProperty注解允许基于Spring Environment属性来是否包含配置。使用prefixname属性来指定应该检查的属性。默认情况下,任何存在且不等于false的属性将被匹配。你还可以使用havingValuematchIfMissing属性创建更高级的检查。

资源条件

@ConditionalOnResource注解只允许在特定的资源出现时才包含配置。资源可以指定使用通常的Spring 的约定,例如,file:/home/user/test.dat

Web应用条件

@@ConditionalOnWebApplication@ConditionalOnNotWebApplication注解允许取决于应用是一个“web应用”来是否包含配置。一个web应用是使用Spring WebApplicationContext,定义了一个session scope或拥有StandardServletEnvironment的任何应用。

SpEL表达式条件

@ConditionalOnExpression注解允许基于SpEL表达式的结果包含配置。

创建自己的starter

对于一个库,一个完整的Spring Boot starter可能包含以下组件:

  • 包含自动配置代码的autoconfigure模块
  • 启动器模块提供对 autoconfigure 模块的依赖,以及库和其他通常有用的附加依赖项。简而言之,添加这个starter应该足以开始使用该库。

如果你不需要将这两个关注点分开,你可以将自动配置代码和依赖项管理合并在一个模块中。

命名

请确保为你的starter提供适当的命名空间。不要以spring-boot 开头,即使你使用的是不同的Maven groupId。我们可能会在将来为你自动配置的东西提供官方支持。

这是一个经验法则。让我们假设你正在为“acme”创建一个starter,请将自动配置模块命名为acme-spring-boot-autoconfigure和starter命名为acme-spring-boot-starter。如果你只有一个组合了这两个模块的模块,那就使用acme-spring-boot-starter

此外,如果你的启动器提供了配置 key,那么为它们使用适当的命名空间。特别是,不要将你的key包含在Spring Boot 使用的命名空间中(例如server, management, spring等)。这些都是“我们的”,我们可以在将来改进/修改它们,这样可能破坏你的东西。

确保触发元数据生成,以便你的key也可以使用IDE辅助功能。你可能需要检查生成的元数据(META-INF/spring-configuration-metadata.json),以确保你的key被正确地记录。

自动配置模块

autoconfigure 模块包含了从这个库启动的所有必要的内容。它还可能包含配置key定义(@ConfigurationProperties)和任何可用于进一步定制组件如何初始化的回调接口。

你应该将对库的依赖项标记为可选的,这样你就可以更容易地在你的项目中包含autoconfigure模块。如果你这样做,将不会提供这些库,而且Spring Boot将默认关闭。

starter模块

starter是一个空jar。它的唯一目的是提供必要的依赖项来与库一起工作;把它看作需要什么上手的自以为是的观点(不懂)。

不要对starter添加的项目做出假设。 如果你自动配置的库通常需要其他starter,请提及它们。 如果可选依赖关系的数量高,则提供一组适当的默认依赖关系可能会很困难,因为你应避免为库的典型用法带来不必要的依赖关系。