本节提供了一些常见的“如何做…”类型的问题的答案,这些问题在使用Spring Boot时经常出现。 这绝对不是详尽的列表,但它确实涵盖了很多。

如果你有一个我们没在这里介绍的特定问题,你可能需要访问 stackoverflow.com 来查看是否有其他人已经提供了答案; 这也是提出新问题的好地方(请使用spring-boot标签)。

我们也很乐意扩展这一节;如果你想添加一个“操作指南”,你可以向我们发送一个pull request

Spring Boot 应用

创建自己的FailureAnalyzer

FailureAnalyzer``是在启动时拦截异常的好方法,并将其转换为可读的消息,将其封装到FailureAnalysis中。Spring Boot为应用程序上下文相关的异常、jsr-303验证等提供了这样的分析器。实际上,也很容易创建自己的分析器。

AbstractFailureAnalyzerFailureAnalyzer的一个方便的扩展,它检查异常中指定的异常类型的存在。你可以从它扩展,以便你的实现只有在实际存在时才有机会处理异常。如果出于某种原因,你无法处理异常,则返回null以给另一个实现一个处理异常的机会。

FailureAnalyzer的实现可以注册到META-INF/spring.factories,例如:ProjectConstraintViolationFailureAnalyzer:

1
2
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.ProjectConstraintViolationFailureAnalyzer

排除自动配置故障

Spring Boot 自动配置尽可能做“正确的事情”,但是有时候失败了并且很难告知为何失败。

在任何 Spring Boot ApplicationContext中有一个非常有用的ConditionEvaluationReport。如果启用了DEBUG日志输出,你将看到它。如果你使用spring-boot-actuator,也有一个autoconfig端点,该端点以JSON的格式呈现报表。使用它来调试应用程序,并查看Spring Boot 在运行时添加了哪些特性(没有哪些特性)。

可以通过查看源代码和Javadoc来回答更多的问题。下面是一些经验法则:

  • 查找名为*AutoConfiguration的类,并读取它们的源代码,特别是@Conditional*,以找出它们启用和何时启用的功能。添加-debug到命令行或系统属性-Ddebug,这样可以在控制台上打印应用程序中所做的所有自动配置决策的日志。在运行的Actuator 应用程序中,可以查看autoconfig端点(/application/autoconfig或类似的JMX的端点)获取相同的信息。
  • 寻找@ConfigurationProperties(例如ServerProperties)类,并从那里读取可用的外部配置选项。@ConfigurationProperties有一个name属性,它充当外部属性的前缀,因此ServerPropertiesprefix="server",其配置属性是server.port, server.address等。在一个运行的 Actuator 应用程序中,可以查看configprops端点。
  • 查找Binderbind方法的用法,来以一种轻松的方式从Environment中明确地拉出配置值。它经常被用作前缀。
  • 查找直接绑定到Environment@Value注解。
  • 查找@ConditionalOnExpression注释,它在SpEL表达式中有开关的功能,通常使用从Environment中解析的占位符来进行计算。

在开始之前自定义Environment或ApplicationContext

SpringApplication拥有 ApplicationListenersApplicationContextInitializers,用于定制上下文或环境。Spring Boot 从META-INF/spring.factories加载了许多这样的内部使用的自定义。有不止一种方法可以注册额外的:

  • 在运行之前,通过在SpringApplication上以编程的方式调用addListenersaddInitializers方法。
  • 通过设置context.initializer.classescontext.listener.classes来声明。
  • 通过添加META-INF/spring.factories来声明并打包成一个jar文件,所有应用程序都将其作为一个库使用。

SpringApplication向监听器发送一些特殊的ApplicationEvents(有些甚至在创建上下文之前),然后注册ApplicationContext所发布的事件的监听器。请参阅“Spring Boot特性”小节中的“应用程序事件和监听器”部分以获得完整的列表。

也可以在使用EnvironmentPostProcessor刷新应用程序上下文之前自定义Environment。每个实现都应该在META-INF/spring.factories中注册:

1
org.springframework.boot.env.EnvironmentPostProcessor=com.example.YourEnvironmentPostProcessor

该实现可以加载任意文件并将其添加到Environment中。例如,下面这个例子从类路径加载一个YAML配置文件:

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
public class EnvironmentPostProcessorExample implements EnvironmentPostProcessor {

private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();

@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
Resource path = new ClassPathResource("com/example/myapp/config.yml");
PropertySource<?> propertySource = loadYaml(path);
environment.getPropertySources().addLast(propertySource);
}

private PropertySource<?> loadYaml(Resource path) {
if (!path.exists()) {
throw new IllegalArgumentException("Resource " + path + " does not exist");
}
try {
return this.loader.load("custom-resource", path, null);
}
catch (IOException ex) {
throw new IllegalStateException(
"Failed to load yaml configuration from " + path, ex);
}
}

}

在默认情况下,Environment已经准备好了所有常见的Spring Boot 加载的属性。因此,可以从环境中获取文件的位置。这个例子在列表的末尾添加了custom-resource属性源,因此在其他任何位置中定义的键都是有优先级的。自定义的实现可以明确地定义其他顺序。

虽然在你的@SpringBootApplication上使用@PropertySource似乎很方便,而且很容易在Environment中加载自定义资源,但是我们并不推荐它,因为Spring Boot 会在ApplicationContext刷新之前准备好Environment。通过@PropertySource定义的任何key 都将被加载得太晚以至于不会对自动配置产生任何影响。

构建ApplicationContext层次结构(添加父或上下文)

你可以使用ApplicationBuilder类来创建父/子ApplicationContext层次结构。参见“Spring Boot 功能”部分的“流式构建器API”部分,了解更多信息。

创建非web应用

并非所有Spring 应用程序都必须是web应用程序(或web服务)。如果你想要在main方法中执行一些代码,但是也要启动一个Spring应用程序来设置要使用的基础组件,那么使用Spring Boot的SpringSpringApplication特性这事就很容易了。一个SpringApplication根据它是否需要一个web应用程序来更改它的ApplicationContext类。你可以做的第一件事就是将servlet API依赖从类路径中去除。如果你不能这样做(例如,你从相同的代码库运行两个应用程序),那么你可以在你的SpringApplication实例上显式地调用setWebEnvironment(false)方法,或者设置applicationContextClass属性(通过Java API或外部属性)。你希望作为业务逻辑运行的应用程序代码可以作为CommandLineRunner实现,并作为一个@Bean定义注入上下文中。

属性和配置

在构建时自动展开属性

你可以使用现有的构建配置自动地扩展它们,而不是在你的项目的构建配置中硬编码指定的一些属性。这在Maven和Gradle中都是可能的。

使用Maven自动展开属性

你可以使用资源过滤来自动展开Maven项目中的属性。如果你使用了spring-boot-starter-parent,那么你可以通过@..@占位符访问Maven项目属性。例如:

1
2
app.encoding=@project.build.sourceEncoding@
app.java.version=@java.version@

只有生产配置才能通过这种方式过滤(比如src/test/resources中不会过滤)。

如果你启用了addResources标志,spring-boot:run可以将src/main/resources直接添加到classpath中(出去热加载的目的)。这将绕过资源过滤和这个功能。你可以使用exec:java goal或者自定义这个插件的配置,查看插件使用页面获取更多细节。

如果你没有使用starter parent,你需要在pom.xml(在<build/>元素中)加入以下代码:

1
2
3
4
5
6
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>

和(<plugins/>中):

1
2
3
4
5
6
7
8
9
10
11
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<configuration>
<delimiters>
<delimiter>@</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration>
</plugin>

如果你在配置中使用了标准的Spring占位符(例如:${foo}),useDefaultDelimiters属性非常重要。如果没有设置为false,这些也将会被展开。

使用Gradle自动展开属性

你可以通过配置Java插件的processResources任务来自动展开Gradle项目的属性:

1
2
3
processResources {
expand(project.properties)
}

然后你可以通过占位符引用Gradle项目属性,例如:

1
2
app.name=${name}
app.description=${description}

Gradle的expand方法使用Groovy的SimpleTemplateEngine,它将转换${...}标志。${..}样式与Spring自己的属性占位符技术有冲突。为了一起使用Spring的属性占位符和自动展开,你需要转义\${..}Spring 的属性占位符。

外部化SpringApplication的配置

一个SpringApplication具有bean属性(主要是setter方法),因此当你创建应用程序时你可以使用它的Java API来修改其行为。或者你可以使用spring.main.*中的属性对配置进行外部化。例如,在application.properties中你可以:

1
2
spring.main.web-environment=false
spring.main.banner-mode=off

然后Spring Boot banner 将不会在启动时打印出来,并且这个应用将不会是web应用。

上面的例子还演示了允许在属性名中使用下划线(_)和斜杠(-)是多么的灵活。

定义在外部配置的属性将会覆盖通过Java API指定的值,除了用来创建ApplicationContext的sources。让我们看下面的应用

1
2
3
4
new SpringApplicationBuilder()
.bannerMode(Banner.Mode.OFF)
.sources(demo.MyApp.class)
.run(args);

使用了下面的配置:

1
2
spring.main.sources=com.acme.Config,com.acme.ExtraConfig
spring.main.banner-mode=console

实际的应用将会显示出banner(因为被配置覆盖了)并且·ApplicationContext使用3个source(以下面的顺序):demo.MyApp, com.acme.Config, com.acme.ExtraConfig

更改应用程序的外部属性的位置

默认情况下不同的source是以定义好的顺序(查看“Spring Boot特性”一节中外部配置了解确切的顺序)添加到SpringEnvironment的。

增强和修改这一功能的一个很好的方法是将@propertysource注解添加到你的应用程序sources。传递给SpringApplication静态方法,并使用setSources()添加的这些类将被检查是否有PropertySources,如果它们有,那么这些属性将被尽早添加到Environment中,以便在ApplicationContext生命周期的所有阶段中使用。以这种方式添加的属性比使用默认位置(如application.Properties)、系统属性、环境变量或命令行添加的任何属性优先级都要低。

你也可以提供系统属性(或环境变量)来改变这一行为:

  • spring.config.name (SPRING_CONFIG_NAME),默认application为这个文件名的根。
  • spring.config.location (SPRING_CONFIG_LOCATION) 是要加载的文件(例如classpath资源或URL)。为这个文档设置了一个单独的Environment属性并且它可以被系统属性,环境变量或者命令行参数覆盖。

无论你设置了什么环境变量,Spring Boot都会加载application.properties。如果使用了YAML那么以“.yml”结尾的文件默认也会被添加到列表中去。

