On this page
78. 嵌入式 Web 服务器
每个 Spring Boot Web 应用程序都包含一个嵌入式 Web 服务器。此功能引发了许多操作问题,包括如何更改嵌入式服务器以及如何配置嵌入式服务器。本节回答这些问题。
78.1 使用其他 Web 服务器
许多 Spring Boot 启动器都包含默认的嵌入式容器。
对于 Servlet 堆栈应用程序,
spring-boot-starter-web
通过包含spring-boot-starter-tomcat
来包含 Tomcat,但是您可以使用spring-boot-starter-jetty
或spring-boot-starter-undertow
来代替。对于反应堆应用程序,
spring-boot-starter-webflux
通过包含spring-boot-starter-reactor-netty
来包含 Reactor Netty,但是您可以改用spring-boot-starter-tomcat
,spring-boot-starter-jetty
或spring-boot-starter-undertow
。
切换到其他 HTTP 服务器时,除了包括所需的依赖关系之外,还需要排除默认的依赖关系。 Spring Boot 为 HTTP 服务器提供了单独的启动器,以帮助简化此过程。
以下 Maven 示例显示了如何排除 Tomcat 并包括 Jetty for Spring MVC:
<properties>
<servlet-api.version>3.1.0</servlet-api.version>
</properties>
<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>
Note
Servlet API 的版本已被覆盖,因为与 Tomcat 9 和 Undertow 2.0 不同,Jetty 9.4 不支持 Servlet 4.0.
以下 Gradle 示例显示了如何排除 Netty 并为 Spring WebFlux 包括 Undertow:
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'
// ...
}
Note
必须使用spring-boot-starter-reactor-netty
才能使用WebClient
类,因此即使需要包含其他 HTTP 服务器,也可能需要保持对 Netty 的依赖。
78.2 禁用 Web 服务器
如果您的 Classpath 包含启动 Web 服务器所需的位,则 Spring Boot 将自动启动它。要禁用此行为,请在application.properties
中配置WebApplicationType
,如以下示例所示:
spring.main.web-application-type=none
78.3 更改 HTTP 端口
在独立应用程序中,主 HTTP 端口默认为8080
,但可以设置为server.port
(例如,在application.properties
中或作为系统属性)。得益于Environment
值的轻松绑定,您还可以使用SERVER_PORT
(例如,作为 OS 环境变量)。
要完全关闭 HTTP 端点但仍创建WebApplicationContext
,请使用server.port=-1
。 (这样做有时对测试很有用.)
有关更多详细信息,请参阅“ Spring Boot 功能”部分中的“ 第 28.4.4 节“自定义嵌入式 Servlet 容器””或ServerProperties源代码。
78.4 使用随机未分配的 HTTP 端口
要扫描可用端口(使用 os 本机来防止冲突),请使用server.port=0
。
78.5 在运行时发现 HTTP 端口
您可以从日志输出或ServletWebServerApplicationContext
通过其WebServer
访问服务器正在运行的端口。最好的方法是确保它已初始化,是添加类型为ApplicationListener<ServletWebServerInitializedEvent>
的@Bean
,并在事件发布时将其从事件中拉出。
使用@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
的测试还可以通过使用@LocalServerPort
注解将实际端口注入字段中,如以下示例所示:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class MyWebIntegrationTests {
@Autowired
ServletWebServerApplicationContext server;
@LocalServerPort
int port;
// ...
}
Note
@LocalServerPort
是@Value("${local.server.port}")
的元 Comments。不要尝试在常规应用程序中注入端口。如我们所见,仅在初始化容器之后才设置该值。与测试相反,应早处理应用程序代码回调(在值实际可用之前)。
78.6 启用 HTTP 响应压缩
Jetty,Tomcat 和 Undertow 支持 HTTP 响应压缩。可以在application.properties
中启用它,如下所示:
server.compression.enabled=true
默认情况下,响应的长度必须至少为 2048 个字节才能执行压缩。您可以通过设置server.compression.min-response-size
属性来配置此行为。
默认情况下,仅当响应的 Content Type 为以下之一时,它们才被压缩:
text/html
text/xml
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
您可以通过设置server.compression.mime-types
属性来配置此行为。
78.7 配置 SSL
可以通过设置各种server.ssl.*
属性(通常在application.properties
或application.yml
中)来声明性地配置 SSL。以下示例显示了在application.properties
中设置 SSL 属性:
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 连接器。SpringBoot 不支持通过application.properties
配置 HTTP 连接器和 HTTPS 连接器。如果要同时拥有两者,则需要以编程方式配置它们之一。我们建议使用application.properties
来配置 HTTPS,因为 HTTP 连接器在两者中以编程方式进行配置比较容易。有关示例,请参见spring-boot-sample-tomcat-multi-connectors示例项目。
78.8 配置 HTTP/2
您可以使用server.http2.enabled
配置属性在 Spring Boot 应用程序中启用 HTTP/2 支持。这种支持取决于所选的 Web 服务器和应用程序环境,因为 JDK8 不立即支持该协议。
Note
Spring Boot 不支持h2c
,即 HTTP/2 协议的明文版本。因此,您必须首先配置 SSL。
具有 Undertow 的 78.8.1 HTTP/2
从 Undertow 1.4.0 开始,在 JDK8 上无需任何其他要求即可支持 HTTP/2.
78.8.2 使用 Jetty 的 HTTP/2
从 Jetty 9.4.8 开始,Conscrypt library还支持 HTTP/2.要启用该支持,您的应用程序需要具有两个附加依赖项:org.eclipse.jetty:jetty-alpn-conscrypt-server
和org.eclipse.jetty.http2:http2-server
。
带有 Tomcat 的 78.8.3 HTTP/2
默认情况下,Spring Boot 随 Tomcat 9.0.x 一起提供,当使用 JDK 9 或更高版本时,Tomcat 9.0.x 支持 HTTP/2.另外,如果libtcnative
库及其依赖项已安装在主机 os 上,则可以在 JDK 8 上使用 HTTP/2.
如果没有,则必须使库文件夹可用于 JVM 库路径。您可以使用 JVM 参数(例如-Djava.library.path=/usr/local/opt/tomcat-native/lib
)来执行此操作。有关更多信息,请参见Tomcat 官方文档。
在没有该本机支持的情况下,在 JDK 8 上启动 Tomcat 9.0.x 会记录以下错误:
ERROR 8787 --- [ main] o.a.coyote.http11.Http11NioProtocol : The upgrade handler [org.apache.coyote.http2.Http2Protocol] for [h2] only supports upgrade via ALPN but has been configured for the ["https-jsse-nio-8443"] connector that does not support ALPN.
此错误不是致命错误,并且该应用程序仍以 HTTP/1.1 SSL 支持开头。
具有 Reactor Netty 的 78.8.4 HTTP/2
spring-boot-webflux-starter
默认使用 Reactor Netty 作为服务器。使用 JDK 9 或更高版本的 JDK 支持,可以将 Reactor Netty 配置为 HTTP/2.对于 JDK 8 环境或最佳运行时性能,此服务器还支持带有本机库的 HTTP/2.为此,您的应用程序需要具有其他依赖项。
Spring Boot Managementio.netty:netty-tcnative-boringssl-static
“超级 jar”的版本,其中包含所有平台的本机库。开发人员可以选择使用分类器仅导入所需的依赖项(请参见Netty 官方文档)。
78.9 配置 Web 服务器
通常,您应该首先考虑使用许多可用的配置密钥之一,并通过在application.properties
(或application.yml
或环境等,请参见“ 第 77.8 节“发现外部属性的内置选项””)中添加新条目来自定义 Web 服务器。 server.*
命名空间在这里非常有用,它包括server.tomcat.*
,server.jetty.*
等名称空间,用于特定于服务器的功能。请参阅附录 A,通用应用程序属性的列表。
前面的部分已经介绍了许多常见的用例,例如压缩,SSL 或 HTTP/2.但是,如果您的用例不存在配置密钥,则应查看WebServerFactoryCustomizer。您可以声明一个这样的组件并访问与您选择的服务器相关的工厂:您应该为所选服务器(Tomcat,Jetty,Reactor Netty,Undertow)和所选 Web 堆栈(Servlet 或 Reactive)选择变体。
以下示例适用于具有spring-boot-starter-web
(Servlet 堆栈)的 Tomcat:
@Component
public class MyTomcatWebServerCustomizer
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory factory) {
// customize the factory here
}
}
此外,Spring Boot 还提供:
Server | Servlet stack | Reactive stack |
---|---|---|
Tomcat | TomcatServletWebServerFactory |
TomcatReactiveWebServerFactory |
Jetty | JettyServletWebServerFactory |
JettyReactiveWebServerFactory |
Undertow | UndertowServletWebServerFactory |
UndertowReactiveWebServerFactory |
Reactor | N/A | NettyReactiveWebServerFactory |
获得WebServerFactory
的访问权限后,通常可以向其添加定制程序以配置特定的部分,例如连接器,服务器资源或服务器本身-全部使用服务器特定的 API。
作为最后的选择,您还可以声明自己的WebServerFactory
组件,它将覆盖 Spring Boot 提供的组件。在这种情况下,您不能再依赖server
名称空间中的配置属性。
78.10 将 Servlet,过滤器或侦听器添加到应用程序
在 Servlet 堆栈应用程序中,即使用spring-boot-starter-web
,有两种方法可以将Servlet
,Filter
,ServletContextListener
和 Servlet API 支持的其他侦听器添加到您的应用程序中:
78.10.1 使用 Spring Bean 添加 Servlet,过滤器或侦听器
要使用 Spring bean 添加Servlet
,Filter
或 Servlet *Listener
,您必须为其提供@Bean
定义。当您要注入配置或依赖项时,这样做非常有用。但是,您必须非常小心,以免引起过多其他 bean 的急切初始化,因为必须在应用程序生命周期的早期就将它们安装在容器中。 (例如,让它们依赖于您的DataSource
或 JPA 配置不是一个好主意.)您可以通过在首次使用 Bean 时(而不是在初始化时)延迟初始化 Bean 来解决这些限制。
对于Filters
和Servlets
,您还可以通过添加FilterRegistrationBean
或ServletRegistrationBean
来代替基础组件或在基础组件之外添加 Map 和 init 参数。
Note
如果在过滤器注册上未指定dispatcherType
,则使用REQUEST
。这与 Servlet 规范的默认调度程序类型一致。
像其他任何 Spring bean 一样,您可以定义 Servlet 过滤器 bean 的 Sequences。请确保选中“ 名为“将 Servlet,过滤器和侦听器注册为 Spring Bean”的部分”部分。
禁用 Servlet 或过滤器的注册
作为described earlier,任何Servlet
或Filter
bean 都会自动注册到 servlet 容器中。要禁用特定Filter
或Servlet
bean 的注册,请为其创建注册 bean 并将其标记为已禁用,如以下示例所示:
@Bean
public FilterRegistrationBean registration(MyFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
78.10.2 使用 Classpath 扫描添加 Servlet,过滤器和侦听器
通过使用@ServletComponentScan
Comments@Configuration
类并指定包含要注册的组件的包,可以自动将@WebServlet
,@WebFilter
和@WebListener
Comments 的类注册到嵌入式 servlet 容器中。默认情况下,@ServletComponentScan
从带 Comments 的类的包中扫描。
78.11 配置访问日志
可以通过它们各自的名称空间为 Tomcat,Undertow 和 Jetty 配置访问日志。
例如,以下设置使用custom pattern记录对 Tomcat 的访问。
server.tomcat.basedir=my-tomcat
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.pattern=%t %a "%r" %s (%D ms)
Note
日志的默认位置是相对于 Tomcat 基本目录的logs
目录。默认情况下,logs
目录是一个临时目录,因此您可能需要修复 Tomcat 的基本目录或对日志使用绝对路径。在前面的示例中,相对于应用程序的工作目录,日志位于my-tomcat/logs
中。
可以用类似的方式配置 Undertow 的访问日志,如以下示例所示:
server.undertow.accesslog.enabled=true
server.undertow.accesslog.pattern=%t %a "%r" %s (%D ms)
日志存储在相对于应用程序工作目录的logs
目录中。您可以通过设置server.undertow.accesslog.directory
属性来自定义此位置。
最后,Jetty 的访问日志也可以配置如下:
server.jetty.accesslog.enabled=true
server.jetty.accesslog.filename=/var/log/jetty-access.log
默认情况下,日志重定向到System.err
。有关更多详细信息,请参见Jetty 文件。
78.12 在前端代理服务器后面运行
您的应用程序可能需要发送302
重定向或使用绝对链接将内容渲染回自身。当在代理后面运行时,调用者需要链接到代理,而不要链接到托管您的应用的计算机的物理地址。通常,此类情况是通过与代理之间的 Contract 来处理的,该代理添加了 Headers,以告诉后端如何构造与自身的链接。
如果代理添加了常规的X-Forwarded-For
和X-Forwarded-Proto
Headers(大多数代理服务器都这样做),则在application.properties
中将server.use-forward-headers
设置为true
的情况下,应正确渲染绝对链接。
Note
如果您的应用程序在 Cloud Foundry 或 Heroku 中运行,则server.use-forward-headers
属性默认为true
。在所有其他情况下,它默认为false
。
78.12.1 自定义 Tomcat 的代理配置
如果使用 Tomcat,则可以另外配置用于携带“转发的”信息的 Headers 名称,如以下示例所示:
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/16
和127/8
中的 IP 地址是受信任的。您可以通过在application.properties
上添加一个条目来定制阀门的配置,如以下示例所示:
server.tomcat.internal-proxies=192\\.168\\.\\d{1,3}\\.\\d{1,3}
Note
仅当使用属性文件进行配置时,才需要双反斜杠。如果使用 YAML,则单个反斜杠就足够了,并且与上一个示例中所示的值相等的值为192\.168\.\d{1,3}\.\d{1,3}
。
Note
您可以通过将internal-proxies
设置为空来信任所有代理(但在生产环境中不要这样做)。
您可以完全关闭 Tomcat RemoteIpValve
的配置,方法是关闭自动开关(为此,设置server.use-forward-headers=false
)并在TomcatServletWebServerFactory
bean 中添加新的 Valve 实例。
78.13 使用 Tomcat 启用多个连接器
您可以将org.apache.catalina.connector.Connector
添加到TomcatServletWebServerFactory
,这可以允许多个连接器,包括 HTTP 和 HTTPS 连接器,如以下示例所示:
@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);
}
}
78.14 使用 Tomcat 的 LegacyCookieProcessor
默认情况下,Spring Boot 使用的嵌入式 Tomcat 不支持 Cookie 格式的“版本 0”,因此您可能会看到以下错误:
java.lang.IllegalArgumentException: An invalid character [32] was present in the Cookie value
如果有可能,您应该考虑将代码更新为仅存储符合以后 Cookie 规范的值。但是,如果无法更改 cookie 的编写方式,则可以将 Tomcat 配置为使用LegacyCookieProcessor
。要切换到LegacyCookieProcessor
,请使用添加了TomcatContextCustomizer
的WebServerFactoryCustomizer
bean,如以下示例所示:
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {
return (factory) -> factory.addContextCustomizers(
(context) -> context.setCookieProcessor(new LegacyCookieProcessor()));
}
78.15 使用 Undertow 启用多个侦听器
将UndertowBuilderCustomizer
添加到UndertowServletWebServerFactory
并将侦听器添加到Builder
,如以下示例所示:
@Bean
public UndertowServletWebServerFactory servletWebServerFactory() {
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
@Override
public void customize(Builder builder) {
builder.addHttpListener(8080, "0.0.0.0");
}
});
return factory;
}
78.16 使用@ServerEndpoint 创建 WebSocket 端点
如果要在使用嵌入式容器的 Spring Boot 应用程序中使用@ServerEndpoint
,则必须声明一个ServerEndpointExporter
@Bean
,如以下示例所示:
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
前面示例中显示的 Bean 在基础 WebSocket 容器中注册了所有带有@ServerEndpoint
Comments 的 Bean。当部署到独立 servlet 容器时,此角色由 servlet 容器初始化程序执行,并且不需要ServerEndpointExporter
bean。