【Spring Boot官方文档原文理解翻译-持续更新中】
文章目录
- 【Spring Boot官方文档原文理解翻译-持续更新中】
- Chapter 4. Getting Started
- 4.1. Introducing Spring Boot
- 4.2. System Requirements
- 4.2.1. Servlet Containers
- 4.3. Installing Spring Boot
- 4.3.1. Installation Instructions for the Java Developer
- 4.3.1.1. Maven Installation
- 4.3.2. Installing the Spring Boot CLI
- Manual Installation
- Installation with SDKMAN!
- OSX Homebrew Installation
- Command-line Completion
- 4.4. Developing Your First Spring Boot Application
- Chapter 6. Developing with Spring Boot
- 6.1. Build Systems
- 6.1.1. Dependency Management
- 6.1.2. Maven
- 6.1.3. Gradle
- 6.1.4. Ant
- 6.1.5. Starters
- 6.2. Structuring Your Code
- 6.2.1. Using the “default” Package
- 6.2.2. Locating the Main Application Class
- 6.3. Configuration Classes
- 6.3.1. Importing Additional Configuration Classes
- 6.3.2. Importing XML Configuration
- 6.4. Auto-configuration
- 6.4.1. Gradually Replacing Auto-configuration
- 6.4.2. Disabling Specific Auto-configuration Classes
- 6.6. Using the @SpringBootApplication Annotation
- Chapter 7. Core Features
- 7.4.6. Log Groups
- 7.6. JSON
- 7.6.1. Jackson
- Custom Serializers and Deserializers
- Chapter 8. Web
- 8.1. Servlet Web Applications
- 8.1.1. The “Spring Web MVC Framework”
- Spring MVC Auto-configuration
- HttpMessageConverters
- MessageCodesResolver
- Static Content
- Welcome Page
- Custom Favicon
- Path Matching and Content Negotiation
- ConfigurableWebBindingInitializer
- Template Engines
- Error Handling
- Custom Error Pages
- 8.1.2. JAX-RS and Jersey
- 8.1.3. Embedded Servlet Container Support
- Servlets, Filters, and listeners
- Servlet Context Initialization
- The ServletWebServerApplicationContext
- Customizing Embedded Servlet Containers
- Customizing ConfigurableServletWebServerFactory Directly
- JSP Limitations
- 8.2. Reactive Web Applications
- 8.2.1. The “Spring WebFlux Framework”
- Spring WebFlux Auto-configuration
- HTTP Codecs with HttpMessageReaders and HttpMessageWriters
- Static Content
- Welcome Page
- Template Engines
- Error Handling
- Chapter 13. Production-ready Features
- Spring MVC Metrics
- Chapter 14. Deploying Spring Boot Applications
- 14.2. Installing Spring Boot Applications
- Chapter 17. “How-to” Guides
- 17.1. Spring Boot Application
- 17.3. Embedded Web Servers
- 17.3.1. Use Another Web Server
- 17.3.7. Configure SSL
- 17.3.6. Enable HTTP Response Compression
- 17.3.11. Configure Access Logging
- 17.7.2. Configure Log4j for Logging
- 17.16.9. Build an Executable Archive from Ant without Using spring-boot-antlib
- Appendices
- Appendix A: Common Application Properties
- A.11. Server Properties
Chapter 4. Getting Started
如果您想入门 Spring Boot 或是 Spring,那么就从阅读这一部分开始吧。它会回答这些基础的 “是什么?”、“怎么做?” 和 “ 为什么?” 这类问题,并且包含了 Spring Boot 的详细介绍和使用说明。稍后我们将指导您搭建第一个 Spring Boot 应用,并且谈论一些其中的核心原则。
4.1. Introducing Spring Boot
Spring Boot 帮助您创建一个单例的、生产级的且基于 Spring 的应用程序。因为我们对 Spring 平台和第三方库保持中立,所以您可以以最少的麻烦上手 (Spring Boot),大多数的 Spring Boot 应用程序甚至只需要很少的 (近乎没有的) Spring 配置。
Spring Boot 可以创建 Java 应用程序,这些程序通过 java -jar
命令或者传统 war 包依赖启动,而且 (为了方便) 我们还提供了一个运行 “ spring scripts (spring 脚本)” 的命令行工具。
我们的主要目标是
- 致力于为所有 Spring 的开发者提供更快速、可广泛访问的入门体验
- 保持开箱即用,虽然随着需求开始会偏离默认值但是会迅速摆脱困境
- 提供一系列非功能性的特性,且通常是大类别的项目(例如内置服务器、安全、度量、运行状况检查和外部化配置)
- 无任何代码生成并且不需要 XML 配置
4.2. System Requirements
Spring Boot 2.7.2 版本至少需要 Java 8 且兼容包含 Java 18 在内的更高JDK版本,Spring Framework 5.3.22 (或更高版本) 也需要。
为下列搭建工具提供了显式支持:
搭建工具 | 版本 |
Maven | 3.5+ |
Gradle | 6.8.x, 6.9.x, 和 7.x |
4.2.1. Servlet Containers
Spring Boot 支持下列嵌入式的 servlet 容器:
Servlet 容器名称 | Servlet 版本 |
Tomcat 9.0 | 4.0 |
Jetty 9.4 | 3.1 |
Jetty 10.0 | 4.0 |
Undertow 2.0 | 4.0 |
而且,也可以把 Spring Boot 应用部署在任何兼容 servlet 3.1+ 版本的容器中
4.3. Installing Spring Boot
如果不想使用前面的两种方式也没关系,您只需要有 Java SDK v1.8 或更高版本即可。此外,在开始使用之前,您应该先使用下列命令检查电脑当前安装的 Java 版本
$ java -version
如果您是 Java 开发菜鸟或者只是想浅尝辄止,最好先试试 Spring Boot CLI (Spring Boot 命令行接口),而且需要先阅读 “ classic ” 的安装指导。
4.3.1. Installation Instructions for the Java Developer
您可以像使用任何 Java 标准库那样使用 Spring Boot,为此请引入对应版本的 spring-boot-*.jar
文件到类路径下。Spring Boot 不需要任何指定的集成工具,所以可以使用任何 IDE(全称是Intergration Development Environment,意为集成开发环境)或者编辑器进行软件开发。此外,Spring Boot 平平无奇,所以我们可以像运行其他的 Java 程序一样去运行和调试 Spring Boot 应用程序。
因为这样管理起来更方便
4.3.1.1. Maven Installation
Spring Boot 兼容 Apache Maven 3.3+ 版本。如果您还没有安装 Maven 的话,建议按照 maven.apache.org 站点上的说明进行安装配置。
小贴士
大多数的操作系统上,Maven 会与包管理器进行捆绑安装。如果使用 OSX Homebrew 包管理器的话,尝试运行
brew install maven
命令安装 Maven,Ubuntu 用户运行sudo apt-get install maven
命令也可以安装 Maven。而带有 Chocolatey 软件的 Windows 用户则可以从高级(管理员)提示符中运行choco install maven
安装 Maven
Spring Boot 依赖使用 org.springframework.boot.groupId
作为组织名。按照惯例,Maven 的 POM 文件继承 spring-boot-starter-parent
项目的同时会声明一个或多个 “ 启动器 ” 的依赖。不过 Spring Boot 自身也提供了可选的 Maven 插件来创建可执行的 jar 包。
4.3.2. Installing the Spring Boot CLI
Spring Boot CLI (Command Line Interface:命令行接口) 是一个命令行工具,可快速开发 Spring 原型。并且它可以运行 Groovy 脚本,这意味着只需很少的样板代码就可以进行快速开发,因为您熟悉 Java 语法。
虽然不一定需要通过 CLI 使用 Spring Boot,但是它也是一个快捷的方法,让您不需要 IDE 也可以脱离后台获取 Spring 应用。
Manual Installation
Spring CLI 正式版本在 Spring 软件仓库下载:
此外,(如果有需要的话) 这个仓库还提供最新的快照版本,下载后,按照 INSTALL.txt 指导文件从解压缩开始操作。
综上所述,这是一个 .zip
文件 bin/
目录下的 spring
脚本(spring.bat
用于 Windows操作系统 )。此外, java -jar
命令可以与 .jar
文件混用(脚本有助于设置正确的 classpath 目录)
Installation with SDKMAN!
SDKMAN! (软件开发工具管理员) 用于管理多个版本的各种二进制软件开发工具包,其中包含 Groovy 和 Spring Boot CLI,(如果我们想利用SDKMAN安装SpringBoot) 可以从 sdkman.io 站点中获取 SDKMAN 并且使用下面的命令安装它:
$ sdk install springboot
$ spring --version
Spring CLI v2.7.2
如果想为 CLI 开发功能并且想要访问自己构建的版本,那么请使用下面的命令:
$ sdk install springboot dev /path/to/spring-boot/spring-boot-cli/target/spring-bootcli-2.7.2-bin/spring-2.7.2/
$ sdk default springboot dev
$ spring --version
Spring CLI v2.7.2
前面的指导会安装一个名为 dev
的本地 spring
实例。此外它会指向搭建的目标位置,所以每次重构 Spring Boot 时,Spring 的版本都是最新的。
运行下列命令查看:
$ sdk ls springboot
================================================================================
Available Springboot Versions
================================================================================
> + dev
* 2.7.2
================================================================================
+ - local version
* - installed
> - currently in use
================================================================================
OSX Homebrew Installation
如果在 Mac 上使用 HomeBrew,使用下列命令安装 Spring Boot CLI:
$ brew tap spring-io/tap
$ brew install spring-boot
Homebrew 会将 spring
安装到 /usr/local/bin
目录上。
注意
如果您没有看到公式,那么安装的 brew 可能已经过时。在这种情况下,应该运行
brew update
升级它并且再试一次。
Command-line Completion
Spring Boot CLI 包含 BASH 和 zsh 的 shell 命令脚本,您可以在任何shell 中使用 source 命令脚本(也叫spring)或者…
4.4. Developing Your First Spring Boot Application
Chapter 6. Developing with Spring Boot
本章将会更加详细地介绍如何正确使用 Spring Boot,覆盖了类似于系统搭建、自动配置和如何运行应用程序的话题。除此之外,也包含了一些 (使用) Spring Boot 最佳实践。虽然 Spring Boot 平平无奇 (只不过就是另一个您可以使用的库),但是下面的一些建议,可以让开发的过程变得更简单。
如果是从 Spring Boot 才开始的 (指跳过了 Spring 直接学习 Spring Boot),那么建议您在阅读这一章前先阅读 入门
6.1. Build Systems
强烈建议选择支持 依赖管理 并且可以发布到 “ Maven Central ” 仓库(Maven 中心仓库)的依赖自动化搭建系统,最好是 Maven 或者 Gradle。当然也可以让 Spring Boot 与其他的自动化搭建系统一起工作(例如:Ant),不过对比前面两种,我们对其他的支持力度不大。
6.1.1. Dependency Management
所有的 Spring Boot 的正式版本都提供了它所支持的一个依赖管理列表。在开发中,不需要为这些依赖手动提供版本号,Spring Boot 将自动管理。当 Spring Boot 更新升级时,这些依赖也会同时进行更新升级。
注意
如果需要的话,可以手动指定某个依赖的版本号并且覆盖 Spring Boot 推荐的。
依赖管理列表包含了所有可以和 Spring 模块一起使用的第三方库的依赖细化列表。这个列表可以作为一个标准的材料清单 (spring-boot dependencies
),用于 Maven 或者 Gradle。
警告
所有的 Spring Boot 的正式版本都与 Spring 框架的一个基础版本相关联,强烈建议不要手动指定其版本号
6.1.2. Maven
要了解如何使用 Spring Boot 与 Maven,请查阅 Spring Boot 的 Maven 插件文档:
6.1.3. Gradle
要了解如何使用 Spring Boot 与 Gradle,请查阅 Spring Boot 的 Gradle 插件文档:
6.1.4. Ant
搭建 Spring Boot 项目可以使用 Apache 的 Ant + lvy。spring-boot-antlib
模块也有助于 Ant 创建可执行的 jar 包。
典型的声明依赖关系的 ivy.xml
文件如下所示:
<ivy-module version="2.0">
<info organisation="org.springframework.boot" module="spring-boot-sample-ant" />
<configurations>
<conf name="compile" description="everything needed to compile this module" />
<conf name="runtime" extends="compile" description="everything needed to run
this module" />
</configurations>
<dependencies>
<dependency org="org.springframework.boot" name="spring-boot-starter"
rev="${spring-boot.version}" conf="compile" />
</dependencies>
</ivy-module>
典型的 build.xml
文件如下所示:
<project
xmlns:ivy="antlib:org.apache.ivy.ant"
xmlns:spring-boot="antlib:org.springframework.boot.ant"
name="myapp" default="build">
<property name="spring-boot.version" value="2.7.2" />
<target name="resolve" description="--> retrieve dependencies with ivy">
<ivy:retrieve pattern="lib/[conf]/[artifact]-[type]-[revision].[ext]" />
</target>
<target name="classpaths" depends="resolve">
<path id="compile.classpath">
<fileset dir="lib/compile" includes="*.jar" />
</path>
</target>
<target name="init" depends="classpaths">
<mkdir dir="build/classes" />
</target>
<target name="compile" depends="init" description="compile">
<javac srcdir="src/main/java" destdir="build/classes"
classpathref="compile.classpath" />
</target>
<target name="build" depends="compile">
<spring-boot:exejar destfile="build/myapp.jar" classes="build/classes">
<spring-boot:lib>
<fileset dir="lib/runtime" />
</spring-boot:lib>
</spring-boot:exejar>
</target>
</project>
小贴士
如果不希望使用
spring-boot-antlib
模块,请查看 How-to Guides 中的 Build an Executable Archive from Ant without Using spring-boot-antlib
6.1.5. Starters
启动器是一套便捷的依赖描述符,可以将其引入到应用程序中。有了启动器,就获得了所有 Spring 和其相关技术的一站式服务,不需要亲自查找示例代码并且复制粘贴大量的依赖描述符。例如,如果想要使用 Spring 与 JPA 访问数据库,可以在项目中包含 spring-boot-starter-data-jpa
启动器。
启动器包含许多快速启动以及运行项目所需的依赖,并且相互之间具有一致的、受支持的托管依赖传递关系。
在名称里的东西是什么?
所有的官方启动器都遵循一个类似的命名模板:
spring-boot-starter-*
,*
是一个独特的应用类型。这种命名结构旨在当需要找到启动器时提供帮助。大部分的 IDE 中集成的 Maven 允许按名称查找依赖,例如,安装了合适的 Eclipse 或者是 Spring Tools 插件后,在 POM 编辑器中按 ctrl + space 键入“ spring-boot-starter ” 可以获得完整的依赖列表。正如 “ Creating Your Own Starter ” (创造自己的启动器)所言,第三方启动器不应该以
spring-boot
开头,因为这是为 Spring Boot 官方依赖所保留的。不过,一个第三方启动器常常会以项目的名称作为开头(所以一般不用担心)。比如,一个叫做thirdpartproject
的第三方项目将会毫无疑问地以thirdpartproject-spring-boot-starter
命名
Spring Boot的 org.springframework.boot
分组提供下列应用启动器:
表1.Spring Boot 应用启动器
名称 | 简要描述 |
spring-boot-starter | Spring Boot 的核心启动器,包含自动配置支持,日志记录和 YAML |
spring-boot-starter-activemq | JMS(Java消息服务:是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。)使用 Apache 的 ActiveMQ消息队列发送消息的启动器 |
spring-boot-starter-amqp | Spring 的 AMQP(AMQP:即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议)和Rabbit MQ(RabbitMQ:这是一个开源消息代理软件。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。)的启动器 |
spring-boot-starter-aop | Spring AOP 切面方向的编程和 AspectJ 的启动器 |
spring-boot-starter-artemis | JMS 使用 Apache 的 Artemis 的启动器 |
spring-boot-starter-batch | Spring 批处理任务的启动器 |
spring-boot-starter-cache | Spring 框架缓存支持的启动器 |
spring-boot-starter-data-cassandra | Cassandra 分布式数据库(Cassandra:是一套开源分布式NoSQL数据库系统。它最初由Facebook开发,用于储存收件箱等简单格式数据,集GoogleBigTable的数据模型与Amazon Dynamo的完全分布式的架构于一身Facebook于2008将 Cassandra 开源,此后,由于Cassandra良好的可扩展性,被Digg、Twitter等知名[Web 2.0](https://baike.baidu.com/item/Web 2.0)网站所采纳,成为了一种流行的分布式结构化数据存储方案。)和 String Data Cassandra 的启动器 |
spring-boot-starter-data-cassandra-reactive | Cassandra 分布式数据库和 String Data Cassandra Reactive 的启动器 |
spring-boot-starter-data-couchbase | 使用 Couchbase 面向文档数据库(CouchBase:是一款开源的、分布式的、面向文档的NoSQL数据库,主要用于分布式缓存和数据存储领域。能够通过manage cache提供快速的亚毫米级别的k-v存储操作,并且提供快速的查询和其功能强大的能够指定SQL-like查询的查询引擎。)和 Spring Data Couchbase 的启动器 |
spring-boot-starter-data-couchbase-reactive | Cassandra 分布式数据库和 String Data Cassandra Reactive 的启动器 |
spring-boot-starter-data-elasticsearch | Elasticsearch 搜索(Elasticsearch:是位于 Elastic Stack 核心的分布式搜索和分析引擎。Logstash 和 Beats 有助于收集、聚合和丰富您的数据并将其存储在 Elasticsearch 中。Kibana 使您能够以交互方式探索、可视化和分享对数据的见解,并管理和监控堆栈。Elasticsearch 是索引、搜索和分析魔法发生的地方。)、分析引擎和Spring Data Elasticsearch的启动器 |
spring-boot-starter-data-jdbc | Spring Data JDBC(JDBC:全称Java Database Connectivity,也即Java数据库连接,简称JDBC。它是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。)的启动器 |
spring-boot-starter-data-jpa | Spring Data JPA(JPA:JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。)与Hibernate(Hibernate:是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的JavaEE架构中取代CMP,完成数据持久化的重任。)的启动器 |
spring-boot-starter-data-ldap | Spring Data LDAP(LDAP:全称Lightweight Directory Access Protocol, 意为轻型目录访问协议,它是一个开放的,中立的,工业标准的应用协议,通过IP协议提供访问控制和维护分布式信息的目录信息。)的启动器 |
spring-boot-starter-data-mongodb | MongoDB 面向文档数据库(MongoDB:是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。)和 Spring Data MongoDB 的启动器 |
spring-boot-starter-data-mongodb-reactive | MongoDB 面向文档数据库和 Spring Data MongoDB Reactive 的启动器 |
spring-boot-starter-data-neo4j | Neo4j 图形数据库(Neo4j:是一个高性能的,NOSQL图形数据库,它将结构化数据存储在网络上而不是表中。它是一个嵌入式的、基于磁盘的、具备完全的事务特性的Java持久化引擎,但是它将结构化数据存储在网络(从数学角度叫做图)上而不是表中。)和 Spring Data Neo4j 的启动器 |
spring-boot-starter-data-r2dbc | Spring Data R2DBC 的启动器 |
spring-boot-starter-data-redis | Spring Data Redis 存储 Redis 键值对与和 Lettuce client 的启动器 |
spring-boot-starter-data-redis-reactive | Spring Data Redis reactive 存储 Redis 键值对和 Lettuce client 的启动器 |
spring-boot-starter-data-rest | 用于 Spring Data REST 在 Rest 上公开 Spring Data 仓库的启动器 |
spring-boot-starter-freemarker | 使用FreeMarker视图(FreeMarker:是一款模板引擎, 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网络、电子邮件、配置文件、源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。)搭建MVC网络应用而生的启动器 |
spring-boot-starter-graphql | Spring GraphQL搭建GraphQL应用的启动器 |
spring-boot-starter-groovy-templates | 用于 Groovy 模板视图(Groovy:是一种基于JVM(Java虚拟机)的敏捷开发语言,它结合了Python、Ruby和Smalltalk的许多强大的特性,Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码。由于其运行在 JVM 上的特性,Groovy也可以使用其他非Java语言编写的库。)搭建 MVC 网络应用的启动器 |
spring-boot-starter-hateoas | Spring MVC 搭建基于超媒体和 Restful 风格的网络应用和 Spring HATEOAS 的启动器 |
spring-boot-starter-integration | Spring Integration 的启动器 |
spring-boot-starter-jdbc | HikariCP连接池使用 JDBC 的启动器 |
spring-boot-starter-jersey | 用 JAX-RS 和 Jersey 搭建 Restful 风格的网络应用的启动器。此外,也可以选用 |
spring-boot-starter-jooq | JDBC 使用 jOOQ 访问 SQL 数据库的启动器。此外,也可以选用 |
spring-boot-starter-json | 读写 JSON(JSON:全称JavaScript Object Notation, JS对象简谱,这是一种轻量级的数据交换格式。它基于 ECMAScript(European Computer Manufacturers Association, 欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。)的启动器 |
spring-boot-starter-jta-atomikos | JTA 事务(JTA:即Java Transaction API,JTA允许应用程序执行分布式事务处理——在两个或多个网络计算机资源上访问并且更新数据。JDBC驱动程序的JTA支持极大地增强了数据访问能力。)使用 Atomikos(Atomikos: 是一个为Java平台提供增值服务的并且开源类事务管理器。)的启动器 |
spring-boot-starter-mail | Java 和 Spring 框架的邮件发送支持的启动器 |
spring-boot-starter-mustache | Mustache 视图搭建网络应用的启动器 |
spring-boot-starter-oauth2-client | Spring Security 的 OAuth2(OAuth2:是一种授权框架,提供了一套详细的授权机制(指导)。用户或应用可以通过公开的或私有的设置,授权第三方应用访问特定资源。)或 OpenID 连接客户端特性使用的启动器 |
spring-boot-starter-oauth2-resource-server | Spring Security 的 OAuth2 资源服务器特性使用的启动器 |
spring-boot-starter-quartz | Spring Quartz 的启动器 |
spring-boot-starter-rsocket | 搭建 RSocket 客户端和服务器的启动器 |
spring-boot-starter-security | Spring Security 的启动器 |
spring-boot-starter-test | 使用包含 JUnit Jupiter、Hamcrest 和 Mockito 的依赖代码库测试 Spring Boot 应用的启动器 |
spring-boot-starter-thymeleaf | Thymeleaf 视图搭建 MVC 网络应用的启动器 |
spring-boot-starter-validation | Java Bean 验证与 Hibernate 验证器的启动器 |
spring-boot-starter-web | Spring MVC搭建包含Restful风格的网络应用的启动器。使用Tomcat作为默认嵌入式容器 |
spring-boot-starter-web-services | Spring 网络服务的启动器 |
spring-boot-starter-webflux | Spring 框架的 Reactive 网络支持搭建 WebFlux 应用的启动器 |
spring-boot-starter-websocket | Spring 框架的 WebSocket 支持搭建 WebSocket 应用的启动器 |
除了上面提到的应用启动器,下列启动器常常用于添加 生产就绪
表2.Spring Boot生产启动器
名称 | 简要描述 |
spring-boot-starter-actuator | 使用Spring Boot的Actuator提供生产就绪特性帮助监督和管理您的应用的启动器 |
最后,Spring Boot还包含了以下启动器,如果您想排除或者更换指定技术面的话会用到:
表3.Spring Boot技术启动器
名称 | 简要描述 |
spring-boot-starter-jetty | 使用Jetty作为内置的servlet容器的启动器,也可选用 |
spring-boot-starter-log4j2 | 使用Log4j2的日志记录的启动器,也可选用 |
spring-boot-starter-logging | 使用Logback记录日志的启动器,它也是Spring默认日志记录启动器 |
spring-boot-starter-reactor-netty | 使用Reactor Netty作为内置的reactive HTTP 服务器的启动器 |
spring-boot-starter-tomcat | 使用Tomcat作为内置的servlet容器的启动器,也是 |
spring-boot-starter-undertow | 使用Undertow(Undertow:是一个采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制。Undertow 是红帽公司的开源产品,是 Wildfly 默认的 Web 服务器。)作为内置servlet容器的启动器,也可选用 |
要了解如何更换技术面,请查看 swapping web server 和 logging system
小贴士
对于社区贡献的启动器列表,可以查看 Github上 的
spring-boot-starters
模块中的 README 文件
6.2. Structuring Your Code
Spring Boot 不需要使用任何指定的布局代码来工作,不过也有些最佳实践是有所帮助的。
6.2.1. Using the “default” Package
当一个类不包含 package
关键字的声明,它被认为在 “ 默认包 ” 下。这种 “ 默认包 ” 的使用通常是不被鼓励且应该摒弃的。在 Spring Boot 使用 @ComponentScan
、@ConfigurationPropertiesScan
、@EntityScan
、或者是@SpringBootApplication
注解的时候, 它会引起奇怪的 BUG,因为每个 jar 包里的类都是可读的
小贴士
建议遵守 Java 的推荐包命名规范且使用反向域名(例如:
com.example.project
)
6.2.2. Locating the Main Application Class
我们通常推荐在其他类上面的根路径包中查找主应用类。 @SpringBootApplication 注解 常常用于放置在主类上,并且暗中为某些项目定义了一个基础的 “ 搜索包 ” 。例如,如果您编写一个 JPA 应用,@SpringBootApplication
注释的包将用于定位 @Entity
注解项。此外,使用根路径包也允许组件扫描仅用于项目。
小贴士
如果不想使用
@SpringBootApplication
注解, 它导入的@EnableAutoConfiguration
和@ComponentScan
注解也定义了那些行为(这里的行为指的是组件扫描,自动配置等功能),换成这两个注解也是可行的
下面的示例代码展示了一个典型的布局:
com
+- example
+- myapplication
+- MyApplication.java
|
+- customer
| +- Customer.java
| +- CustomerController.java
| +- CustomerService.java
| +- CustomerRepository.java
|
+- order
+- Order.java
+- OrderController.java
+- OrderService.java
+- OrderRepository.java
MyApplication.java
文件将会声明main方法,以及基本的 @SpringBootApplication
注解,如下所示:
Java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class MyApplication
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
6.3. Configuration Classes
Spring Boot 偏爱基于 Java 的配置,虽然 XML 文件源也可以使用 SpringApplication
,但是我们通常建议您的主要来源应当是一个独立的 @Configuration
类。通常来说,定义 main
方法的类是一个合格的 @Configuration
的候选者
6.3.1. Importing Additional Configuration Classes
您不需要把所有的 @Configuration
放在一个独立的类中,比如 @Import
注解常常用于导入其他的配置类。此外,您还可以使用 @ComponentScan
注解,它会自动装配所有包含 @Configuration
注解的类将其作为 Spring 的组件。
6.3.2. Importing XML Configuration
如果您必须使用基于 XML 的配置,那么我们也建议您先从 @Configuration
类开始,然后再使用 @ImportResource
注解加载 XML 配置文件。
6.4. Auto-configuration
Spring Boot 自动配置基于添加的 jar 包依赖自动构建 Spring 应用程序。例如:如果 HSQLDB
(Hyper SQL Database) 在类路径下,并且没有手动配置任何数据库连接的 bean,那么 Spring Boot 自动配置一个在内存中的数据库。
通过往某一个 @Configuration
配置类中添加 @EnableAutoConfiguration
或者 @SpringBootApplication
注解来选择使用自动配置。
小贴士
您应该只添加一个
@SpringBootApplication
或者@EnableAutoConfiguration
注解,通常建议只将某一个添加到您的@Configuration
配置类中
6.4.1. Gradually Replacing Auto-configuration
自动配置是非侵入式的,在任何时候,都可以使用自定义配置更换特定的自动配置。例如:如果您添加自定义的 DataSource
的 bean,那么默认内置的数据库就会失效。
如果需要了解当前应用的自动配置及其详情,打开应用程序 --debug
开关,这样做可以启用核心日志器进行日志调试并且将情况输出到控制台。
6.4.2. Disabling Specific Auto-configuration Classes
如果发现应用程序中有不需要的自动配置类,可以使用 @SpringBootAplication
注解的 exclude 属性来禁用他们,如下所示:
Java
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class MyApplication {
}
Kotlin
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
@SpringBootApplication(exclude = [DataSourceAutoConfiguration::class])
class MyApplication
如果该类不在当前类的类路径下,还可以使用 excludeName
属性指定类的全限定名。如果您更偏爱 @EnableAutoConfiguration
而不是 @SpringBootApplication
注解, exclude
和 excludeName
属性也能使用。顺便一提,使用 spring.autoconfigure.exclude
属性,还能排除自动配置类的列表
小贴士
您可以通过注解级别以及使用属性定义排除项
注意
虽然自动配置类是 public 的,但是这些 API 的公共部分仅用于禁用自动配置类。这些类的实际内容,类似于嵌套配置类或者只供内部使用的 bean 方法所以我们不推荐直接使用这些。
6.6. Using the @SpringBootApplication Annotation
Chapter 7. Core Features
7.4.6. Log Groups
7.6. JSON
7.6.1. Jackson
Custom Serializers and Deserializers
Chapter 8. Web
因为 Spring Boot 非常适合网络应用开发,所以您可以使用内置的 Tomcat、Jetty、Undertow 或 Netty 创建独立的 HTTP 服务器。虽然大多数的网络应用使用 spring-boot-starter-web
模块快速启动和运行,但是您也可以选择使用 spring-boot-starter-webflux
模块搭建 reactive 网络应用。
如果您还没有开发过 Spring Boot 网络应用,可以按照 Getting started 章节中的示例进行开发。
8.1. Servlet Web Applications
如果想搭建基于 servlet 的网络应用,您可以利用 Spring Boot 的 Spring MVC 或 Jersey 的自动配置实现
8.1.1. The “Spring Web MVC Framework”
Spring Web MVC framework(通常被称为 “ Spring MVC ”)是一个强大的 “ model view controller (模型-视图-控制器)” 的网络框架。Spring MVC 可以让您创建特殊的 @Controller
或 @RestController
的 bean 处理接收的 HTTP 请求,在控制器中的方法使用 @RequestMapping
注解映射 HTTP 请求。
下列代码展示了提供 JSON 数据的典型 @RestController
:
Java
import java.util.List;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/users")
public class MyRestController {
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public MyRestController(UserRepository userRepository, CustomerRepository
customerRepository) {
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
@GetMapping("/{userId}")
public User getUser(@PathVariable Long userId) {
return this.userRepository.findById(userId).get();
}
@GetMapping("/{userId}/customers")
public List<Customer> getUserCustomers(@PathVariable Long userId) {
return this.userRepository.findById(userId).map(this.customerRepository::findByUser).get();
}
@DeleteMapping("/{userId}")
public void deleteUser(@PathVariable Long userId) {
this.userRepository.deleteById(userId);
}
}
Kotlin
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/users")
class MyRestController(private val userRepository: UserRepository, private val
customerRepository: CustomerRepository) {
@GetMapping("/{userId}")
fun getUser(@PathVariable userId: Long): User {
return userRepository.findById(userId).get()
}
@GetMapping("/{userId}/customers")
fun getUserCustomers(@PathVariable userId: Long): List<Customer> {
return
userRepository.findById(userId).map(customerRepository::findByUser).get()
}
@DeleteMapping("/{userId}")
fun deleteUser(@PathVariable userId: Long) {
userRepository.deleteById(userId)
}
}
“ WebMvc.fn ” 就是功能变体,从实际的请求处理中分离路由配置,如下所示:
Java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.function.RequestPredicate;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
import static org.springframework.web.servlet.function.RequestPredicates.accept;
import static org.springframework.web.servlet.function.RouterFunctions.route;
@Configuration(proxyBeanMethods = false)
public class MyRoutingConfiguration {
private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON);
@Bean
public RouterFunction<ServerResponse> routerFunction(MyUserHandler userHandler) {
return route()
.GET("/{user}", ACCEPT_JSON, userHandler::getUser)
.GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
.DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
.build();
}
}
Kotlin
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.web.servlet.function.RequestPredicates.accept
import org.springframework.web.servlet.function.RouterFunction
import org.springframework.web.servlet.function.RouterFunctions
import org.springframework.web.servlet.function.ServerResponse
@Configuration(proxyBeanMethods = false)
class MyRoutingConfiguration {
@Bean
fun routerFunction(userHandler: MyUserHandler): RouterFunction<ServerResponse> {
return RouterFunctions.route()
.GET("/{user}", ACCEPT_JSON, userHandler::getUser)
.GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
.DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
.build()
}
companion object {
private val ACCEPT_JSON = accept(MediaType.APPLICATION_JSON)
}
}
Java
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;
@Component
public class MyUserHandler {
public ServerResponse getUser(ServerRequest request) {
...
return ServerResponse.ok().build();
}
public ServerResponse getUserCustomers(ServerRequest request) {
...
return ServerResponse.ok().build();
}
public ServerResponse deleteUser(ServerRequest request) {
...
return ServerResponse.ok().build();
}
}
Kotlin
import org.springframework.stereotype.Component
import org.springframework.web.servlet.function.ServerRequest
import org.springframework.web.servlet.function.ServerResponse
@Component
class MyUserHandler {
fun getUser(request: ServerRequest?): ServerResponse {
return ServerResponse.ok().build()
}
fun getUserCustomers(request: ServerRequest?): ServerResponse {
return ServerResponse.ok().build()
}
fun deleteUser(request: ServerRequest?): ServerResponse {
return ServerResponse.ok().build()
}
}
Spring MVC 是 Spring 核心框架的一部分,详情信息可以在 参考文档 中获取。此外,在 spring.io/guides 中的一些指南也有提及。
小贴士
您可以以模块化路由器的定义来定义任意数量RounterFunction
的 bean,如果需要应用优先级可以对 bean 进行排序。
Spring MVC Auto-configuration
Spring Boot 为 Spring MVC 提供适用于大多数应用程序的自动配置
自动配置基于Spring的默认值添加下列功能:
- 包含
ContentNegotiatingViewResolver
和BeanNameViewResolver
的 bean - 支持静态资源服务,包括WebJars(后面的章节
Coverter
、GenericConverter
和Formatter
的 bean 自动注册- 支持
HttpMessageConverters
(后面的章节 MessageCodesResolver
的自动注册(后面的章节- 静态 index.html 支持
- 自动使用
ConfigurableWebBindingInitializer
的 bean(后面的章节
如果想要保留这些自定义的 Spring Boot MVC 功能并且获取更多(例如:拦截器,格式化器,视图控制器以及其他),可以添加自己的 WebMvcConfiugurer
类型的@Configurarion
配置类,@EnableWebMvc
配置的会自动移除。
如果需要提供定制的 RequestMappingHandlerMapping
、 RequestMappingHandlerAdapter
或 ExceptionHandlerExceptionResolver
的实例,并且始终保留自定义的 Spring Boot MVC 功能,那么您需要声明一个 WebMvcRegistrations
类型的 bean ,并且使用它来提供这些定制组件的实例。
如果您想完全控制 Spring MVC,可以使用 @EnableWebMvc
注解并添加自定义的 @Configuration
配置类,或者直接添加一个自定义的 @Configuration
配置类,就像@EnableWebMvc
的 Javadoc 中描述的 @DelegatingWebMvcConfiguration
一样。
注意
Spring MVC 使用不同的
ConversionService
,他们从application.properties
或application.yaml
配置文件中读取并转换值。这意味着Period
、Duration
和DataSize
不可用且@DurationUnit
和@DataSizeUnit
注解将会被忽略。如果需要自定义 Spring MVC 使用的
ConversionService
,只要提供一个带 addFormatters 方法的WebMvcConfigurer
的 bean 即可。在这个方法中,可以注册任何喜欢的转换器,也可以托管给ApplicationConversionService
类提供的静态方法。
HttpMessageConverters
Spring MVC 使用 HttpMessageConverter
接口转换 HTTP 请求和响应,所以合理的默认值可以做到开箱即用的,例如,对象可以自动转为 JSON(使用 Jackson 库)或 XML(如果 Jackson 的 XML 扩展可用的话,使用 Jackson 的 XML 扩展,否则使用 JAXB)。在默认情况下,String 使用 UTF-8
编码。
如果您需要添加或自定义转换器,推荐使用 Spring Boot 的 HttpMessageConverters
类,如下所示:
Java
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
@Configuration(proxyBeanMethods = false)
public class MyHttpMessageConvertersConfiguration {
@Bean
public HttpMessageConverters customConverters() {
HttpMessageConverter<?> additional = new AdditionalHttpMessageConverter();
HttpMessageConverter<?> another = new AnotherHttpMessageConverter();
return new HttpMessageConverters(additional, another);
}
}
Kotlin
import org.springframework.boot.autoconfigure.http.HttpMessageConverters
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.HttpMessageConverter
@Configuration(proxyBeanMethods = false)
class MyHttpMessageConvertersConfiguration {
@Bean
fun customConverters(): HttpMessageConverters {
val additional: HttpMessageConverter<*> = AdditionalHttpMessageConverter()
val another: HttpMessageConverter<*> = AnotherHttpMessageConverter()
return HttpMessageConverters(additional, another)
}
}
所有存在于上下文中的 HttpMessageConverter 的 bean 都添加到转换器列表中,以相同的方式可以重写默认的转换器。
MessageCodesResolver
Spring MVC 有一个默认消息处理策略,从绑定错误:MessageCodesResolver
中翻译错误信息并生成错误码。如果设置了 spring.mvc.message-codes-resolver-format
属性的值为 PREFIX_ERROR_CODE
或 POSTFIX_ERROR_CODE
,那么 Spring Boot 会创建 MessageCodesResolver
(详细信息请查看 DefaultMessageCodesResolver.Format 中的枚举)
Static Content
在默认情况下,Spring Boot 从 classpath 路径下的 /static
(或 /public
或 resources
或 /META-INF/resources
)目录 或者 ServletContext
的根目录中提供静态内容,Spring MVC 中使用了 ResourceHttpRequestHandler
便于通过添加自己 WebMvcConfigurer
类并且重写 addResourceHandlers
方法来修改(默认的静态资源处理)逻辑。
独立的网络应用程序中,容器中默认的 servlet 也被作为后路启用,如果 Spring 不处理它的话,则从 ServletContext
的根目录提供内容。不过大多数时间下不会发生这种情况(除非您修改了缺省的 MVC 配置),因为 Spring 总是通过 DispatcherServlet 来处理 HTTP 请求。
虽然在默认情况下,资源映射会在 /**
路径上,但是我们可以调换 spring.mvc.static-path-pattern
属性。对于实例,可以实现移动所有资源到 /resources/**
路径,如下所示:
Properties
spring.mvc.static-path-pattern=/resources/**
Yaml
spring:
mvc:
static-path-pattern: "/resources/**"
您可以使用 spring.web.resources.static-locations
属性 (使用目录位置列表用更换默认值) 自定义静态资源路径,而 servlet 根内容路径 “ / ” 自动添加为位置
除了前面提及的 “ 标准 ” 静态资源位置之外,还有一种特别的情况是 Webjars 的内容。如果它们是以 Webjars 格式打包,那么任何 /webjars/**
路径上的资源都将从 jar 文件中提供。
小贴士
如果您的应用程序打包成 jar 包,则不使用
src/main/webapp
目录。尽管此目录是一个通俗的标准,但是它仅与 war 包一起工作,并且即使您生成了 jar 包也会通过大多数的构建工具默默无视掉它
Spring Boot 支持 Spring MVC 提供的先进资源处理功能,允许例如缓存清除的静态资源或使用与版本无关的URL Webjars 文件这类情况。
要使用与版本无关的 URL Webjars 文件,请添加 webjars-locator-core
依赖,然后声明您的 Webjar 文件。以 jQuery 为例,添加 "/webjars/jquery/jquery.min.js"
文件会导致 "/webjars/jquery/x.y.z/jquery.min.js"
中的 x.y.z
是 Webjar 的版本
注意
如果您使用 JBoss,需要声明
webjars-locator-jboss-vfs
依赖代替webjars-locator-core
,不然所有的 Webjar 包都会被解析为404
。
要使用缓存清除,以下配置为所有的静态资源配置缓存清除的方案,可以高效地在 URL 中添加内容哈希 ,例如 <link href="/css/spring2a2d595e6ed9a0b24f027f2b63b134d6.css" />
:
Properties
spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/**
Yaml
spring:
web:
resources:
chain:
strategy:
content:
enabled: true
paths: "/**"
注意
运行时在模板中链接重写的资源,都要感谢
ResourceUrlEncodingFilter
自动配置的 Thymeleaf 和 FreeMarker 模板引擎。您应当在使用 JSP 时手动声明此过滤器,目前其它模板引擎不自动支持,但是可以自定义模板 宏/助手以及支持ResourceUrlProvier
的使用
使用 JavaScript 模块加载器动态加载资源时,重命名文件不是一种选择。这就是为什么也支持其他策略且可以组合的原因。 一个 “ 固定的 ” 策略在 URL 中添加静态版本字符串而不更改文件名,如下所示:
Properties
spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/**
spring.web.resources.chain.strategy.fixed.enabled=true
spring.web.resources.chain.strategy.fixed.paths=/js/lib/
spring.web.resources.chain.strategy.fixed.version=v12
Yaml
spring:
web:
resources:
chain:
strategy:
content:
enabled: true
paths: "/**"
fixed:
enabled: true
paths: "/js/lib/"
version: "v12"
有了这个配置,位于 "/js/lib"
下的JavaScript 模块会使用固定的版本策略 ("v12/js/lib/mymodule.js"
),而其他资源仍然使用此内容 (<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css" />
)
有关于更多支持选项,请参阅 WebProperties.Resources 获取
小贴士
Welcome Page
Spring Boot 支持静态和模板化的欢迎页面,它首先在配置的静态内容位置下寻找 index.html
文件,如果找不到,它就会寻找 index
模板。如果找到任意一个,它就会自动将其作为应用程序的欢迎页面
Custom Favicon
类似于其他的静态资源,Spring Boot 在配置的静态内容位置下查找 favicon.ico
图标文件,如果存在这样的文件,它会自动将其作为应用程序的图标。
Path Matching and Content Negotiation
Spring MVC 通过查看请求路径将传入的 HTTP 请求映射到处理器,并且与应用程序中定义的映射进行匹配(例如,控制器方法上的 @GetMapping
注解)
Spring Boot 默认禁用后缀模式匹配,意味着像 "GET /projects/spring-boot.json"
的请求将不会匹配 @GetMapping("/projects/spring-boot")
映射。这被认为是 Spring MVC 应用程序的最佳实践。此特性在主要用于过去未正确发送 " Accept " 请求头的 HTTP 客户端;我们需要确保发送正确的内容类型到客户端。如今,内容协商更可靠。
不过还有其他的方法可以替代后缀匹配,处理始终不能正确接受 “ Accept” 239 请求头的 HTTP 客户端,(例如) 使用查询参数确保像 "GET /projects/spring-boot?format=json"
的请求可以将请求映射到 @GetMapping("/projects/spring-boot")
:
Properties
spring.mvc.contentnegotiation.favor-parameter=true
Yaml
spring:
mvc:
contentnegotiation:
favor-parameter: true
或者,如果喜欢的话,可以自定义各类的参数名:
Properties
spring.mvc.contentnegotiation.favor-parameter=true
spring.mvc.contentnegotiation.parameter-name=myparam
Yaml
spring:
mvc:
contentnegotiation:
favor-parameter: true
parameter-name: "myparam"
虽然大多数标准的媒体类型都支持开箱即用,但是也可以定义新的:
Properties
spring.mvc.contentnegotiation.media-types.markdown=text/markdown
Yaml
spring:
mvc:
contentnegotiation:
media-types:
markdown: "text/markdown"
后缀模式的匹配已经弃用并且在未来的版本中将会被移除,假如您了解警告并且仍然想要让您的应用使用后缀模式进行匹配,那么需要以下配置:
Properties
spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-suffix-pattern=true
Yaml
spring:
mvc:
contentnegotiation:
favor-path-extension: true
pathmatch:
use-suffix-pattern: true
或者与其打开所有后缀模式,不如仅支持注册后缀模式这样更安全些:
Properties
spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-registered-suffix-pattern=true
Yaml
spring:
mvc:
contentnegotiation:
favor-path-extension: true
pathmatch:
use-registered-suffix-pattern: true
作为 Spring Framework 5.3,Spring MVC 支持一些匹配请求路径到控制器处理器的实现策略,原先仅支持 AntPathMatcher
策略,但是现在也提供 PathPatternParser
。现在,Spring Boot 提供配置属性选择以及新策略的挑选:
Properties
spring.mvc.pathmatch.matching-strategy=path-pattern-parser
Yaml
spring:
mvc:
pathmatch:
matching-strategy: "path-pattern-parser"
有关于为什么要考虑到这个新的实现,请参考 dedicated blog post
注意
PathPatternParser 是优化的实现但是限制了某些可变路径模式的用法并且不兼容后缀模式的匹配 (
spring.mvc.pathmatch.use-suffix-pattern
,spring.mvc.pathmatch.use-registeredsuffix-pattern
) 或用 servlet 前缀映射的 DispatcherServlet (spring.mvc.servlet.path
)。
ConfigurableWebBindingInitializer
Spring MVC 使用 WebBindingInitializer
为特定请求初始化 WebDataBinder
,如果您创建自己的 ConfigurableWebBindingInitializer
@Bean
, 那么 Spring Boot 会自动配置 Spring MVC 来使用它。
Template Engines
除了 REST 风格的网络服务,您还能使用 Spring MVC 提供动态 HTML 内容,Spring MVC 支持多变的模板技术,其中包括 Thymeleaf、FreeMarker 和 JSP (Java Server Pages:Java服务器页面,是一种动态网页开发技术,其根本是一个简化的 servlet 设计,它是在传统的网络 HTML 文件中插入 Java 程序代码和 JSP 标记,从而形成 JSP 文件,后缀名为 .jsp)。还有就是许多其他的模板引擎包含它们自己的 Spring MVC 集成。
Spring Boot 包括对下列模板引擎的自动配置支持 :
小贴士
如果可能的话,应当避免使用 JSP,因为当它们与带有嵌入式的 servlet 容器一起使用时会有一些 已知的限制。
当这些模板引擎与默认配置一起使用时,它会从src/main/resources/templates
目录下自动获取
小贴士
根据运行应用程序的方式不同,IDE 可能会使用不同的 classpath 路径。
在 IDE 的 main 方法中运行应用程序会导致顺序的不同比如当您从打包的 jar 包中使用 Maven 或 Gradle 运行应用程序时,这可能会导致 Spring Boot 无法找到期望的模板。如果您有这个问题,首先需要在 IDE 中重新整理您的类路径以便正确放置模块的类和资源。
Error Handling
在默认情况下,Spring Boot 提供一个 /error
映射以明智的方式处理所有的错误,并且在 servlet 容器中注册为一个 “ 全局的 ” 错误页面。对于机器客户端,生成一个 JSON 响应包含错误细节,HTTP 状态和异常消息。对于浏览器客户端,有一个 “白色标签的” 错误视图以 HTML 呈现相同的数据(需要定制的话,请添加一个 view
解析为 error
视图)。
如果您想定制默认的错误处理行为的话,有大量的 server.error
属性能够用于设置,更多细节请查阅附录部分的 “ Server Properties ”
要更换全部默认的行为,请实现 ErrorController
并且注册该类型的 bean 定义或添加 ErrorAttributes
类型的 bean 以使用现有机制然后更换内容。
小贴士
自定义
ErrorController
的基类是BasicErrorController
,如果想要为新的内容类型 (默认专门处理text/html
并且为所有的其他内容提供回调方法 ) 添加处理器,那就少不了它。为此,需要扩展BasicErrorController
类,添加一个带有@RequestMapping
注解和produces
属性的公开方法,并且创建一个新内容类型的 bean。
您也可以定义一个带有 @ControllerAdvice
注解的类将 JSON 文件自定义为返回特殊的控制器或异常类型,如下所示:
Java
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import
org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
@ControllerAdvice(basePackageClasses = SomeController.class)
public class MyControllerAdvice extends ResponseEntityExceptionHandler {
@ResponseBody
@ExceptionHandler(MyException.class)
public ResponseEntity<?> handleControllerException(HttpServletRequest request,
Throwable ex) {
HttpStatus status = getStatus(request);
return new ResponseEntity<>(new MyErrorBody(status.value(), ex.getMessage()),
status);
}
private HttpStatus getStatus(HttpServletRequest request) {
Integer code = (Integer)
request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
HttpStatus status = HttpStatus.resolve(code);
return (status != null) ? status : HttpStatus.INTERNAL_SERVER_ERROR;
}
}
Kotlin
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.ResponseBody
import
org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
import javax.servlet.RequestDispatcher
import javax.servlet.http.HttpServletRequest
@ControllerAdvice(basePackageClasses = [SomeController::class])
class MyControllerAdvice : ResponseEntityExceptionHandler() {
@ResponseBody
@ExceptionHandler(MyException::class)
fun handleControllerException(request: HttpServletRequest, ex: Throwable):
ResponseEntity<*> {
val status = getStatus(request)
return ResponseEntity(MyErrorBody(status.value(), ex.message), status)
}
private fun getStatus(request: HttpServletRequest): HttpStatus {
val code = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE) as Int
val status = HttpStatus.resolve(code)
return status ?: HttpStatus.INTERNAL_SERVER_ERROR
}
}
在上面的示例中,如果在同一包裹中定义的 SomeController
抛出 MyException
异常,会使用 MyErrorBody
的 POJO 的 JSON 形式而不是 ErrorAttributes
。
在某些情况下,控制器层级的错误处理不会被 度量设施
Java
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
@Controller
public class MyController {
@ExceptionHandler(CustomException.class)
String handleCustomException(HttpServletRequest request, CustomException ex) {
request.setAttribute(ErrorAttributes.ERROR_ATTRIBUTE, ex);
return "errorView";
}
}
Kotlin
import org.springframework.boot.web.servlet.error.ErrorAttributes
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.ExceptionHandler
import javax.servlet.http.HttpServletRequest
@Controller
class MyController {
@ExceptionHandler(CustomException::class)
fun handleCustomException(request: HttpServletRequest, ex: CustomException?):
String {
request.setAttribute(ErrorAttributes.ERROR_ATTRIBUTE, ex)
return "errorView"
}
}
Custom Error Pages
如果您想对于指定的状态码展示自定义的 HTML 错误页面,那么您应当添加文件到 /error
目录下,并且错误页面也可以是 静态 HTML 文件(也就是说,能被添加到任何的静态资源目录下)或通过使用模板生成。此外,文件的名称应当是确切的状态码或序列掩码。
例如,要映射 404
为静态 HTML 文件,您的目录结构应当如下所示:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- public/
+- error/
| +- 404.html
+- <other public assets>
要使用 FreeMarker 模板映射所有的 5xx
错误。您的目录结构应当如下所示:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- templates/
+- error/
| +- 5xx.ftlh
+- <other templates>
对于很多复杂的映射,您也可以添加实现 ErrorViewResolver
接口的 bean,如下所示:
Java
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.ModelAndView;
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
if (status == HttpStatus.INSUFFICIENT_STORAGE) {
// We could add custom model values here
new ModelAndView("myview");
}
return null;
}
}
Kotlin
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver
import org.springframework.http.HttpStatus
import org.springframework.web.servlet.ModelAndView
import javax.servlet.http.HttpServletRequest
class MyErrorViewResolver : ErrorViewResolver {
override fun resolveErrorView(request: HttpServletRequest, status: HttpStatus,
model: Map<String, Any>): ModelAndView? {
// Use the request or status to optionally return a ModelAndView
if (status == HttpStatus.INSUFFICIENT_STORAGE) {
// We could add custom model values here
return ModelAndView("myview")
}
return null
}
}
您也可以使用常规的 Spring MVC 功能例如 @ExceptionHandler
方法 以及@ControllerAdvice
注解,然后,ErrorController
类将会获取任何未处理的异常。
Mapping Error Pages outside of Spring MVC
对于未使用 Spring MVC 的应用程序,您可以直接使用 ErrorPageResiter
接口注册 ErrorPages
。此抽象与底层内置的 servlet 容器一起工作即使您没有 Spring MVC DispatchServlet
。
Java
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
@Configuration(proxyBeanMethods = false)
public class MyErrorPagesConfiguration {
@Bean
public ErrorPageRegistrar errorPageRegistrar() {
return this::registerErrorPages;
}
private void registerErrorPages(ErrorPageRegistry registry) {
registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
}
}
Kotlin
import org.springframework.boot.web.server.ErrorPage
import org.springframework.boot.web.server.ErrorPageRegistrar
import org.springframework.boot.web.server.ErrorPageRegistry
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpStatus
@Configuration(proxyBeanMethods = false)
class MyErrorPagesConfiguration {
@Bean
fun errorPageRegistrar(): ErrorPageRegistrar {
return ErrorPageRegistrar { registry: ErrorPageRegistry ->
registerErrorPages(registry) }
}
private fun registerErrorPages(registry: ErrorPageRegistry) {
registry.addErrorPages(ErrorPage(HttpStatus.BAD_REQUEST, "/400"))
}
}
注意
如果
错误页面
与最终通过过滤器
处理的路径一起注册,那么过滤器
必须明确地注册为错误
分派器,如下所示:
Java
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyFilterConfiguration {
@Bean
public FilterRegistrationBean<MyFilter> myFilter() {
FilterRegistrationBean<MyFilter> registration = new
FilterRegistrationBean<>(new MyFilter());
// 省略实际业务中的代码逻辑
registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
return registration;
}
}
Kotlin
import org.springframework.boot.web.servlet.FilterRegistrationBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.util.EnumSet
import javax.servlet.DispatcherType
@Configuration(proxyBeanMethods = false)
class MyFilterConfiguration {
@Bean
fun myFilter(): FilterRegistrationBean<MyFilter> {
val registration = FilterRegistrationBean(MyFilter())
// ...
registration.setDispatcherTypes(EnumSet.allOf(DispatcherType::class.java))
return registration
}
}
注意,默认的 FilterRegisterBean 不包含 错误
分派器类型。
Error handling in a war deployment
当部署到 servlet 容器中时,Spring Boot 使用错误页面过滤器转发带有错误状态的请求到对应的错误页面。这是必要的,因为 servlet 规范确实如此,不提供用于注册错误页面的 API,它只依赖于部署的容器、war 文件和应用程序使用的技术,以及一些也许是必要的额外配置。
如果响应没有预先提交的话,错误页面过滤器只转发请求到正确的错误页面。在默认情况下,WebSphere 应用程序服务器 8.0 (以及更高版本) 会响应被成功调用 servlet 的服务方法,不过您也可以通过将 com.ibm.ws.webcontainer.invokeFlushAfterService
属性的值设置为 false
来禁用此行为。
如果您使用的是 Spring Security 并且希望使用它访问错误页面的主体,那么必须配置一个 Spring Security 的过滤器,以便在发生错误时进行调用。为此,请根据需要设置 spring.security.filter.dispatcher-types
属性的值为 async
、error
、forward
或 request
。
CORS Support
Cross-origin resource sharing(CORS:跨域资源共享)是大部分浏览器实现的 W3C 规范,它允许您以灵活的方式指定授权的跨域资源请求,而不是使用 IFRAME(iframe:HTML 内联框架元素 (<iframe>
) 表示嵌套的浏览内容。它能够将另一个 HTML 页面嵌入到当前页面中。) 或 JSONP(Jsonp 全称 JSON with Padding,这是 JSON 的一种 " 使用模式 ",可以让网页从别的域名获取资料,即跨域读取数据。),因为这既不安全且功能也不完善。
Spring MVC 从 4.2 版本开始支持 CORS,在您的 Spring Boot 应用程序中,Controller 可以直接通过 @CrossOrigion
注解使用 CORS,不需要任何额外的配置。如下所示,通过注册WebMvcConfigurer
类型的 bean 并且重写其 addCorsMappings(CorsRegistry)
方法可以定义全局的 CORS 配置:
Java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration(proxyBeanMethods = false)
public class MyCorsConfiguration {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**");
}
};
}
}
Kotlin
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.CorsRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@Configuration(proxyBeanMethods = false)
class MyCorsConfiguration {
@Bean
fun corsConfigurer(): WebMvcConfigurer {
return object : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/api/**")
}
}
}
}
8.1.2. JAX-RS and Jersey
如果您更喜欢基于 JAX-RS 编程模型的 REST 端点,那么您可以使用 Jersey 和 Apache CXF 替代 Spring MVC,它们和 Spring MVC 一样可以做到开箱即用。例如,我们想使用 CXF,需要在应用程序上下文中将它的 Servlet
或者 Filter
注册为 bean,而如果我们使用的是 Jersey,那就更方便啦,Spring 对它有很好的支持,在 Spring Boot 中提供了对应的启动器和自动配置。
如下示例所示,为了使用 Jersey,首先需要包含一个 spring-boot-starter-jersey
依赖,接着还需要一个 ResourceConfig
类型的 @Bean
注册所有的端点:
Java
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;
@Component
public class MyJerseyConfig extends ResourceConfig {
public MyJerseyConfig() {
register(MyEndpoint.class);
}
}
Kotlin
import org.glassfish.jersey.server.ResourceConfig
import org.springframework.stereotype.Component
@Component
class MyJerseyConfig : ResourceConfig() {
init {
register(MyEndpoint::class.java)
}
}
警告
Jersey 不支持扫描大多数的可执行依赖,例如,当运行可执行的 war 文件时,它无法扫描在完整的可执行 jar 文件 或
WEB-INF/classes
目录上找到的包端点,为了避免这种限制,我们不应该使用packages
方法,并且包端点应该使用register
方法单独注册,就像上述的示例一样。
如果想使用更高级的自定义功能,我们可以注册任意数量的 bean 实现ResourceConfigCustomizer
接口。
所有注册的端点应当带有 @Components
和对应的 HTTP 资源注解(@GET
或者其他的),如下示例所示:
Java
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import org.springframework.stereotype.Component;
@Component
@Path("/hello")
public class MyEndpoint {
@GET
public String message() {
return "Hello";
}
}
Kotlin
import org.springframework.stereotype.Component
import javax.ws.rs.GET
import javax.ws.rs.Path
@Component
@Path("/hello")
class MyEndpoint {
@GET
fun message(): String {
return "Hello"
}
}
自从 Endpoint 成为 Spring 的 @Component 组件以来,它的生命周期就受到了 Spring 的管理。您不但可以使用 @Autowired 注解将其作为依赖进行注入,而且也能通过 @Value 注解将其作为外部配置进行注入。在默认情况下,Jersey 注册的 servlet 会映射到 /*
目录 ,不过您也可以通过添加 @ApplicationPath
注解到您的 ResourceConfig
类(这里指的是 ResourceConfig
类型的 bean`)上更改其默认的映射位置。
在默认情况下,Jersey 被设置为 servlet,而这个 servlet 一般存在于名称为 jerseyServletRegistration
且类型是 ServletRegistrationBean
的 @Bean
中。一般来说,这个 servlet 是懒初始化的,但您也可以通过设置 spring.jersey.servlet.load-on-startup
属性来决定是否懒初始化。此外,您还可以通过创建相同名称的 bean 来启用或覆盖它或者通过设置 spring.jersey.type=filter
(在这种情况下, @Bean 将会更换或覆盖 jerseyFilterRegistration
这个名称)属性使用过滤器代替 servlet,并且这种过滤器含有 @Order 注解,所以我们可以设置 spring.jersey.filter.order
属性的值。当使用 Jersey 作为过滤器的时候,处理任何请求的 servlet 不会被当前的 Jersey 过滤器拦截。
如果应用程序不包括这样的 servlet,那可能是希望通过设置 server.servlet.register-default-servlet
的值为 true 来启用默认的 servlet。此外,注册的 servlet 和过滤器都能通过使用 spring.jersey.init.*
赋予其初始化参数来指定属性的映射。
8.1.3. Embedded Servlet Container Support
对于 servlet 应用程序而言,Spring Boot 包含了对 Tomcat、Jetty 和 Undertow 等嵌入式服务器的良好支持,并且大多数的开发者都会使用适当的 “ 启动器 ” 获取完整的配置实例。在默认情况下,内置服务器监听 8080
端口的 HTTP 请求。
Servlets, Filters, and listeners
(因为)任何 Sevlet
,Filter
或者 servlet *Listener
实例都是与内置容器一起被注册的 Spring bean,(所以)如果您想引用来自配置中的application.properties
的值的话,是非常方便的。
在默认情况下,如果应用上下文仅包含一个独立的 Servlet,则会将其映射到 “/” 路径。而在拥有多个 servlet bean 的情况下,bean 的名称会作为路径的前缀,此外,过滤器会映射到 “/*” 路径。
如果基于约定的映射不够灵活,您可以通过 ServletRegistrationBean
、FilterRegistrationBean
和 ServletListenerRegistrationBean
类实现对应用上下文的完全控制。
将过滤器的 bean 保留为无序的操作通常是安全的,但是如果确实需要一个指定的顺序的话,应当使用 @Order
注释 过滤器 或实现 Ordered
接口,此外还有一点应当注意, bean 方法上不能使用 @Order
注解来配置 过滤器 的顺序。如果您的过滤器类不能添加 @Order
注解或实现Ordered
接口,那么您不但必须为您的过滤器类定义一个 FilterRegistrationBean
类并且使用 setOrder(int)
方法设置注册 bean 的顺序,还应当避免配置读取请求主体且顺序为 Ordered.HIGHEST_PRECEDENCE
的过滤器,因为它可能会与应用程序配置的字符编码相悖。如果 servlet 过滤器包装了请求,则应该为其配置一个小于或等于 OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER
的顺序。
小贴士
如要查看应用程序中每个 过滤器 的顺序,请为 [web 日志记录分组](#7.4.6. Log Groups) (
logging.level.web=debug
) 开启 debug 级别的日志,此级别的日志会包含已注册过滤器的详细细节,比如,它们的顺序和URL配置模式会在应用程序启动时进行记录。
警告
当注册 过滤器 bean 时要注意,因为他们很早就在应用程序的生命周期中初始化了(不要重复初始化)。如果 过滤器 需要与其他的 bean 一同注册 ,可以考虑使用 DelegatingFilterProxyRegistrationBean。
Servlet Context Initialization
嵌入式 servlet 容器不直接执行 servlet 3.0+ javax.servlet.ServletContainerInitializer
接口或 Spring 的 org.springframework.web.WebApplicationInitializer
接口,这是有意为之的,旨在规避第三方库的内部竞争破坏 Spring Boot 应用程序的风险
如果需要在 Spring Boot 应用程序中初始化 servlet 上下文,应当注册一个 实现 org.springframework.boot.web.servlet.ServletContextInitializer
接口的 bean,其单独的 onStartup
方法会提供对 ServletContext
的访问,(甚至)有必要的话,也可以非常简单地作为当前程序中 WebApplicationInitializer
的适配器而存在。
Scanning for Servlets, Filters, and listeners
使用嵌入式容器时,@ServletComponentScan
能够自动注册带有 @WebServlet
、@WebFilter
和 @WebListener
注解的类
小贴士
@ServletComponentScan
在独立的容器中不起作用,会被容器的内置发现机制替代。
The ServletWebServerApplicationContext
在底层,Spring Boot 为嵌入式 servlet 容器提供不同类型的 ApplicationContext
支持。例如:ServletWebServerApplicationContext
就是一种特殊类型的 WebApplicationContext
,其引导程序会寻找独立的 ServletWebServletFactory
类型的 bean,且一般来说(其实现类)TomcatServletWebServletFactory
、JettyServletWebServerFactory
或 UndertowServletWebServerFactory
在应用程序中都已经自动配置了。
注意
一般情况下,我们不需要关心这些实现类,(因为) 大多数的应用程序都是自动配置的,并且对应的 ApplicationContext 和 ServletWebServerFactory 都是托管创建的 (不需要操心)。
在嵌入式容器中, ServletContext 被设置为服务器的启动部分作用在应用程序初始化期间。一种方式是注入 ApplicationContext 作为 bean 的依赖并且仅在需要才使用ServletContext。 另一种方式就是一旦服务器开始启动就使用回调方法,通过使用 ApplicationListener 监听 ApplicationStartedEvent,如下所示:
import javax.servlet.ServletContext;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.web.context.WebApplicationContext;
public class MyDemoBean implements ApplicationListener<ApplicationStartedEvent> {
private ServletContext servletContext;
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();
this.servletContext = ((WebApplicationContext)
applicationContext).getServletContext();
}
}
Customizing Embedded Servlet Containers
通过 Spring 的 Environment
属性可以配置常见的 servlet 容器设置,一般来说,我们会在 application.properties 或 application.yaml 文件中定义属性。
常见的服务器设置包括:
- 网络配置:传入的 HTTP 请求监听端口 (
servlet.port
),接口地址绑定servlet.address
属性等等 - 会话 设置:session 是否持久化 (
server.servlet.session.persistent
),session 超时时间 (server.servlet.session.timeout
),session 数据位置 (server.servlet.session.store-dir
) 和 session-cookie 配置 (server.servlet.session.cookie.*
)。 - 错误管理:错误页面的位置 (
server.error.path
) 等等 - SSL
- HTTP compression
Spring Boot 尝试尽可能多地暴露常见的设置,但世事总不尽人意,(所以) 对于意料之外的情况,(则会通过) 专有命名空间提供特定服务器的自定义设置 (参考 server.tomcat
或 server.undertow
属性)。比如,可以使用嵌入式服务器特定的特性配置 access logs
小贴士
有关于 (服务器的) 完整 (设置) 列表,请参阅
ServerProperties
类
SameSite Cookies
网络浏览器可以使用 SameSite
的 cookie 特性控制跨站点请求是否提交 cookies 以及如何提交,这个特性与现代网络浏览器紧密相关,(所以) 这些浏览器已经更改当属性缺失时使用的默认值。
如果想改变会话中 cookie 的 SameSite
特性,那么可以使用 server.servlet.session.cookie.same-site
属性,自动配置的 Tomcat、Jetty 和 Undertow 支持此属性。此外,也常常用于配置基于 Spring Session servlet 的 SessionRepository
类型的bean。
例如,如果您希望会话 cookies 具有 None
值的 SameSite
属性,那么需要添加下列配置到您的 application.properties
或 application.yaml
文件:
Properties
server.servlet.session.cookie.same-site=none
Yaml
server:
servlet:
session:
cookie:
same-site: "none"
如果您希望改变其他 cookies 的 SameSite
特性添加 HttpServletResponse
,那么可以使用 CookieSameSiteSupplier
。CookieSameSiteSupplier
通过 Cookie
返回一个 SameSite
或 null
值。
有大量便捷的工厂和过滤器方法,我们可以拿来快速上手匹配特定的 cookies。例如,添加的下列 bean 将自动为所有名称与正则表达式 myapp.*
匹配 cookies 应用的 SameSite
站点。
Java
import org.springframework.boot.web.servlet.server.CookieSameSiteSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MySameSiteConfiguration {
@Bean
public CookieSameSiteSupplier applicationCookieSameSiteSupplier() {
return CookieSameSiteSupplier.ofLax().whenHasNameMatching("myapp.*");
}
}
Kotlin
import org.springframework.boot.web.servlet.server.CookieSameSiteSupplier
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration(proxyBeanMethods = false)
class MySameSiteConfiguration {
@Bean
fun applicationCookieSameSiteSupplier(): CookieSameSiteSupplier {
return CookieSameSiteSupplier.ofLax().whenHasNameMatching("myapp.*")
}
}
Programmatic Customization
如果需要有计划性地配置嵌入式的 servlet 容器,那么要注册 bean 实现 WebServletFactoryCustomizer
接口。WebServletFactoryCustomizer
提供了对 ConfigurableServletWebServletFactory
的访问,包含许多的定制 setter 方法。下面的示例展示了详细的端口设置:
Java
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import
org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;
@Component
public class MyWebServerFactoryCustomizer implements
WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
@Override
public void customize(ConfigurableServletWebServerFactory server) {
server.setPort(9000);
}
}
kotlin
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory
import org.springframework.stereotype.Component
@Component
class MyWebServerFactoryCustomizer :
WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
override fun customize(server: ConfigurableServletWebServerFactory) {
server.setPort(9000)
}
}
TomcatServletWebServerFactory
、JettyServletWebServerFactory
和 UndertowServletWebServerFactory
是 ConfigurableServletWebServerFactory
的专有实现,对于 Tomcat、Jetty 和 Undertow 服务器具有特定的 settter 方法,下面的示例就展示了如何自定义提供访问 Tomcat 专用的配置选项的 TomcatServletWebServerFactory
:
Java
import java.time.Duration;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
@Component
public class MyTomcatWebServerFactoryCustomizer implements
WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory server) {
server.addConnectorCustomizers((connector) ->
connector.setAsyncTimeout(Duration.ofSeconds(20).toMillis()));
}
}
Kotlin
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.stereotype.Component
import java.time.Duration
@Component
class MyTomcatWebServerFactoryCustomizer :
WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
override fun customize(server: TomcatServletWebServerFactory) {
server.addConnectorCustomizers({ connector -> connector.asyncTimeout =
Duration.ofSeconds(20).toMillis() })
}
}
Customizing ConfigurableServletWebServerFactory Directly
对于需要扩展 ServletWebServerFactory
类的更高级用例,可以选择性地公开这种类型的 bean。
(此外它) 为许多的配置项提供了 setter 方法,即使您有更多奇怪的需求,一些”钩子“方法也可以满足,查阅 source code documentation 获取更多细节。
注意
自动配置定制器仍然应用于您的自定义工厂,所以请仔细使用该选项。
JSP Limitations
当运行使用嵌入式的 servlet 容器 (并打包为可执行文件) 的 Spring Boot 应用程序时,JSP 的支持有一些限制。
- 如果使用
war
格式打包,那么应用程序应该使用 Jetty 或 Tomcat。可执行的 war 包将在java -jar
命令启动时开始工作,不过也可以将其部署到任何标准的容器中。此外,当使用可执行 jar 包时 JSP 不受支持。 - undertow 容器不支持 JSP。
- 创建一个自定义的
error.jsp
页面不会覆盖 error handling 的默认视图,应该使用 Custom error pages 替换。
8.2. Reactive Web Applications
Spring Boot 提供 Spring WebFlux 的自动配置简化 reactive 网络应用程序的部署。
8.2.1. The “Spring WebFlux Framework”
Spring WebFlux 是在 Spring Framework 5.0 版本中引入的 reactive 网络框架,和 Spring MVC 不同的是,它不需要 servlet API,且是完全异步和非阻塞的,通过 Project Reactor 实现了 Reactive Streams 规范。
Spring WebFlux 有两种风格:功能性的以及基于注解的。基于注解的风格非常接近 Spring MVC 的模型,如下所示:
Java
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/users")
public class MyRestController {
private final UserRepository userRepository;
private final CustomerRepository customerRepository;
public MyRestController(UserRepository userRepository, CustomerRepository
customerRepository) {
this.userRepository = userRepository;
this.customerRepository = customerRepository;
}
@GetMapping("/{userId}")
public Mono<User> getUser(@PathVariable Long userId) {
return this.userRepository.findById(userId);
}
@GetMapping("/{userId}/customers")
public Flux<Customer> getUserCustomers(@PathVariable Long userId) {
return
this.userRepository.findById(userId).flatMapMany(this.customerRepository::findByUser);
}
@DeleteMapping("/{userId}")
public Mono<Void> deleteUser(@PathVariable Long userId) {
return this.userRepository.deleteById(userId);
}
}
Kotlin
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
@RestController
@RequestMapping("/users")
class MyRestController(private val userRepository: UserRepository, private val
customerRepository: CustomerRepository) {
@GetMapping("/{userId}")
fun getUser(@PathVariable userId: Long): Mono<User?> {
return userRepository.findById(userId)
}
@GetMapping("/{userId}/customers")
fun getUserCustomers(@PathVariable userId: Long): Flux<Customer> {
return userRepository.findById(userId).flatMapMany { user: User? ->
customerRepository.findByUser(user)
}
}
@DeleteMapping("/{userId}")
fun deleteUser(@PathVariable userId: Long): Mono<Void> {
return userRepository.deleteById(userId)
}
}
“ WebFlux.fn ” 即功能变体,从实际的请求处理中分离路由配置,如下所示:
Java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import static
org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
@Configuration(proxyBeanMethods = false)
public class MyRoutingConfiguration {
private static final RequestPredicate ACCEPT_JSON =
accept(MediaType.APPLICATION_JSON);
@Bean
public RouterFunction<ServerResponse> monoRouterFunction(MyUserHandler
userHandler) {
return route()
.GET("/{user}", ACCEPT_JSON, userHandler::getUser)
.GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
.DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
.build();
}
}
Kotlin
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.web.reactive.function.server.RequestPredicates.DELETE
import org.springframework.web.reactive.function.server.RequestPredicates.GET
import org.springframework.web.reactive.function.server.RequestPredicates.accept
import org.springframework.web.reactive.function.server.RouterFunction
import org.springframework.web.reactive.function.server.RouterFunctions
import org.springframework.web.reactive.function.server.ServerResponse
@Configuration(proxyBeanMethods = false)
class MyRoutingConfiguration {
@Bean
fun monoRouterFunction(userHandler: MyUserHandler): RouterFunction<ServerResponse>
{
return RouterFunctions.route(
GET("/{user}").and(ACCEPT_JSON), userHandler::getUser).andRoute(
GET("/{user}/customers").and(ACCEPT_JSON),
userHandler::getUserCustomers).andRoute(
DELETE("/{user}").and(ACCEPT_JSON), userHandler::deleteUser)
}
companion object {
private val ACCEPT_JSON = accept(MediaType.APPLICATION_JSON)
}
}
Java
import reactor.core.publisher.Mono;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
@Component
public class MyUserHandler {
public Mono<ServerResponse> getUser(ServerRequest request) {
// ...
}
public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
// ...
}
public Mono<ServerResponse> deleteUser(ServerRequest request) {
// ...
}
}
Kotlin
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono
@Component
class MyUserHandler {
fun getUser(request: ServerRequest?): Mono<ServerResponse> {
return ServerResponse.ok().build()
}
fun getUserCustomers(request: ServerRequest?): Mono<ServerResponse> {
return ServerResponse.ok().build()
}
fun deleteUser(request: ServerRequest?): Mono<ServerResponse> {
return ServerResponse.ok().build()
}
}
WebFlux 是 Spring Framework 的一部分,详细信息可从 reference documentation 中获得。
小贴士
在应用程序中同时添加
spring-boot-starter-web
和spring-boot-starter-webflux
模块会导致 Spring Boot 自动配置 Spring MVC,而不是 WebFlux。之所以选择这种行为,是因为许多 Spring 开发者将spring-boot-starter-webflux
添加到他们的 Spring MVC 应用程序中使用响应式的WebClient
,不过您依旧可以通过将所选的应用程序类型设置为SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE)
来强制执行您的选择。
Spring WebFlux Auto-configuration
Spring Boot 提供了适合大多数应用程序的 Spring WebFlux 自动配置。
该自动配置在 Spring 的默认值上添加了以下功能:
- 配置
HttpMessageReader
和HttpMessageWriter
实例的编码解码器 (稍后详细 - 静态资源服务支持,包含 WebJar 支持 (稍后详细描述)
如果您希望保持 Spring Boot 的 WebFlux 功能以及添加额外的 WebFlux configuration,请添加自己的 WebFluxConfigurer
类型的 @Configuration
配置类且没有 @EnableWebFlux
注解。
如果您希望获取 Spring WebFlux 的完全掌控,请添加自己的 @Configuration
配置类并标注 @EnableWebFlux
注解。
HTTP Codecs with HttpMessageReaders and HttpMessageWriters
Spring WebFlux 使用 HttpMessageReader
和 HttpMessageWriter
接口转换 HTTP 请求和响应,通过编码解码器检查类路径中的可用库配置合理的默认值。
Spring Boot 为编码解码器提供了专用的配置属性 —— spring.codec.*
,还可以使用 CodeCustomizer
的实例完成进一步的定制。例如,spring.jackson.*
配置键应用于 Jackson 编码解码器。
假如需要新增或自定义编码解码器,请创建自定义的 CodeCustomizer
组件,如下所示:
Java
import org.springframework.boot.web.codec.CodecCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.codec.ServerSentEventHttpMessageReader;
@Configuration(proxyBeanMethods = false)
public class MyCodecsConfiguration {
@Bean
public CodecCustomizer myCodecCustomizer() {
return (configurer) -> {
configurer.registerDefaults(false);
configurer.customCodecs().register(new
ServerSentEventHttpMessageReader());
// ...
};
}
}
Kotlin
import org.springframework.boot.web.codec.CodecCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.http.codec.CodecConfigurer
import org.springframework.http.codec.ServerSentEventHttpMessageReader
class MyCodecsConfiguration {
@Bean
fun myCodecCustomizer(): CodecCustomizer {
return CodecCustomizer { configurer: CodecConfigurer ->
configurer.registerDefaults(false)
configurer.customCodecs().register(ServerSentEventHttpMessageReader())
}
}
}
也可以利用 Boot’s custom JSON serializers and deserializers
Static Content
默认情况下,Spring Boot 从类路径上名为 /static
(或 /public
、/resources
、/META-INF/resources
) 读取静态资源文件,它默认使用的是 Spring WebFlux 的 ResourceWebHandler
(处理静态资源),所以您可以通过添加自己的 WebFluxConfigurer
并且重写 addResourceHandler
方法修改其行为。
一般来说,资源映射到 /**
上,但设置spring.webflux.static-path-pattern
属性可以调整。例如,按如下方式可以实现,将所有的资源重新定位到 /resource/**
:
Properties
spring.webflux.static-path-pattern=/resources/**
Yaml
spring:
webflux:
static-path-pattern: "/resources/**"
使用 spring.web.resources.static-locations
属性可以自定义静态资源的位置,(但是) 这样做会替换目录位置列表的默认值。如果真这么做了,默认的欢迎页面会检测并切换到您的自定义位置,因此在启动时假如有一个 index.html
文件在任意位置下,那么它将是应用程序的主页。
除了前面列出的 “标准” 静态资源位置,还有一种特殊情况适用于 Webjars content。在 /webjars
上的任何资源,如果以 Webjars 格式打包,那么都可以从 jar 文件中提供。
小贴士
Spring WebFlux 应用程序不完全依赖于 servlet API,所以他们不能作为 war 文件部署且不能使用
src/main/webapp/
目录
Welcome Page
Spring WebFlux 支持静态的和模板化的欢迎页面,它首先在配置的静态文件位置下寻找 index.html
文件,假如一无所获,那么就查找 index
模板,如果找到一个,自动将其视为应用程序的欢迎页面。
Template Engines
像 REST 风格的 web 服务一样,您也能使用 Spring WebFlux 提供动态的 HTML 内容。Spring WebFlux 支持各式各样的模板技术,包括 Thymeleaf、FreeMarker 和 Mustache。
Spring Boot 包含下列模板引擎的自动配置支持:
当您使用上述这些具有默认配置的模板引擎时,它会自动获取 src/main/resources/templates
下的模板。
Error Handling
Spring Boot 提供一个以一种合理的方式处理所有错误的 WebExceptionHandler
,它的处理顺序位置在 WebFlux 提供的处理器之前,这被认为是最后的防线。对于机器客户端
Chapter 13. Production-ready Features
Spring MVC Metrics
Chapter 14. Deploying Spring Boot Applications
14.2. Installing Spring Boot Applications
Chapter 17. “How-to” Guides
本文回答了在使用Spring Boot时经常出现的例如 “ 我该怎么做 ” 这类的普通问题。它的覆盖范围可能并不广泛,但是确实涵盖了很多内容。
如果您有具体的问题我们没有覆盖到,那么您也许可以检查stackoverflow.com,看看有没有人已经提供了回答。这也是一个询问问题的好地方(问的时候请带上 spring-boot
标签)
如果您想要添加问题的回答到 “ How to ”,只要给我们发送一个拉取代码请求即可,我们非常荣幸地欢迎您扩展这个章节。
17.1. Spring Boot Application
本节包含了与 Spring Boot 应用相关的话题
17.3. Embedded Web Servers
每个 Spring Boot 网络应用都包含了一个内置的网络服务器,但也就是这个特性 “ 十分厉害 ” ,产生了大量的 “ how-to ” 问题,包括类似于如何去改变内置服务器并且配置它这样的问题等等。如果您对此也有疑问的话,可以从本文开始,因为我们在这里回答了这些问题
17.3.1. Use Another Web Server
大部分的 Spring Boot 启动器都包含了默认的内置容器
- 对于 servlet 栈应用,
spring-boot-starter-web
通过spring-boot-starter-tomcat
包含了 Tomcat 网络应用服务器,但我们可以使用spring-boot-starter-jetty
或者spring-boot-starter-undertow
替换掉 - 对于 reactive 栈应用,
spring-boot-starter-webflux
通过spring-boot-starter-reactor-netty
包含了 Reactor Netty 框架,但我们可以使用spring-boot-starter-tomcat
、spring-boot-starter-jetty
或者spring-boot-starter-undertow
替换掉
17.3.7. Configure SSL
17.3.6. Enable HTTP Response Compression
17.3.11. Configure Access Logging
17.7.2. Configure Log4j for Logging
17.16.9. Build an Executable Archive from Ant without Using spring-boot-antlib
Appendices
Appendix A: Common Application Properties
A.11. Server Properties