Spring Boot 在DEBUG级别将会打印出加载的配置文件,并在TRACE级别打印出没有找到的备选文件。

查看ConfigFileApplicationListener了解更多细节。

使用’短’命令行参数

有些人喜欢用(例如)--port=9000而不用--server.port=9000在命令行设置配置属性。你可以很容易地通过在application.properties中使用占位符来启用这个功能,例如:

1
server.port = ${port:8080}

如果你从spring-boot-starter-parentPOM继承而来,maven-resources-plugins默认的过滤标记从${*}修改为@(例如@maven.token@代替${maven.token})来避免与Spring风格的占位符冲突。如果你直接为application.properties启用了maven的过滤,你可能也需要修改默认的过滤符为其他的分隔符。

在这种特定情况下端口绑定可以在Heroku和Clound Foundry这样的PaaS环境中生效,因为这两个平台PORT环境变量是自动设置的,并且Spring 可以绑定到Environment属性的大写的同义词属性。

使用YAML外部属性

YAML是JSON的一个超集,并且方便以层次格式存储外部属性。例如:

1
2
3
4
5
6
7
8
spring:
application:
name: cruncher
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost/test
server:
port: 9000

创建一个名为application.yml的文件并放在classpath根目录,也需要将snakeyaml添加到依赖中(maven坐标为org.yaml:snakeyaml,如果你使用spring-boot-starter将会自动包含进来)。YAML文件将会解析为Java Map<String,Object>(就像一个JSON对象),并且Spring Boot使这个map变扁平,这样它具有一层深度并且拥有句号分隔的key,有点儿像java中常常使用的Properties文件。

上面的YAML例子对应到applicatoin.properties文件:

1
2
3
4
spring.application.name=cruncher
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/test
server.port=9000

在“Spring Boot特性”一节中查看“使用YAML代替Properties”了解更多细节。

设置激活的Spring profile

Spring Environment 有一个针对此的API,但是通常你应该设置一个系统属性(spring.profiles.active)或者一个OS环境变量(SPRING_PROFILES_ACTIVE)。例如通过一个-D参数(记住把它放在main 类或者jar包前)来启动你的应用:

1
$ java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar

在Spring Boot中你也可以在application.properties中设置激活的profile。例如:

1
spring.profiles.active=production

通过这种方式设置的值可以被系统属性或者环境变量设置替代,但是SpringApplicationBuilder.profiles()方法不会。因此后面的Java API 可以用来参数化profile而不改变默认的。

查看“Spring Boot特性”一节中Profiles了解更多细节。

根据环境改变配置

YAML文件实际上是一系列以---线分隔的文档,并且每个文档都被解析成独立的扁平map。

如果一个YAML文档包含了spring.profiles key, 那么这个profiles的值(逗号隔开的列表)将会传入Spring Environment.acceptsProfiles() 并且如果其中的任何profile激活了,那么这个文档将会包含到最终的合并结果中(否则不会)。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server:
port: 9000
---

spring:
profiles: development
server:
port: 9001

---

spring:
profiles: production
server:
port: 0

在这个例子中,默认的端口号是9000,但是如果Spring “development” profile激活了,那么端口号会是9001,如果”production” 激活了,那么它将是0。

发现外部属性的内置选项

Spring Boot 在运行时从application.properties(或.yml)(和其他位置)绑定外部属性到应用程序中。在一个位置上没有(而且技术上不可能是)所有受支持的属性的详尽列表,因为属性可以来自类路径上的附加jar文件。

具有Actuator 功能的运行中的应用程序有一个configprops端点,它显示了通过@ConfigurationProperties可获得的所有绑定和可绑定属性。

附录包括一个application.properties示例,包含了Spring Boot所支持的最常见属性的列表。最终的列表来自搜索@ConfigurationProperties@Value注解,以及偶尔使用Binder的源代码。

嵌入式Web服务器

使用其它Web服务器

下面的Spring Boot starters 为我们带来了一个默认的容器:

  • spring-boot-starter-webspring-boot-starter-tomcat一起带来了tomcat,
    但可以使用spring-boot-starter-jettyspring-boot-starter-undertow替代。
  • spring-boot-starter-webfluxspring-boot-starter-reactor-netty带来了Reactor Netty,但是可以使用spring-boot-starter-tomcat, spring-boot-starter-jettyspring-boot-starter-undertow替代。

许多starter 只支持Spring MVC,因此它们传递地带来了spring-boot-starter-web

如果你选择使用不同的HTTP服务器,你需要排除那些依赖并且引入你选择的。Spring Boot为HTTP 服务器提供了独立的starter来使这个过程尽可能简单。

下面是一个Maven 的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- Exclude the Tomcat dependency -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

Gradle:

1
2
3
4
5
6
7
8
9
10
11
configurations {
// exclude Reactor Netty
compile.exclude module: 'spring-boot-starter-reactor-netty'
}

dependencies {
compile 'org.springframework.boot:spring-boot-starter-webflux'
// Use Undertow instead
compile 'org.springframework.boot:spring-boot-starter-undertow'
// ...
}

使用WebClient需要spring-boot-starter-reactor-netty,因此如果你需要使用其他的HTTP服务器,你需要排除它。

配置Jetty

一般可以按照“发现内置外部属性”@ConfigurationProperties(ServerProperties是主要的一个)的建议,但也看看ServletWebServerFactoryCustomizer。Jetty api非常丰富,所以一旦你可以访问JettyServletWebServerFactory你可以以多种方式修改它。或者是添加自己的JettyServletWebServerFactory

添加Servlet, Filter 或 Listener

有两种方法可以添加ServletFilterServletContextListener和其他受Servlet规范支持的Listener。你可以为它们提供Spring bean,也可以启用Servlet组件扫描。

使用Spring Bean添加Servlet,Filter和Listener

要添加Servlet、Filter或Servlet*Listener,可以为其提供一个@Bean定义。当你想要注入配置或依赖项时,这可能非常有用。但是,你必须非常小心,它们不会引起太多其他bean的热切初始化,因为它们必须在应用程序生命周期的早期就被安装在容器中(例如,让它们依赖于你的DataSource或JPA配置不是一个好主意)。你可以通过在第一次使用时懒加载而不是初始化时来解决这些限制。

对于FiltersServlet,你还可以通过添加FilterRegistrationBeanServletRegistrationBean来添加映射和初始化参数而不是使用底层组件。

如果在过滤器注册中没有指定dispatcherType,它将匹配FORWARDINCLUDEREQUEST。 如果已启用异步,则它也将匹配ASYNC
如果要迁移web.xml中没有dispatcher元素的过滤器,则需要自行指定dispatcherType

1
2
3
4
5
6
7
@Bean
public FilterRegistrationBean myFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
....
return registration;
}

禁止注册Servlet 或 Filter

如上所述,任何ServletFilter bean将自动注册到servlet容器。 要禁用特定的FilterServlet bean的注册,请为其创建一个注册 bean并将其标记为禁用。例如:

1
2
3
4
5
6
@Bean
public FilterRegistrationBean registration(MyFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}

使用类路径扫描添加Servlet,Filter和Listener

@WebServlet@WebFilter@WebListener注解类可以自动注册一个嵌入的servlet容器,通过使用@ServletComponentScan注解@Configuration类,并指定包含你想要注册的组件的包(s)。默认情况下,@ServletComponentScan将从带注解的类的包中进行扫描。

修改HTTP端口

在独立应用程序中,主HTTP端口默认为8080,但可以使用server.port(例如在application.properties或系统属性中)进行设置。 由于Environment值的松散绑定,你还可以使用SERVER_PORT(例如作为OS环境变量)。

要完全关闭HTTP端点,但仍然创建WebApplicationContext,请使用server.port = -1(这对于测试有时很有用)。

有关更多详细信息,请参阅“Spring Boot功能”部分中的“定制嵌入式Servlet容器”ServerProperties源代码。

使用随机未分配的HTTP端口

要扫描一个空闲端口(使用OS本机来防止冲突),请使用server.port = 0

在运行时发现HTTP端口

你可以从日志输出中访问运行服务器的端口,也可以通过ServletWebServerApplicationContextEmbeddedWebServer访问。最好的办法来获取和确保它已经初始化了,就是添加一个ApplicationListener<ServletWebServerInitializedEvent>类型的@Bean,并在容器发布的时候将其从容器中取出。

使用@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)的测试用例也可以使用@LocalServerPort注解将实际的端口注入到字段中。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class MyWebIntegrationTests {

@Autowired
ServletWebServerApplicationContext server;

@LocalServerPort
int port;

// ...

}

@LocalServerPort@Value(“$ {local.server.port}”)的元注解。不要试图在普通应用程序中注入端口。 正如我们刚刚看到的,只有在容器初始化后才会设置该值。 与测试相反,应用程序代码回调会被提前处理(即在值实际可用之前)。

配置SSL

可以通过设置各种server.ssl.*属性(通常在application.propertiesapplication.yml中)声明性地配置SSL。例如:

1
2
3
4
server.port=8443
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password=secret
server.ssl.key-password=another-secret

请参阅Ssl了解所有支持的属性的详细信息。

使用类似上例的配置意味着应用程序将不再支持8080端口上的普通HTTP连接器。Spring Boot不支持通过application.properties配置HTTP和HTTPS两个连接器。如果你想有两个,那么你需要以编程方式配置其中之一。建议使用application.properties配置HTTPS,因为HTTP连接器以编程方式更容易配置。有关示例,请参阅spring-boot-sample-tomcat-multi-connectors示例项目。

配置访问日志

可以通过各自的命名空间为Tomcat,Undertow和Jetty配置访问日志。

例如,以下以自定义模式配置Tomcat访问日志。

1
2
3
server.tomcat.basedir=my-tomcat
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%t %a "%r" %s (%D ms)

日志的默认位置是相对于tomcat基本目录的logs目录,默认情况下该目录是临时目录,因此你可能需要修复Tomcat的基本目录或使用日志的绝对路径。在上面的示例中,日志将在相对于应用程序的工作目录的my-tomcat/logs目录中。

undertow 访问日志可以用类似的方式进行配置

1
2
server.undertow.accesslog.enabled=true
server.undertow.accesslog.pattern=%t %a "%r" %s (%D ms)

日志存储在相对于应用程序的工作目录的logs目录中。这可以通过server.undertow.accesslog.directory进行自定义。

最后,jetty的访问日志也可以这样配置:

1
2
server.jetty.accesslog.enabled=true
server.jetty.accesslog.filename=/var/log/jetty-access.log

默认情况下,日志将被重定向到System.err。 有关更多详细信息,请参阅文档

使用前端代理服务器

你的应用程序可能需要发送302重定向或使用绝对链接呈现内容。在代理之后运行时,调用者需要链接到代理,而不是托管应用的机器的物理地址。通常情况下,这种情况是通过与代理的约定来处理的,代理将添加头来告诉后端如何构建到自己的链接。

如果代理添加了常规的X-Forwarded-ForX-Forwarded-Proto头(大部分是开箱即用的),只要在你的application.properties中设置server.use-forward-headerstrue的,那么服务器就可以正确地呈现绝对链接。

如果你的应用程序正在Cloud Foundry 或 Heroku 中运行,那么server.use-forward-headers属性将默认为true(如果未指定)。 在所有其他情况下,它默认为false

自定义Tomcat的代理配置

如果你正在使用Tomcat,则可以另外配置用于携带“转发”信息的头的名称:

1
2
server.tomcat.remote-ip-header=x-your-remote-ip-header
server.tomcat.protocol-header=x-your-protocol-header

Tomcat也配置了一个默认的正则表达式来匹配要被信任的内部代理。默认情况下,10/8, 192.168/16, 169.254/16127/8中的IP地址是可信的。你可以通过向application.properties添加条目来自定义阀的配置。例如:

1
server.tomcat.internal-proxies=192\\.168\\.\\d{1,3}\\.\\d{1,3}

只有在使用属性文件进行配置时才需要双反斜杠。如果你使用的是YAML,则单个反斜杠就足够了,并且与上面显示的相同的值将是192\.168\.\d{1,3}\.\d{1,3}

你可以通过将internal-proxies设置为空来信任所有代理(但不要在生产中这样做)。

通过切换自动关闭(即设置 server.use-forward-headers = false)并在TomcatServletWebServerFactory bean中添加一个新的阀实例,可以完全控制Tomcat的RemoteIpValve配置。

配置Tomcat

通常,你可以按照“发现外部属性的内置选项”中有关@ConfigurationPropertiesServerProperties是此处的主要内容)的建议,还可以查看ServletWebServerFactoryCustomizer和各种可以添加到Tomcat特定的*Customizers。 Tomcat的API相当丰富,所以一旦你可以访问TomcatServletWebServerFactory,你可以通过多种方式对其进行修改。或者是添加自己的TomcatServletWebServerFactory

启用Tomcat多Connector功能

org.apache.catalina.connector.Connector添加到TomcatServletWebServerFactory,该TomcatServletWebServerFactory可以允许多个连接器,例如, HTTP和HTTPS连接器:

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
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addAdditionalTomcatConnectors(createSslConnector());
return tomcat;
}

private Connector createSslConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
try {
File keystore = new ClassPathResource("keystore").getFile();
File truststore = new ClassPathResource("keystore").getFile();
connector.setScheme("https");
connector.setSecure(true);
connector.setPort(8443);
protocol.setSSLEnabled(true);
protocol.setKeystoreFile(keystore.getAbsolutePath());
protocol.setKeystorePass("changeit");
protocol.setTruststoreFile(truststore.getAbsolutePath());
protocol.setTruststorePass("changeit");
protocol.setKeyAlias("apitester");
return connector;
}
catch (IOException ex) {
throw new IllegalStateException("can't access keystore: [" + "keystore"
+ "] or truststore: [" + "keystore" + "]", ex);
}
}

使用 Tomcat的 LegacyCookieProcessor

Spring Boot使用的嵌入式Tomcat不支持开箱即用的Cookie格式的“Version 0”,你可能会看到以下错误:

1
java.lang.IllegalArgumentException: An invalid character [32] was present in the Cookie value

如果可能的话,你应该考虑更新你的代码,只存储符合新Cookie规范的值。但是,如果你无法更改cookie的写入方式,则可以将Tomcat配置为使用LegacyCookieProcessor。 要切换到LegacyCookieProcessor,请使用添加TomcatContextCustomizerServletWebServerFactoryCustomizer bean:

1
2
3
4
5
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {
return (serverFactory) -> serverFactory.addContextCustomizers(
(context) -> context.setCookieProcessor(new LegacyCookieProcessor()));
}

配置Undertow

启用 Undertow 多Listener功能

使用@ServerEndpoint创建WebSocket端点

如果要在使用嵌入式容器的Spring Boot应用程序中使用@ServerEndpoint,则必须声明一个ServerEndpointExporter @Bean

1
2
3
4
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}

这个bean将用底层的WebSocket容器注册任何ServerEndpoint注解的bean。当部署到一个独立的servlet容器时,这个角色由servlet容器初始化器执行,而ServerEndpointExporter bean是不需要的。

启用HTTP响应压缩

Jetty,Tomcat和Undertow都支持HTTP响应压缩。可以通过application.properties启用:

1
server.compression.enabled=true

默认情况下,响应的长度必须至少达到2048个字节,以便进行压缩。可以使用server.compression.min-response-size属性进行配置。

默认情况下,只有当内容类型为以下内容时,响应才会被压缩:

  • text/html
  • text/xml
  • text/plain
  • text/css

可以使用server.compression.mime-types属性进行配置。

Spring MVC

编写JSON REST 服务

在Spring Boot应用程序中,只要Jackson2位于类路径上, 任何Spring @RestController都应该在默认情况下呈现JSON响应。例如:

1
2
3
4
5
6
7
8
9
@RestController
public class MyController {

@RequestMapping("/thing")
public MyThing thing() {
return new MyThing();
}

}

只要MyThing可以由Jackson2(例如普通的POJO或Groovy对象)序列化,那么localhost:8080/thing将默认返回JSON。有时在浏览器中,你可能会看到XML响应,因为浏览器倾向于发送XML优先的accept头。

编写XML REST 服务

如果在类路径中有Jackson XML扩展(jackson-dataformat-xml),它将被用于呈现XML响应,而与我们使用的JSON相同的示例也会起作用。要使用它,请在你的项目中添加以下依赖项:

1
2
3
4
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>

你可能还想添加对Woodstox的依赖。它比JDK提供的默认StAX实现快得多,而且还增加了美化的打印支持和改进的命名空间处理:

1
2
3
4
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
</dependency>

如果不能使用杰克逊的XML扩展,那么将使用JAXB(默认为JDK提供),并附加要求将MyThing注解@XmlRootElement:

1
2
3
4
5
@XmlRootElement
public class MyThing {
private String name;
// .. getters and setters
}

为了让服务器呈现XML而不是JSON,你可能需要发送一个Accept:text/XML头(或使用浏览器)。

自定义Jackson ObjectMapper

Spring MVC(客户端和服务器端)使用HttpMessageConverters在HTTP交互中协商内容转换。如果Jackson 在类路径中,你已经得到了Jackson2ObjectMapperBuilder提供的默认的转换器(s),这是自动配置的一个实例。

默认创建的ObjectMapper(或用于JacksonXML转换器的XmlMapper)实例具有以下自定义属性:

  • MapperFeature.DEFAULT_VIEW_INCLUSION是禁用的
  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES是禁用的

Spring Boot也有一些特性,可以使定制这个行为变得更加容易。

你可以使用环境配置ObjectMapperXmlMapper实例。Jackson提供了一套广泛的简单的开关特性,可用于配置其处理的各个方面。这些特性是在Jackson的六个枚举中描述的,这些枚举映射到环境中的属性:

Jackson 枚举 环境属性
com.fasterxml.jackson.databind.DeserializationFeature `spring.jackson.deserialization.=true
com.fasterxml.jackson.core.JsonGenerator.Feature `spring.jackson.generator.=true
com.fasterxml.jackson.databind.MapperFeature `spring.jackson.mapper.=true
com.fasterxml.jackson.core.JsonParser.Feature `spring.jackson.parser.=true
com.fasterxml.jackson.databind.SerializationFeature `spring.jackson.serialization.=true
com.fasterxml.jackson.annotation.JsonInclude.Include `spring.jackson.default-property-inclusion=always

例如,设置spring.jackson.serialization.indent_output = true启用美化打印。注意,由于使用了松散绑定indent_output的情况不必与对应的枚举常量相匹配,后者是INDENT_OUTPUT

这种基于环境的配置应用于自动配置Jackson2ObjectMapperBuilder bean,并将适用于任何使用这个builder创建的mappers ,包括自动配置ObjectMapper bean。

上下文的Jackson2ObjectMapperBuilder可以由一个或多个Jackson2ObjectMapperBuilderCustomizer bean定制。这种定制的bean可以被排序,而Boot自己的定制器的顺序是0,允许在Boot的定制之前和之后进行额外的定制。

任何com.fasterxml.jackson.databind.Module类型的bean将自动注册自动配置的Jackson2ObjectMapperBuilder和应用于任何它创建的ObjectMapper实例。当向你的应用程序添加新特性时,这提供了一种用于定制模块的全局机制。

如果你想完全替换默认ObjectMapper,要么定义一个这种类型的@ bean并将其标记为@Primary,或者,如果你喜欢builder方式,可以定义一个Jackson2ObjectMapperBuilder@Bean。注意,在这两种情况下,这将禁用ObjectMapper的所有自动配置。

如果你提供任何MappingJackson2HttpMessageConverter类型的@ Beans然后他们将取代MVC的默认配置。另外,还提供了一种HttpMessageConverters类型的bean(如果使用默认的MVC配置的话,总是可用的),它提供了一些有用的方法来访问默认的和用户增强的消息转换器。

请参阅“定制@ResponseBody渲染”部分和WebMvcAutoConfiguration源代码以获得更多详细信息。

自定义@ResponseBody渲染

Spring使用HttpMessageConverters来渲染@ResponseBody(或来自@RestController的响应)。只需在Spring Boot 上下文中添加这种类型的bean,就可以提供额外的转换器。如果添加的bean是默认包含的类型(例如MappingJackson2HttpMessageConverter JSON转换),那么它将取代默认值。提供了一种HttpMessageConverters类型的bean(如果使用默认的MVC配置的话,总是可用的),它提供了一些有用的方法来访问默认的和用户增强的消息转换器(例如,如果你想手动将它们注入到一个定制的RestTemplate中)。

