嵌入式Web服务器

每个Spring Boot Web应用程序都包含一个嵌入式Web服务器。此功能会导致许多操作方法问题,包括如何更改嵌入式服务器以及如何配置嵌入式服务器。本节回答了这些问题。

使用其他Web服务器

许多Spring Boot启动器都包含默认的嵌入式容器。

  • 对于servlet堆栈应用程序,spring-boot-starter-web包括Tomcat spring-boot-starter-tomcat,但您可以使用spring-boot-starter-jettyspring-boot-starter-undertow替代。
  • 对于反应栈的应用,spring-boot-starter-webflux包括反应堆的Netty通过包括spring-boot-starter-reactor-netty,但你可以使用spring-boot-starter-tomcatspring-boot-starter-jettyspring-boot-starter-undertow代替。

切换到其他HTTP服务器时,除了包含所需的依赖项外,还需要排除默认依赖项。Spring Boot为HTTP服务器提供单独的启动程序,以帮助使此过程尽可能简单。

以下Maven示例显示如何排除Tomcat并为Spring MVC包含Jetty:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<properties>
<servlet-api.version>3.1.0</servlet-api.version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 排除Tomcat依赖项 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 使用Jetty -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

Servlet API的版本已被覆盖,因为与Tomcat 9和Undertow 2.0不同,Jetty 9.4不支持Servlet 4.0。

以下Gradle示例显示如何排除Netty并包含Spring WebFlux的Undertow:

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

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

spring-boot-starter-reactor-netty需要使用WebClient该类,因此即使您需要包含不同的HTTP服务器,也可能需要依赖Netty。

禁用Web服务器

如果您的类路径包含启动Web服务器所需的位,Spring Boot将自动启动它。要禁用此行为,请WebApplicationType在您的配置中进行配置application.properties,如以下示例所示:

1
spring.main.web-application-type=none

更改HTTP端口

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

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

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

使用随机未分配的HTTP端口

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

在运行时发现HTTP端口

您可以从日志输出或ServletWebServerApplicationContext通过其输出访问服务器正在运行的端口 WebServer。获得它并确保它已被初始化的最佳方法是添加一个@Bean类型, ApplicationListener<ServletWebServerInitializedEvent>并在发布时将容器拉出事件。

使用的测试@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)还可以使用@LocalServerPort注释将实际端口注入字段,如以下示例所示:

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

@Autowired
ServletWebServerApplicationContext server;

@LocalServerPort
int port;
// ...
}

@LocalServerPort是一个元注释@Value("${local.server.port}")。不要尝试在常规应用程序中注入端口。正如我们刚刚看到的那样,只有在容器初始化之后才设置该值。与测试相反,应用程序代码回调会尽早处理(在值实际可用之前)。

启用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
  • text/javascript
  • application/javascript
  • application/json
  • application/xml

您可以通过设置server.compression.mime-types属性来配置此行为。

配置SSL

可以通过设置各种server.ssl.*属性以声明方式配置SSL ,通常在application.properties或中application.yml。以下示例显示了在以下位置设置SSL属性application.properties

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不支持HTTP连接器和HTTPS连接器的配置application.properties。如果要同时使用两者,则需要以编程方式配置其中一个。我们建议使用 application.properties配置HTTPS,因为HTTP连接器更容易以编程方式配置。有关spring-boot-sample-tomcat-multi-connectors 示例,请参阅 示例项目。

配置HTTP / 2

您可以使用server.http2.enabled配置属性在Spring Boot应用程序中启用HTTP / 2支持 。此支持取决于所选的Web服务器和应用程序环境,因为JDK8不支持该协议。

Spring Boot不支持h2cHTTP / 2协议的明文版本。因此,您必须先配置SSL

Undertow的HTTP / 2

从Undertow 1.4.0+开始,支持HTTP / 2,对JDK8没有任何额外要求。

Jetty的HTTP / 2

从Jetty 9.4.8开始,Conscrypt库也支持HTTP / 2 。要启用该支持,您的应用程序需要有两个额外的依赖项:org.eclipse.jetty:jetty-alpn-conscrypt-serverorg.eclipse.jetty.http2:http2-server

Tomcat的HTTP / 2

Spring Boot默认使用Tomcat 9.0.x,它在使用JDK 9或更高版本时支持HTTP / 2开箱即用。或者,如果libtcnative 库及其依赖项安装在主机操作系统上,则可以在JDK 8上使用HTTP / 2 。

必须使库文件夹(如果尚未可用)到JVM库路径。您可以使用JVM参数来完成此操作 -Djava.library.path=/usr/local/opt/tomcat-native/lib。有关Tomcat官方文档的更多 信息

在没有该本机支持的情况下在JDK 8上启动Tomcat 9.0.x会记录以下错误:

1
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 beenconfigured for the ["https-jsse-nio-8443"] connector that does not support ALPN.

此错误不是致命错误,应用程序仍以HTTP / 1.1 SSL支持启动。

Reactor Netty的HTTP / 2

spring-boot-webflux-starter默认情况下,反应堆的Netty作为服务器使用。可以使用JDK 9或更高版本的JDK支持为Reactor Netty配置HTTP / 2。对于JDK 8环境或最佳运行时性能,此服务器还支持具有本机库的HTTP / 2。要启用它,您的应用程序需要具有其他依赖项。

Spring Boot管理io.netty:netty-tcnative-boringssl-static“超级jar” 的版本 ,包含所有平台的本机库。开发人员可以选择使用分类器仅导入所需的依赖项(请参阅Netty官方文档)。