在正常使用MVC时,任何提供的WebMvcConfigurer bean通过重写configureMessageConverters方法也可以贡献转换器,但与正常MVC不一样的是,你只能供应你需要的额外的转换器(因为 Spring Boot 使用相同的机制提供它默认的)。最后,如果你选择通过提供自己的@EnableWebMvc配置退出Spring Boot默认的MVC配置,然后你可以完全控制,并使用WebMvcConfigurationSupportgetMessageConverters来手动做任何事。

更多细节请参见WebMvcAutoConfiguration源代码。

处理文件上传

Spring Boot包含Servlet 3 javax.servlet.http.PartAPI来支持上传文件。在默认情况下,Spring Boot配置Spring MVC,每个文件最大为1MB,在单个请求中最多可达到10MB的文件数据。你可以覆盖这些值,以及存储中间数据的位置(例如,存储到/tmp目录),以及通过使用在MultipartProperties类中公开的属性将数据刷新到磁盘的阈值。如果你想指定文件是无限的,例如,设置spring.servlet.multipart.max-file-size属性为-1

当你希望在Spring MVC控制器处理程序方法中作为@RequestParam注解的MultipartFile类型参数来接收multipart编码的文件数据时,multipart的支持是很有帮助的。

查看MultipartAutoConfiguration源代码获取更多的细节。

关闭Spring MVC DispatcherServlet

Spring Boot希望从应用程序的/提供所有内容。如果你更愿意将自己的servlet映射到该URL,你可以这样做,但是你可能会丢失一些其他的Boot MVC 特性。添加自己的servlet并将其映射到根资源只需声明一个Servlet类型的@ Bean并且给它特殊的bean名称-dispatcherServlet(如果你想关掉它,而不是取代它,你还可以创建一个不同类型同名称的bean)。

关闭默认MVC配置

对MVC配置进行完全控制的最简单方法是,提供你自己的@Configuration,并使用@EnableWebMvc注解。这将把所有MVC配置掌握在你的手中。

自定义ViewResolver

ViewResolver是Spring MVC的核心组件,将@Controller中的视图名转换为实际的View实现。请注意,ViewResolvers主要用于UI应用程序,而不是REST风格的服务(View不用于渲染@ResponseBody)。有许多ViewResolver的实现可供选择,而Spring本身并不建议你应该使用哪一个。另一方面,Spring Boot根据它在类路径和应用程序上下文中找到的内容,为你安装一个或两个。DispatcherServlet使用它在应用程序上下文中找到的所有解析器,依次尝试每一个解析器,直到得到一个结果,因此,如果你要添加自己的解析器,那么你必须了解该顺序,并在其中添加你的解析器。

WebMvcAutoConfiguration为您的上下文添加了以下ViewResolvers:

  • 一个InternalResourceViewResolver bean,id 为“defaultViewResolver”。这一项定位了可以使用DefaultServlet来渲染的物理资源(例如,如果你使用的是静态资源和JSP页面)。它在视图名中应用一个前缀和一个后缀,然后在servlet上下文中查找带有该路径的物理资源(默认值都是空的,但是通过spring.mvc.view.prefixspring.mvc.view.suffix可以访问外部配置)。可以通过提供相同类型的bean来覆盖它。
  • 一个id为“BeanNameViewResolver”的BeanNameViewResolver。这是视图解析器链中的一个有用的成员,它将使用与正在解析的View相同的名称来获取任何bean。不应该重写或替换它。
  • 一个id 为“viewResolver”的ContentNegotiatingViewResolver 实际上只有存在实际的View类型的bean时才会被添加。这是一个“主”解析器,委托给其他解析器,并试图找到一个匹配客户端发送的“Accept”HTTP头的匹配项。有一个关于ContentNegotiatingViewResolver有用的博客,你可能更喜欢研究学习,可以查看源代码了解更多。你可以通过定义一个bean名为“viewResolver”关掉自动配置ContentNegotiatingViewResolver
  • 如果你用的是Thymeleaf,也会有一个id为“thymeleafViewResolver”的ThymeleafViewResolver。它通过使用前缀和后缀包围视图名来查找资源(spring.thymeleaf.prefixspring.thymeleaf.suffix,默认分别为”classpath:/templates/“和”.html”的)。可以通过提供相同名称的bean来覆盖它。
  • 如果你使用FreeMarker,也会有一个id为“freeMarkerViewResolver”的FreeMarkerViewResolver。它在围绕视图名称的前缀和后缀(spring.freemarker.prefixspring.freemarker.suffix)的加载路径(spring.freemarker.templateLoaderPath,默认分别为“classpath:/templates/”和”.tpl”)中查找资源。可以通过提供相同名称的bean来覆盖它。
  • 如果您使用的是Groovy模板(实际上,如果你的类路径上有Groovy模板),那么也会有一个id为’groovyMarkupViewResolver’的GroovyMarkupViewResolver。它在围绕视图名称的前缀和后缀(spring.groovy.template.prefixspring.groovy.template.suffix)的加载路径(spring.freemarker.templateLoaderPath,默认分另为“classpath:/templates/”和”.tpl”)中查找资源。可以通过提供相同名称的bean来覆盖它。

查看WebMvcAutoConfigurationThymeleafAutoConfigurationFreeMarkerAutoConfigurationGroovyTemplateAutoConfiguration了解更多。

HTTP 客户端

配置RestTemplate使用代理

正如“RestTemplate定制”中所描述的那样,RestTemplateCustomizer可以使用RestTemplateBuilder来构建一个定制的RestTemplate。这是创建配置使用代理的RestTemplate的推荐方法。

代理配置的具体细节取决于正在使用的底层客户端请求工厂。这里有一个例子配置HttpComponentsClientRequestFactory,包含了一个 HttpClient,除了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));
}

}

日志

除了Commons Logging API,Spring Boot没有强制的日志记录依赖,其中有许多实现可供选择。要使用Logback,你需要在引入它和jcl-over-slf4j(它实现了公共资源日志API)。最简单的方法是通过starter,这些启动器都依赖于spring-boot-starter-logging。对于一个web应用程序,你只需要spring-boot-starter-web,因为它依赖于日志starter。例如在Maven中:

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

Spring Boot有一个LoggingSystem抽象,它试图根据类路径的内容来配置日志记录。如果可以使用Logback,这是第一选择。

如果你需要对日志进行的惟一更改是设置不同记录器的级别,那么你可以在application.properties中使用”logging.level” 前缀来执行此操作,,如:

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

你还可以使用“logg.file”设置文件的位置(除控制台之外)。

要更细粒度的设置日志系统,你需要使用由LoggingSystem支持的原生配置格式。默认情况下,Spring Boot从系统的默认位置获取原生配置(例如:Logback的classpath:logback.xml),但是你可以使用”logging.config”属性设置配置文件的位置。

配置Logback进行日志记录

如果你放了一个logback.xml文件在类路径的根目录中,它将从那里获取(或者是logback-spring.xml以利用Boot 提供的模板特性)。Spring Boot提供了一个默认的基本配置,如果你只想设置级别,你可以包括它。例如:

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="org.springframework.web" level="DEBUG"/>
</configuration>

如果你看过spring-boot jar中的base.xml,你将看到它使用了一些有用的系统属性,LoggingSystem为你创建了这些属性。它们是:

  • ${PID}当前的进程ID。
  • ${LOG_FILE} 如果在Boot的外部配置中设置了logging.file
  • ${LOG_PATH} 如果设置了logging.path(表示一个用于存放日志文件的目录)。
  • ${LOG_EXCEPTION_CONVERSION_WORD} 如果在Boot 的外部配置中设置了logging.exception-conversion-word

Spring Boot还使用自定义的Logback转换器在控制台(但不是在日志文件中)提供了一些漂亮的ANSI颜色终端输出。查看默认的base.xml配置了解更多细节。

如果Groovy在类路径上,你应该也能够用logback.groovy来配置Logback(如果有的话,它将会被优先考虑)。

配置logback只以文件输出

如果你想要禁用控制台日志记录,并且只将输出写入文件,那么你需要一个自定义的logback-spring.xml文件,它引入file-appender.xml而不引入console-appender.xml:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
<root level="INFO">
<appender-ref ref="FILE" />
</root>
</configuration>

你还需要在application.properties中加入logging.file:

1
logging.file=myapplication.log

配置Log4j进行日志记录

如果在类路径上存在Log4j 2,Spring Boot 将支持Log4j 2进行日志配置。如果你正在使用starter来收集依赖关系,这意味着你必须排除Logback,然后将log4j 2包含在内。如果你没有使用starter,那么除了Log4j 2之外,你还需要提供jcl-over-slf4j(至少)。

最简单的方式可能是通过starter,尽管它需要配置一些不包含,例如在Maven中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

Log4j starter的使用聚集了对常见日志必要的依赖项(例如,包括Tomcat使用的java.util.logging但是配置使用Log4j 2进行日志记录)。请参阅Actuator Log4j 2的示例以获得更多细节,并在实际操作中查看。

使用YAML或JSON来配置Log4j 2

除了默认的XML配置格式之外,Log4j 2还支持YAML和JSON配置文件。要配置Log4j 2以使用另一种配置文件格式,将适当的依赖项添加到类路径,并命名配置文件以匹配你所选择的文件格式:

格式 依赖 文件名
YAML com.fasterxml.jackson.core:jackson-databind com.fasterxml.jackson.dataformat:jackson-dataformat-yaml log4j2.yaml log4j2.yml
JSON com.fasterxml.jackson.core:jackson-databind log4j2.json log4j2.jsn

数据访问

配置自定义数据源

要配置你自己的DataSource,请在你的配置中定义该类型的@Bean。 Spring Boot会在需要的地方重复使用你的DataSource,包括数据库初始化。 如果你需要将某些设置外部化,则可以轻松地将DataSource绑定到环境(请参阅第24.7.1节“第三方配置”)。

1
2
3
4
5
@Bean
@ConfigurationProperties(prefix="app.datasource")
public DataSource dataSource() {
return new FancyDataSource();
}
1
2
3
app.datasource.url=jdbc:h2:mem:mydb
app.datasource.username=sa
app.datasource.pool-size=30

假设你的FancyDataSource具有常规的JavaBean属性,包括url,用户名和池大小,那么在DataSource可用于其他组件之前,将自动绑定这些设置。 常规的数据库初始化也会发生(所以spring.datasource.*的相关子集仍然可以与你的自定义配置一起使用)。

如果你正在配置自定义的JNDIDataSource,则可以应用相同的原则:

1
2
3
4
5
6
@Bean(destroyMethod="")
@ConfigurationProperties(prefix="app.datasource")
public DataSource dataSource() throws Exception {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
return dataSourceLookup.getDataSource("java:comp/env/jdbc/YourDS");
}

Spring Boot还提供了一个实用的构造器类DataSourceBuilder,可用于创建其中一个标准数据源(如果它在类路径上)。构造器可以根据类路径上的可用内容来检测要使用的类。 它也会根据JDBC URL自动检测驱动程序。

1
2
3
4
5
@Bean
@ConfigurationProperties("app.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}

要使用该DataSource运行应用程序,所需要的只是连接信息; 还可以提供特定于池的设置,请检查将在运行时使用的具体实现以获取更多详细信息。

1
2
3
4
app.datasource.url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.pool-size=30

但有一个问题。 由于连接池的实际类型未公开,所以在你的自定义DataSource的元数据中不会生成任何键,并且你的IDE中没有自动提示功能(DataSource接口不公开任何属性)。 另外,如果你碰巧在类路径上有Hikari,这个基本的设置将不起作用,因为Hikari没有url属性(而是一个jdbcUrl属性)。 你将不得不重写你的配置:

1
2
3
4
app.datasource.jdbc-url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.maximum-pool-size=30

你可以通过强制连接池使用并返回一个专门的实现,而不是DataSource来解决这个问题。 你将无法在运行时更改实现,但选项列表将是显式的。

1
2
3
4
5
@Bean
@ConfigurationProperties("app.datasource")
public HikariDataSource dataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}

你甚至可以进一步利用DataSourceProperties为你做的事情,即如果没有提供一个合理的用户名和密码的URL,则提供一个默认的嵌入式数据库。 你可以很容易地从任何DataSourceProperties的状态初始化一个DataSourceBuilder,所以你可以注入Spring Boot自动创建的那个。 但是,这会将你的配置拆分为两个命名空间:url,用户名,密码,类型和驱动在spring.datasource上,其他的在自定义命名空间(app.datasource)上。为了避免这种情况,你可以在自定义命名空间上重新定义自定义的DataSourceProperties

1
2
3
4
5
6
7
8
9
10
11
12
13
@Bean
@Primary
@ConfigurationProperties("app.datasource")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}

@Bean
@ConfigurationProperties("app.datasource")
public HikariDataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().type(HikariDataSource.class)
.build();
}

除了选择了一个专用的连接池(在代码中)并且它的设置暴露在相同的命名空间,这个设置使你与默认情况下的Spring Boot配合。 由于DataSourceProperties为你处理url/jdbcUrl转义,因此可以像这样配置它:

1
2
3
4
app.datasource.url=jdbc:mysql://localhost/test
app.datasource.username=dbuser
app.datasource.password=dbpass
app.datasource.maximum-pool-size=30

由于你的自定义配置选择Hikari,app.datasource.type将不起作用。 在实践中,构造器将被初始化为设置的任何可能的值,然后被对.type()的调用覆盖。

更多详细信息请参见“Spring Boot功能”部分的第29.1节“配置数据源”DataSourceAutoConfiguration类源代码。

配置两个数据源

如果你需要配置多个数据源,则可以应用上一节中所述的相同技巧。 但是,你必须标记一个DataSource``@Primary,因为各种自动配置都希望能够按类型获得一个。

如果您创建自己的DataSource,则自动配置将退出。在下面的示例中,我们提供了与在主数据源上自动配置提供的完全相同的功能集:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Bean
@Primary
@ConfigurationProperties("app.datasource.foo")
public DataSourceProperties fooDataSourceProperties() {
return new DataSourceProperties();
}

@Bean
@Primary
@ConfigurationProperties("app.datasource.foo")
public DataSource fooDataSource() {
return fooDataSourceProperties().initializeDataSourceBuilder().build();
}

@Bean
@ConfigurationProperties("app.datasource.bar")
public BasicDataSource barDataSource() {
return DataSourceBuilder.create().type(BasicDataSource.class).build();
}

必须将fooDataSourceProperties标记为@Primary,以便数据库初始化程序功能使用你的副本(如果使用的话)。

这两个数据源也都是用于高级自定义的。 例如,你可以如下配置它们:

1
2
3
4
5
6
7
app.datasource.foo.type=com.zaxxer.hikari.HikariDataSource
app.datasource.foo.maximum-pool-size=30

app.datasource.bar.url=jdbc:mysql://localhost/test
app.datasource.bar.username=dbuser
app.datasource.bar.password=dbpass
app.datasource.bar.max-total=30

当然,你也可以将相同的概念应用于辅助DataSource

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
@Bean
@Primary
@ConfigurationProperties("app.datasource.foo")
public DataSourceProperties fooDataSourceProperties() {
return new DataSourceProperties();
}

@Bean
@Primary
@ConfigurationProperties("app.datasource.foo")
public DataSource fooDataSource() {
return fooDataSourceProperties().initializeDataSourceBuilder().build();
}

@Bean
@ConfigurationProperties("app.datasource.bar")
public DataSourceProperties barDataSourceProperties() {
return new DataSourceProperties();
}

@Bean
@ConfigurationProperties("app.datasource.bar")
public DataSource barDataSource() {
return barDataSourceProperties().initializeDataSourceBuilder().build();
}

最后的这个例子在自定义命名空间上配置了两个数据源,这与Spring Boot在自动配置中做了相同的逻辑。

使用Spring Data仓库

Spring Data可以为你创建各种风格的@Repository接口。只要这些@Repositories包含在@EnableAutoConfiguration类的相同包(或子包)中,Spring Boot将为你处理所有这些接口。

对于许多应用程序来说,你需要的所有东西是把正确的Spring Data依赖关系放到你的类路径中(JPA有一个spring-boot-starter-data-jpa和一个Mongodb的spring-boot-starter-data-mongodb),创建一些仓库接口来处理你的@Entity对象。 有两个示例JPA示例Mongodb示例

Spring Boot尝试根据找到的@EnableAutoConfiguration来猜测@Repository定义的位置。 为了更好的控制,可以使用@EnableJpaRepositories注解(来自Spring Data JPA)。

从Spring配置中分离@Entity定义

Spring Boot尝试根据找到的@EnableAutoConfiguration来猜测你的@Entity定义的位置。 为了获得更多控制权,你可以使用@EntityScan注解,例如:

1
2
3
4
5
6
7
8
@Configuration
@EnableAutoConfiguration
@EntityScan(basePackageClasses=City.class)
public class Application {

//...

}

配置JPA属性

Spring Data JPA已经提供了一些与供应商无关的配置选项(例如用于SQL日志记录),Spring Boot公开了这些,还有一些作为hibernate的外部配置属性。 其中一些是根据上下文自动检测,所以你不应该设置它们。

spring.jpa.hibernate.ddl-auto是一个特殊情况,因为它具有不同的默认值,这取决于运行时的条件。 如果使用嵌入式数据库,并且没有像Liquibase或Flyway这样的schema管理器处理DataSource,那么它将默认使用create-drop。 在其他情况下,它默认为none

使用的方言也是根据当前的DataSource自动检测的,但是如果你想明确地使用方言,你可以自己设置spring.jpa.database,并绕过启动时的检查。

指定一个database将导致定义良好的Hibernate方言的配置。一些数据库有不止一个Dialect,这有可能不适合你的需要。在这种情况下,你可以将spring.jpa.database设置为默认值,让Hibernate自己解决,或使用spring.jpa.database-platform属性设置方言。

最常见的选项是:

1
2
spring.jpa.hibernate.naming.physical-strategy=com.example.MyPhysicalNamingStrategy
spring.jpa.show-sql=true

另外,在创建本地EntityManagerFactory时,spring.jpa.properties.*中的所有属性都作为普通的JPA属性(去掉了前缀)传递。

配置Hibernate命名策略

Hibernate使用两种不同的命名策略来将名称从对象模型映射到相应的数据库名称。物理和隐式策略实现的完全限定类名称可分别使用spring.jpa.hibernate.naming.physical-strategyspring.jpa.hibernate.naming.implicit-strategy属性进行配置。

Spring Boot默认使用SpringPhysicalNamingStrategy配置物理命名策略。这个实现提供了与Hibernate 4相同的表结构:所有的点都被下划线替换,而且驼峰也被下划线替换。 默认情况下,所有表名都以小写形式生成,但如果你的schema 需要,则可以覆盖该标志。

具体来说,TelephoneNumber实体将被映射到telephone_number表。

如果你更喜欢使用Hibernate 5的默认值,请设置以下属性:

1
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

有关更多详细信息,请参阅HibernateJpaAutoConfigurationJpaBaseConfiguration

使用自定义EntityManagerFactory

要完全控制EntityManagerFactory的配置,你需要添加一个名为“entityManagerFactory”的@Bean。 Spring Boot的自动配置基于该类型的bean的存在与否关闭其实体管理器。

使用两个实体管理器

即使默认的EntityManagerFactory工作正常,你也需要定义一个新的,否则该类型的第二个bean的存在将关闭默认的这个。 为了简化操作,你可以使用Spring Boot提供的方便的EntityManagerBuilder,或者如果你愿意,可以直接使用Spring ORM中的LocalContainerEntityManagerFactoryBean
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// add two data sources configured as above

@Bean
public LocalContainerEntityManagerFactoryBean customerEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(customerDataSource())
.packages(Customer.class)
.persistenceUnit("customers")
.build();
}

@Bean
public LocalContainerEntityManagerFactoryBean orderEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(orderDataSource())
.packages(Order.class)
.persistenceUnit("orders")
.build();
}

上面的配置几乎可以自行完成。要完成整个过程,你还需要为两个EntityManagers配置TransactionManagers。 如果你把其中一个标记为@Primary,它可以被Spring Boot中默认的JpaTransactionManager支持,另一个必须明确地注入一个新的实例。 或者你也可以使用跨越两者的JTA事务管理器。

如果你使用Spring Data,则需要相应地配置@EnableJpaRepositories

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
@EnableJpaRepositories(basePackageClasses = Customer.class,
entityManagerFactoryRef = "customerEntityManagerFactory")
public class CustomerConfiguration {
...
}

@Configuration
@EnableJpaRepositories(basePackageClasses = Order.class,
entityManagerFactoryRef = "orderEntityManagerFactory")
public class OrderConfiguration {
...
}

使用传统的persistence.xml

Spring不需要使用XML来配置JPA提供程序,并且Spring Boot假定你想要利用该功能。如果你喜欢使用persistence.xml,那么你需要定义自己的LocalEntityManagerFactoryBean(id为’entityManagerFactory’)类型的@Bean,并设置持久化单元名称。