配置Web服务器

通常,您应首先考虑使用众多可用配置键中的一个,并通过在您的application.propertiesapplication.yml或环境等)中添加新条目来自定义您的Web服务器,请参阅“ 第77.8节”,“发现外部属性的内置选项” “) 。该server.* 命名空间是非常有用的在这里,它包括命名空间一样server.tomcat.*server.jetty.*和其他人,了解特定服务器的功能。请参阅附录A,常见应用程序属性列表。

前面的部分介绍了许多常见用例,例如压缩,SSL或HTTP / 2。但是,如果您的用例不存在配置密钥,则应该查看 WebServerFactoryCustomizer。您可以声明这样的组件并获得与您选择的服务器工厂相关的访问权限:您应该为所选服务器(Tomcat,Jetty,Reactor Netty,Undertow)和所选Web堆栈(Servlet或Reactive)选择变体。

以下示例适用于具有spring-boot-starter-web(Servlet堆栈)的Tomcat :

1
2
3
4
5
6
7
8
9
@Component
public class MyTomcatWebServerCustomizer
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

@Override
public void customize(TomcatServletWebServerFactory factory) {
// customize the factory here
}
}

另外Spring Boot提供:

服务器 Servlet Stack Reactive Stack
Tomcat的 TomcatServletWebServerFactory TomcatReactiveWebServerFactory
码头 JettyServletWebServerFactory JettyReactiveWebServerFactory
暗潮 UndertowServletWebServerFactory UndertowReactiveWebServerFactory
反应堆 N / A NettyReactiveWebServerFactory

一旦您有权访问a WebServerFactory,您通常可以向其添加定制器以配置特定部件,例如连接器,服务器资源或服务器本身 - 所有这些都使用特定于服务器的API。

作为最后的手段,您还可以声明自己的WebServerFactory组件,它将覆盖Spring Boot提供的组件。在这种情况下,您不能再依赖server命名空间中的配置属性。

向应用程序添加Servlet,Filter或Listener

在servlet堆栈应用程序中,即使用spring-boot-starter-web,有两种方法可以向应用程序添加ServletFilterServletContextListener,以及Servlet API支持的其他侦听器:

使用Spring Bean添加

要添加ServletFilter或servlet *Listener使用的Spring bean,你必须提供一个@Bean它的定义。当您想要注入配置或依赖项时,这样做非常有用。但是,您必须非常小心,它们不会导致太多其他bean的初始化,因为它们必须在应用程序生命周期的早期安装在容器中。(例如,让它们依赖于您的DataSource或JPA配置并不是一个好主意。)您可以通过在首次使用而不是初始化时懒惰地初始化bean来解决此类限制。

在的情况下FiltersServlets,还可以通过添加添加映射和初始化参数FilterRegistrationBeanServletRegistrationBean代替或除了下面的部件。

如果dispatcherType在过滤器注册中指定了no ,REQUEST则使用。这与Servlet规范的默认调度程序类型一致。

像任何其他Spring bean一样,您可以定义Servlet过滤器bean的顺序; 请务必检查“ 名为”将Servlet,过滤器和监听器注册为Spring Beans “部分。

禁用Servlet或过滤器的注册

正如前面所述,任何 ServletFilter豆与servlet容器自动注册。要禁用特定FilterServletbean的注册,请为其创建注册Bean并将其标记为已禁用,如以下示例所示:

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

使用类路径扫描添加

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

配置访问日志记录

以通过各自的命名空间为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)

日志存储在logs相对于应用程序工作目录的目录中。您可以通过设置server.undertow.accesslog.directory属性来自定义此位置 。

最后,Jetty的访问日志记录也可以配置如下:

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

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

在前端代理服务器后面运行

您的应用程序可能需要发送302重定向或使用绝对链接呈现内容。在代理后面运行时,调用者需要指向代理的链接,而不是托管应用程序的计算机的物理地址。通常,这种情况是通过与代理的合同来处理的,代理会添加标题以告诉后端如何构建自己的链接。

如果代理添加了常规X-Forwarded-ForX-Forwarded-Proto头文件(大多数代理服务器都这样做),则应该正确呈现绝对链接,前提server.use-forward-headerstrue在您的中设置application.properties

如果您的应用程序在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还配置了一个默认的正则表达式,该表达式匹配要信任的内部代理。默认情况下,IP地址中10/8192.168/16169.254/16127/8是值得信赖的。您可以通过添加条目来自定义阀门的配置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为空来信任所有代理(但在生产中不这样做)。

您可以RemoteIpValve通过关闭自动关闭(为此,设置server.use-forward-headers=false)并在TomcatServletWebServerFactorybean中添加新的阀门实例来完全控制Tomcat的配置。

使用Tomcat启用多个连接器

可以添加org.apache.catalina.connector.ConnectorTomcatServletWebServerFactory,这可以允许多个连接器,包括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,请使用WebServerFactoryCustomizer添加a 的 bean TomcatContextCustomizer,如以下示例所示:

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

使用Undertow启用多个侦听器

一个添加UndertowBuilderCustomizerUndertowServletWebServerFactory与收听添加到Builder,如图所示在下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
@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;
}

使用@ServerEndpoint创建WebSocket端点

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

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

前面示例中显示的@ServerEndpointbean使用基础WebSocket容器注册任何带注释的bean。当部署到独立的servlet容器时,此角色由servlet容器初始化程序执行,并且ServerEndpointExporter不需要该 bean。