有关默认设置,请参阅JpaBaseConfiguration

使用Spring Data JPA和Mongo仓库

Spring Data JPA和Spring Data Mongo可以自动为你创建Repository实现。 如果它们都出现在类路径中,那么可能需要做一些额外的配置来告诉Spring Boot你为哪一个(或两者)创建存储库。 最明显的方法是使用标准的Spring Data @Enable*Repositories,并告诉它你的Repository接口的位置(其中’*’是’Jpa’或’Mongo’或是两者)。

还有一些spring.data.*.repositories.enabled标志,你可以使用它们在外部配置中打开和关闭自动配置的存储库。例如,如果你想要关闭Mongo仓库并且仍然使用自动配置的MongoTemplate,这很有用。

其他自动配置的Spring Data 存储库类型(Elasticsearch,Solr)也存在同样的障碍和相同的功能。只需分别更改注解和标志的名称。

暴露Spring数据仓库为REST端点

Spring Data REST只要为应用程序启用了Spring MVC,就可以将Repository实现公开为REST端点。

Spring Boot公开了一些自定义RepositoryRestConfigurationspring.data.rest命名空间的有用属性。如果你需要提供额外的定制,你应该使用RepositoryRestConfigurer bean。

如果你没有在你自定义的RepositoryRestConfigurer中指定任何顺序,它将在Spring Boot在内部使用的那个后运行。 如果你需要指定顺序,请确保它大于0。

配置JPA使用的组件

如果你想配置一个将被JPA使用的组件,那么你需要确保组件在JPA之前被初始化。 如果组件是自动配置的,Spring Boot将为你处理这个问题。例如,当Flyway自动配置时,Hibernate被配置为依赖于Flyway,这样后者有机会在Hibernate尝试使用它之前初始化数据库。

如果你自己配置组件,则可以使用EntityManagerFactoryDependsOnPostProcessor子类作为设置必需依赖项的便捷方式。例如,如果你使用Elasticsearch作为Hibernate Search的索引管理器,那么任何EntityManagerFactory bean都必须配置为依赖于elasticsearchClient bean:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* {@link EntityManagerFactoryDependsOnPostProcessor} that ensures that
* {@link EntityManagerFactory} beans depend on the {@code elasticsearchClient} bean.
*/
@Configuration
static class ElasticsearchJpaDependencyConfiguration
extends EntityManagerFactoryDependsOnPostProcessor {

ElasticsearchJpaDependencyConfiguration() {
super("elasticsearchClient");
}

}

数据库初始化

SQL数据库可以用不同的方式初始化,这取决于你选择的是什么技术。当然,只要数据库是一个独立的进程,你也可以手动完成。

使用JPA初始化数据库

JPA具有用于生成DDL的功能,可以将这些功能设置为在启动时运行。 这是通过两个外部属性来控制的:

  • spring.jpa.generate-ddl(boolean)打开和关闭该功能,并且独立于供应商。
  • spring.jpa.hibernate.ddl-auto(enum)是一个Hibernate特性,以更细粒度的方式控制该行为。

使用Hibernate初始化数据库

你可以显式设置spring.jpa.hibernate.ddl-auto,标准Hibernate属性值为nonevalidateupdatecreatecreate-drop。 Spring Boot根据数据库是否为嵌入式的为你选择一个默认值:如果没有检测到schema管理器,则默认为create-drop,在其他所有情况下均设置为none。通过查看Connection类型来检测嵌入式数据库:hsqldbh2derby是嵌入式的,其余不是。从内存数据库切换到“真实”数据库时请小心,不要假定新平台中存在表和数据。 你必须显式设置ddl-auto,或使用其他机制来初始化数据库。

你可以通过启用org.hibernate.SQL日志记录器来输出schema创建的过程。如果启用调试模式,将自动完成。

另外,如果Hibernate从头开始创建schema(即,如果ddl-auto属性设置为createcreate-drop),那么在启动时将执行类路径根目录中名为import.sql的文件。 这对于演示和测试是非常有用的。 这是一个Hibernate功能(与Spring无关)。

初始化数据库

Spring Boot可以自动创建DataSource的schema(DDL脚本)并对其进行初始化(DML脚本):它分别从标准根类路径位置schema.sqldata.sql中加载SQL。 另外,Spring Boot将处理schema-${platform}.sqldata-${platform}.sql文件(如果存在),其中platformspring.datasource.platform的值。这允许你在必要时切换到数据库特定的脚本,例如你可以选择将其设置为数据库的供应商名称(hsqldbh2oraclemysqlpostgresql等)。

Spring Boot默认启用Spring JDBC初始化程序的快速失败功能,所以如果脚本导致异常,应用程序将无法启动。 你可以使用spring.datasource.continue-on-error来调整它。

在基于JPA的应用程序中,可以选择让Hibernate创建schema或使用schema.sql,但不能两者同时使用。如果你选择了后者,请确保禁用spring.jpa.hibernate.ddl-auto

你也可以通过将spring.datasource.initialize设置为false来禁用初始化。

初始化一个Spring Batch数据库

如果你使用的是Spring Batch,那么它将为大多数流行的数据库平台预先打包SQL初始化脚本。 Spring Boot可以检测你的数据库类型并在启动时执行这些脚本。如果你使用嵌入式数据库,则默认情况下会发生这种情况。你也可以为任何数据库类型启用这个功能:

1
spring.batch.initialize-schema=always

你也可以使用spring.batch.initialize-schema = never显式地关闭初始化功能。

使用更高级别的数据库迁移工具

Spring Boot支持两种更高级的迁移工具:FlywayLiquibase

启动时执行Flyway数据库迁移

要在启动时自动运行Flyway数据库迁移,请将org.flywaydb:flyway-core添加到类路径中。

迁移是以V<VERSION>__<NAME>.sql格式(带有<VERSION>下划线分隔的版本,例如’1’或’2_1’)的脚本。默认情况下,它们位于文件夹classpath:db/migration中,但是可以使用spring.flyway.locations修改它。你还可以添加特殊的{vendor}占位符来使用供应商特定的脚本。 假设:

1
spring.flyway.locations=db/migration/{vendor}

这个配置不是使用db/migration,而是根据数据库的类型(对于Mysql来说即db/migration/mysql)来设置使用的文件夹。可以在DatabaseDriver中查看受支持的数据库列表。

另请参阅flyway-core的Flyway类以了解可用设置(如模式等)的详细信息。另外,Spring Boot在FlywayProperties中提供了一组属性,可用于禁用迁移或关闭位置检查。 Spring Boot将调用Flyway.migrate()来执行数据库迁移。 如果你想要更多的控制,提供一个实现了FlywayMigrationStrategy@Bean

Flyway支持SQL和Java回调。 要使用基于SQL的回调,请将回调脚本放在classpath:db/migration文件夹中。 要使用基于Java的回调,创建一个或多个实现了FlywayCallback的Bean,或者最好是继承BaseFlywayCallback。 任何这样的bean将自动注册到Flyway。 他们可以通过使用@Order或实现Ordered来指定顺序。

默认情况下,Flyway将在你的上下文中自动装配(@PrimaryDataSource,并将其用于迁移。 如果你喜欢使用不同的DataSource,你可以创建一个@Bean标记为@FlywayDataSource - 如果你这样做,记得创建另一个数据源,如果你需要两个数据源记得把它标记为@Primary。 或者,你可以通过在外部属性中设置spring.flyway.[url,user,password]来使用Flyway的原生DataSource

这里有一个Flyway示例,你可以看到如何设置这些东西。

你也可以使用Flyway为特定场景提供数据。例如,你可以将测试专用的迁移脚本放在src/test/resources中,并且只有在你的应用程序开始测试时才会运行它们。 如果你想更复杂一点,可以使用特定profile的配置来自定义spring.flyway.locations,以便某些迁移脚本只在特定profile激活时运行。 例如,在application-dev.properties中:

1
spring.flyway.locations=classpath:/db/migration,classpath:/dev/db/migration

使用该设置,dev/db/migration中的迁移脚本将仅在dev profile激活时运行。

启动时执行Liquibase数据库迁移

要在启动时自动运行Liquibase数据库迁移,请将org.liquibase:liquibase-core添加到你的类路径中。

主要的更改日志默认从db/changelog/db.changelog-master.yaml中读取,但可以设置使用spring.liquibase.change-log。除了YAML,Liquibase还支持JSON,XML和SQL更改日志格式。

默认情况下,Liquibase会在你的上下文中自动装载(@PrimaryDataSource,并用它来进行迁移。 如果你喜欢使用不同的DataSource,你可以创建一个@Bean标记为@LiquibaseDataSource - 如果你这样做的话,记得创建另一个数据源,如果你想要两个数据源,就把它标记为@Primary。 或者,你可以通过在外部属性中设置spring.liquibase.[url,user,password]来使用Liquibase的原生DataSource

有关上下文,默认模式等可用设置的详细信息,请参阅LiquibaseProperties

有一个Liquibase示例,你可以看到如何设置。

消息

禁用事务的JMS会话

如果你的JMS代理不支持事务会话,则必须完全禁用事务支持。如果你创建自己的JmsListenerContainerFactory,则无需执行任何操作,因为默认情况下不会进行事务处理。 如果你想使用DefaultJmsListenerContainerFactoryConfigurer来重用Spring Boot的默认设置,可以按如下所示禁用事务性会话:

1
2
3
4
5
6
7
8
9
10
11
@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(
ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory listenerFactory =
new DefaultJmsListenerContainerFactory();
configurer.configure(listenerFactory, connectionFactory);
listenerFactory.setTransactionManager(null);
listenerFactory.setSessionTransacted(false);
return listenerFactory;
}

这里覆盖了默认的工厂,并且它应该被应用到你的应用程序定义的任何其他工厂(如果有的话)。

批处理应用

默认情况下,批处理应用程序需要一个DataSource来存储作业详细信息。如果你不要这么做,你需要实现BatchConfigurer,请参阅@EnableBatchProcessing的Javadoc获取更多细节。

启动时执行Spring Batch作业

Spring Batch自动配置是通过在你的上下文中添加@EnableBatchProcessing(从Spring Batch)来实现的。

默认情况下,它会在启动时执行应用程序上下文中的所有Jobs(有关详细信息,请参阅JobLauncherCommandLineRunner)。 你可以通过指定spring.batch.job.names(逗号分隔的作业名称模式)来限制到特定作业或作业的范围。

如果应用程序上下文包含JobRegistry,则在注册信息中查找spring.batch.job.names中的作业,而不是从上下文自动装配。 这是一个常见的模式,其中有更复杂的系统,在这个系统中多个作业在子环境中定义并集中注册。

有关更多详细信息,请参阅BatchAutoConfiguration@EnableBatchProcessing

Actuator

更改actuator端点的HTTP端口或地址

在独立应用程序中,Actuator HTTP端口默认与主HTTP端口相同。为了让应用程序在不同的端口上监听,可以设置外部属性management.port。要监听一个完全不同的网络地址(例如,如果你有一个用于管理的内部网络和一个用于外部用户应用的网络),你还可以将management.address设置为服务器能够绑定的有效IP地址。

有关更多详细信息,请参阅ManagementServerProperties源代码以及“Production-ready功能”部分中的第50.2节“自定义管理服务器端口”

自定义“whitelabel”错误页面

如果你遇到服务器错误(使用JSON和其他媒体类型的计算机客户端应该看到具有正确错误代码的合理响应),Spring Boot将安装一个“whitelabel”错误页面。

设置server.error.whitelabel.enabled = false将关闭默认错误页面并恢复为你正在使用的servlet容器的默认值。请注意,Spring Boot仍然会尝试解决错误视图,所以你可能会添加你自己的错误页面,而不是完全禁用它。

用你自己的覆盖错误页面取决于你正在使用的模板技术。例如,如果你使用的是Thymeleaf,则可以添加一个error.html模板,如果你使用的是FreeMarker,则可以添加一个error.ftl模板。一般来说,你需要的是一个用错误名称解析的View,和/或处理/error路径的@Controller。除非你替换了一些默认配置,否则你应该在你的ApplicationContext中找到一个BeanNameViewResolver,所以带有iderror@Bean将是一个简单的方法。查看ErrorMvcAutoConfiguration以获取更多详情。

有关如何在servlet容器中注册处理程序的详细信息,另请参阅错误处理一节。

Actuator 和 Jersey

Actuator HTTP端点仅适用于基于Spring MVC的应用程序。如果你想使用Jersey并仍然使用Actuator,则需要启用Spring MVC(例如,依赖于spring-boot-starter-web)。默认情况下,Jersey和Spring MVC调度程序servlet都映射到相同的路径(/)。你将需要更改其中一个的路径(通过为Spring MVC配置server.servlet.path或为Jersey配置spring.jersey.application-path)。 例如,如果将server.servlet.path = / system添加到application.properties中,则actuator HTTP端点将可以在/ system下使用。

Security

关闭Spring Boot 安全配置

如果你在应用程序中的任何位置使用@EnableWebSecurity定义@Configuration,它将关闭Spring Boot中的默认Web应用程序安全设置(但保留Actuator的安全配置)。 要调整默认值,请尝试设置security.*(请参阅SecurityProperties以获取可用设置的详细信息)以及Common应用程序属性SECURITY部分。

更改AuthenticationManager并添加用户帐户

如果你提供了一个类型为AuthenticationManager@Bean,那么将不会创建默认的,所以你可以使用Spring Security的全部功能(例如各种认证选项)。

Spring Security还提供了一个方便的AuthenticationManagerBuilder,它可以用来构建一个带有通用选项的AuthenticationManager。在webapp中使用这种方法的推荐方法是将其注入WebSecurityConfigurerAdapter中的void方法,例如:

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("barry").password("password").roles("USER"); // ... etc.
}

// ... other stuff for application security

}

如果将其放在嵌套类或独立类中(即不会混入大量可能影响实例化顺序的其他@Beans),你将获得最佳结果。 安全的Web示例是一个有用的模板。

如果遇到实例化问题(例如,对用户详细信息存储使用JDBC或JPA),将AuthenticationManagerBuilder回调提取到GlobalAuthenticationConfigurerAdapter中(在init()方法中,以便在其他地方需要身份验证管理器之前进行)有可能是值得。

1
2
3
4
5
6
7
8
9
10
@Configuration
public class AuthenticationManagerConfiguration extends
GlobalAuthenticationConfigurerAdapter {

@Override
public void init(AuthenticationManagerBuilder auth) {
auth.inMemoryAuthentication() // ... etc.
}

}

使用代理服务器时启用HTTPS

确保你的所有主要端点只能通过HTTPS使用,这对于任何应用程序来说都是非常重要的任务。 如果你使用Tomcat作为servlet容器,那么Spring Boot会在检测到某些环境设置时自动添加Tomcat自己的RemoteIpValve,并且你应该能够依赖HttpServletRequest来报告它是否安全(即使是代理的下游 服务器处理真正的SSL终端)。标准行为是由是否存在某些请求头(x-forwarded-forx-forwarded-proto)来决定的,它们的名字是常规的,所以它应该可以与大多数前端代理一起工作。你可以通过在application.properties中添加一些条目来打开阀门,例如:

1
2
server.tomcat.remote-ip-header=x-forwarded-for
server.tomcat.protocol-header=x-forwarded-proto

(任何一个属性的存在都会打开阀门,或者你可以通过添加TomcatServletWebServerFactory bean来自行添加RemoteIpValve。)

Spring Security也可以被配置为需要所有(或某些请求)的安全通道。 要在Spring Boot应用程序中切换,只需在application.properties中将security.require_ssl设置为true即可。

热加载

刷新静态内容

有几个热加载的选项。推荐的方法是使用spring-boot-devtools,因为它提供了额外的开发期间的功能,如支持快速应用程序重启和LiveReload以及合理的开发期间的配置(例如模板缓存)。Devtools通过监视类路径的变化来工作。这意味着静态资源的变化必须“构建”,以使变更生效。默认情况下,当你保存更改时,这会在Eclipse中自动发生。在IntelliJ IDEA中,Make Project将触发必要的构建。由于默认的重新启动排除,对静态资源的更改不会触发应用程序的重新启动。但是,他们会触发一个实时的重新加载。

另外,在IDE中运行(特别是在调试时)是一种很好的开发方式(所有的现代IDE都允许重新加载静态资源,并且通常也可以热加载Java类的变化)。

最后,可以配置Maven和Gradle插件(请参阅addResources属性)以支持从命令行直接从源文件重新加载静态文件。如果你使用更高级别的工具编写代码,则可以将其用于外部css/js编译。

不重启容器刷新模板

Spring Boot支持的大部分模板技术都包含禁用缓存的配置选项(请参阅下面的详细信息)。 如果你使用的是spring-boot-devtools模块,则在开发时将自动为你配置这些属性。

Thymeleaf 模板

如果你使用的是Thymeleaf,则将spring.thymeleaf.cache设置为false。 有关其他Thymeleaf自定义选项,请参阅ThymeleafAutoConfiguration

FreeMarker 模板

如果你正在使用FreeMarker,则将spring.freemarker.cache设置为false。 有关其他FreeMarker自定义选项,请参阅FreeMarkerAutoConfiguration

Groovy 模板

如果你正在使用Groovy模板,则将spring.groovy.template.cache设置为false。 有关其他Groovy自定义选项,请参阅GroovyTemplateAutoConfiguration

快速重启

spring-boot-devtools模块支持应用程序自动重新启动。虽然速度不如JRebel这样的技术,但通常比“冷启动”要快得多。在调查下面讨论的一些更复杂的重新加载选项之前,你应该尝试一下。

有关更多详细信息,请参阅第20章开发人员工具部分。

不重启容器重新加载Java类

现代的IDE(Eclipse,IDEA等)都支持字节码的热交换,所以如果你做了一个不影响类或方法签名的改变,它应该利索地重新加载,没有副作用。

构建

生成构建信息

Maven和Gradle插件都允许生成包含项目坐标,名称和版本的构建信息。该插件也可以配置为通过配置添加其他属性。 当这个文件出现时,Spring Boot会自动配置一个BuildProperties bean。

要使用Maven生成构建信息,请为build-info goal添加一个execution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

查看Spring Boot Maven Plugin文档以获取更多详细信息。

Gradle一样:

1
2
3
springBoot {
buildInfo()
}

可以使用DSL添加其他属性:

1
2
3
4
5
6
7
springBoot  {
buildInfo {
additionalProperties = [
'foo': 'bar'
]
}
}

生成git信息

Maven和Gradle都允许在构建项目时生成一个git.properties文件,其中包含有关git源代码库状态的信息。

对于Maven用户来说,spring-boot-starter-parent POM包含一个预先配置的插件来生成一个git.properties文件。 只需将以下声明添加到你的POM:

1
2
3
4
5
6
7
8
<build>
<plugins>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
</plugin>
</plugins>
</build>

Gradle用户可以使用gradle-git-properties插件获得相同的结果:

1
2
3
plugins {
id "com.gorylenko.gradle-git-properties" version "1.4.17"
}

git.properties中的提交时间预计与yyyy-MM-dd’T’HH:mm:ssZ格式匹配。这是上面列出的两个插件的默认格式。使用这种格式可以将时间解析成Date,并在序列化为JSON时由Jackson的日期序列化配置设置控制它的格式。

自定义依赖版本

如果你使用直接或间接从spring-boot-dependencies(例如spring-boot-starter-parent)继承的Maven构建,但是你想重写特定的第三方依赖项,则可以添加适当的<properties>元素。浏览spring-boot-dependencies POM以获取完整的属性列表。 例如,要选择一个不同的slf4j版本,你可以添加以下内容:

1
2
3
<properties>
<slf4j.version>1.7.5<slf4j.version>
</properties>

这只适用于你的Maven项目是从spring-boot-dependencies继承(直接或间接)下来的。 如果你在你的dependencyManagement部分中用<scope>import</scope>添加了spring-boot-dependencies,那么你必须自己重新定义这个artifact,而不是重写属性。

每个Spring Boot版本都是针对特定的第三方依赖进行设计和测试的。覆盖版本可能会导致兼容性问题。

使用Maven创建可执行jar

spring-boot-maven-plugin可以用来创建一个可执行的“fat”JAR。如果你使用的是spring-boot-starter-parent POM,则可以简单地声明该插件,并将你的jar包重新打包:

1
2
3
4
5
6
7
8
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

如果你不使用父POM,你仍然可以使用这个插件,但是你必须另外添加一个<executions>部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

有关完整的使用详细信息,请参阅插件文档

使用Spring Boot应用程序作为依赖

像war文件一样,Spring Boot应用程序并不打算用作依赖项。如果你的应用程序包含要与其他项目共享的类,则推荐的方法是将该代码移动到单独的模块中。你的应用程序和其他项目可以依靠这个单独的模块。

如果你不能按照上面的建议重新调整你的代码,那么Spring Boot的Maven和Gradle插件必须被配置为产生一个适合用作依赖项的独立的组件。可执行文件不能用作依赖项,因为可执行的jar格式BOOT-INF/classes中打包应用程序类。这意味着当可执行jar被用作依赖时,它们不能被找到。

为了生成两个组件,一个可以用作依赖关系,一个可执行,必须指定一个分类器。此分类器应用于可执行文件的名称,保留用作依赖项的默认文件。

要在Maven中配置exec的分类器,可以使用以下配置:

1
2
3
4
5
6
7
8
9
10
11
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>

当可执行的jar运行时,提取特定的库

可执行jar中的大多数内部库不需要解压来运行,但某些库可能会有问题。例如,JRuby包含自己的内部jar支持,它假设jruby-complete.jar总是直接作为一个文件直接使用。

为了处理任何有问题的库,可以标记特定的内部jar应该在可执行jar第一次运行时自动解压到“temp文件夹”。

例如,为了说明JRuby应该被标记为使用Maven Plugin解压,你可以添加以下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<requiresUnpack>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-complete</artifactId>
</dependency>
</requiresUnpack>
</configuration>
</plugin>
</plugins>
</build>

使用exclusions 创建不可执行的JAR

通常,如果你将可执行文件和不可执行的jar作为构建产品,则可执行文件版本将具有作为库的jar中不需要的其他配置文件。 例如,不可执行的JAR中可能会排除application.yml配置文件。

maven-jar-plugin用来暴露一个forceCreation属性,允许你在repackage goal 运行后再次创建jar。可以说,由于它依赖于插件执行的顺序,所以它有点脆弱。 在Maven中,可执行的jar文件必须是主要的工件,你可以为库添加一个分类的jar:

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
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>lib</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>lib</classifier>
<excludes>
<exclude>application.yml</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

远程调试Maven启动的Spring Boot应用程序

要将一个远程调试器连接到一个使用Maven启动的Spring Boot应用程序,你可以使用maven插件jvmArguments属性。

查看这个例子了解更多细节。

用Ant构建可执行存档时不使用spring-boot-antlib

传统部署方式

创建可部署的war文件

生成可部署的war文件的第一步是提供一个SpringBootServletInitializer子类并覆盖其configure方法。 这将使用Spring框架的Servlet 3.0支持功能,并当它由servlet容器启动时允许你配置你的应用程序。通常,你更新你的应用程序的主类来继承SpringBootServletInitializer

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootApplication
public class Application extends SpringBootServletInitializer {

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}

public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}

}

下一步是更新你的构建配置,以便你的项目生成一个war文件,而不是一个jar文件。 如果你使用的是Maven,并使用spring-boot-starter-parent(会为你配置Maven的war插件),你只需修改pom.xml将 packaging 更改为war:

1
<packaging>war</packaging>

如果你使用的是Gradle,则需要修改build.gradle启用war插件:

1
apply plugin: 'war'

这个过程的最后一步是确保嵌入的servlet容器不会干扰要部署war文件的servlet容器。 为此,你需要标记嵌入式servlet容器依赖项为provided。

在Maven中:

1
2
3
4
5
6
7
8
9
<dependencies>
<!-- … -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!-- … -->
</dependencies>

在Gradle中:

1
2
3
4
5
dependencies {
// …
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
// …
}

providedRuntime比Gradle的compileOnly配置更受欢迎,因为在其他限制中,compileOnly依赖不在测试类路径上,因此任何基于Web的集成测试都将失败。

如果你使用的是Spring Boot构建工具,则标记嵌入的Servlet容器依赖项为provided将生成一个可执行的war文件,其中提供的依赖项打包在lib-provided的目录中。这意味着,除了可以部署到servlet容器之外,还可以在命令行上使用java -jar运行应用程序。

查看Spring Boot的示例应用程序,可以找到上述配置的基于Maven的示例

为老版本servlet容器创建可部署war文件

较老的Servlet容器不支持Servlet 3.0中使用的ServletContextInitializer引导进程。你仍然可以在这些容器中使用Spring和Spring Boot,但是你将需要将web.xml添加到你的应用程序,并将其配置为通过DispatcherServlet加载ApplicationContext

将现有应用转换为Spring Boot应用

对于非Web应用程序来说,应该很简单(丢弃创建ApplicationContext的代码并将其替换为对SpringApplicationSpringApplicationBuilder的调用)。 Spring MVC Web应用程序通常可以首先创建可部署的war应用程序,然后稍后将其迁移到可执行的war和/或jar。 “将jar转换为war的入门指南”可能对你非常有用。

通过扩展SpringBootServletInitializer(例如,在一个名为Application的类)中创建一个可部署的war,并添加Spring Boot @SpringBootApplication注解。 例如:

1
2
3
4
5
6
7
8
9
10
11
12
@SpringBootApplication
public class Application extends SpringBootServletInitializer {

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
// Customize the application or call application.sources(...) to add sources
// Since our example is itself a @Configuration class (via @SpringBootApplication)
// we actually don't need to override this method.
return application;
}

}

请记住,无论你放在源代码中的是什么,只是一个Spring ApplicationContext,通常任何已经工作的东西都应该在这里工作。可能稍后会删除一些bean,并让Spring Boot 为它们提供自己的默认值,但应该可以先做一些工作。

可以将静态资源移动到类路径根中的/public(或/static/resources或/ META-INF/resources)。messages.properties也是如此。(Spring Boot会在类路径的根目录中自动检测到这个)。

Spring DispatcherServlet和Spring Security的使用不需要进一步的修改。如果你的应用程序中有其他功能,例如使用其他servlet或过滤器,那么你可能需要在Application上下文中添加一些配置,从web.xml中替换这些元素,如下所示:

  • ServletServletRegistrationBean类型的@Bean将该bean安装到容器中,就好像它是web.xml中的<servlet /><servlet-mapping />一样。

  • 类型为FilterFilterRegistrationBean@Bean的行为类似(如<filter /><filter-mapping />)。

  • XML文件中的ApplicationContext可以通过Application中的@ImportResource添加。 或者注解配置大量使用的简单情况已经可以作为@Bean定义在几行代码中重新创建。

    一旦war 生效,我们通过向我们的Application添加main方法来使其可以执行。例如:

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

如果你打算将应用作为war或可执行应用程序启动,则需要在SpringBootServletInitializer回调和main方法都可用的方法中共享构建器的自定义设置,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>@SpringBootApplication
>public class Application extends SpringBootServletInitializer {

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return configureApplication(builder);
}

public static void main(String[] args) {
configureApplication(new SpringApplicationBuilder()).run(args);
}

private static SpringApplicationBuilder configureApplication(SpringApplicationBuilder builder) {
return builder.sources(Application.class).bannerMode(Banner.Mode.OFF);
}

>}

应用程序可以分为多个类别:

  • 没有web.xml的Servlet 3.0+应用程序。
  • 带有web.xml的应用程序.
  • 具有上下文层次的应用程序.
  • 没有上下文层次的应用程序.

所有这些都应该适合转换,但每个可能需要稍微不同的技巧。

如果Servlet 3.0+应用程序已经使用Spring Servlet 3.0+初始化程序支持类,那么它们可能会非常容易转换。通常,来自现有WebApplicationInitializer的所有代码都可以移入SpringBootServletInitializer。如果你现有的应用程序有多个ApplicationContext(例如,如果它使用AbstractDispatcherServletInitializer),那么你可能能够将所有上下文源压缩到一个SpringApplication中。你可能遇到的主要难题是如果这不起作用,你需要维护上下文层次结构。请参阅构建层次结构的示例。现有的包含Web特定功能父上下文通常需要分解,以便所有ServletContextAware组件都在子上下文中。

不是Spring应用的应用程序可能会转换成Spring Boot应用程序,上面的指导可能会有帮助,但是你的路可能会有所不同。

部署WAR到WebLogic

要将Spring Boot应用程序部署到WebLogic,必须确保servlet初始化程序直接实现WebApplicationInitializer(即使从已经实现它的基类中进行继承)。

一个典型的WebLogic初始化器是这样的:

1
2
3
4
5
6
7
8
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.web.WebApplicationInitializer;

@SpringBootApplication
public class MyApplication extends SpringBootServletInitializer implements WebApplicationInitializer {

}

如果使用logback,则还需要告知WebLogic更喜欢打包的版本,而不是预装在服务器上的版本。 你可以通过添加具有以下内容的WEB-INF/weblogic.xml文件来执行此操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app
xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd
http://xmlns.oracle.com/weblogic/weblogic-web-app
http://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd">
<wls:container-descriptor>
<wls:prefer-application-packages>
<wls:package-name>org.slf4j</wls:package-name>
</wls:prefer-application-packages>
</wls:container-descriptor>
</wls:weblogic-web-app>

在老版本容器(Servlet 2.5)中部署WAR

Spring Boot使用Servlet 3.0 API来初始化ServletContext(注册Servlets等),所以不能在Servlet 2.5容器中使用相同的应用程序。然而,使用一些特殊的工具可以在一个较老的容器上运行Spring Boot应用程序。 如果你依赖了org.springframework.boot:spring-boot-legacy独立于Spring Boot的核心并且目前在1.0.2.RELEASE上提供),那么你只需要创建一个web.xml并 声明一个上下文监听器来创建应用程序上下文以及你的过滤器和servlet。上下文监听器是Spring Boot的特殊用途,但其余部分对于Servlet 2.5中的Spring应用程序来说是正常的。 例如:

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
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>demo.Application</param-value>
</context-param>

<listener>
<listener-class>org.springframework.boot.legacy.context.web.SpringBootContextLoaderListener</listener-class>
</listener>

<filter>
<filter-name>metricsFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
<filter-name>metricsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.context.WebApplicationContext.ROOT</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>

在这个例子中,我们使用一个应用程序上下文(由上下文监听器创建的上下文),并使用init参数将其附加到DispatcherServlet。 这在Spring Boot应用程序中是正常的(通常你只有一个应用程序上下文)。

使用Jedis 代替Lettuce

Spring Boot starter(spring-boot-starter-data-redis)默认使用Lettuce。 你需要排除该依赖关系,并包含Jedis。 Spring Boot管理这些依赖关系,以使这个过程尽可能简单。
在Maven中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>

在Gradle中:

1
2
3
4
5
6
7
8
configurations {
compile.exclude module: "lettuce"
}

dependencies {
compile("redis.clients:jedis")
// ...
}