注:本系列教程花了三个星期的业余时间来翻译,虽然尽心尽力,但水平有限,难免有失误或表达不太好,请尽力地骂,骂完,可以打赏安慰我,我才有动力改。
源文档地址:https://docs.spring.io/spring-boot/docs/2.2.x/reference/html/howto.html#howto

3.嵌入式Web服务器

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

3.1. 使用另一个Web服务器

许多Springboot程序都包含默认的嵌入式容器。

对于servlet堆栈应用程序,spring-boot-starter-web 通过包含spring-boot-starter-tomcat来包含Tomcat,但是您也可以使用spring-boot-starter-jettyspring-boot-starter-undertow来代替。

对于响应式应用程序,spring-boot-starter-webflux通过包含spring-boot-starter-reactor-netty来引用响应式 netty(netty比较适合),但是您也可以使用spring-boot-starter-tomcat, spring-boot-starter-jetty, 或spring-boot-starter-undertow来代替。

下面的Maven示例演示了如何排除Tomcat并包括SpringMVC的Jetty:

<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>
Servlet API的版本已被重写,与Tomcat9和Undertow2.0不同,Jetty9.4不支持Servlet 4.0。

下面的Gradle示例演示了如何排除netty,并包括SpringWebFlux的Untow:

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'
    // ...
}
spring-boot-starter-reactor-netty 需要使用WebClient类,因此即使需要包含不同的HTTP服务器,也可能需要对Netty保持依赖关系。

3.2. 禁用Web服务器

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

spring.main.web-application-type=none

3.3. 修改HTTP端口

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

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

更多信息, 请查看 “spring-boot-features.html” , 或查看 ServerProperties源码

3.4. 使用随机未分配的HTTP端口

要扫描可用端口(使用操作系统本机防止冲突),请使用server.port=0.

3.5. 在运行时发现HTTP端口

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

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

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class MyWebIntegrationTests {

    @Autowired
    ServletWebServerApplicationContext server;

    @LocalServerPort
    int port;

    // ...

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

3.6. 启用HTTP响应压缩

HTTP响应压缩由jetty、tomcat和undertow支持。它可以在application.properties中启用,如下所示:

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属性来配置此行为。

3.7. 配置 SSL

可以通过设置各种 server.ssl.*属性(通常在application.propertiesapplication.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连接器更容易通过编程方式进行配置。有关示例,请参见Springboot示例Tomcat多连接器示例项目。

3.8. 配置HTTP/2

您可以使用server.http2.enabled配置属性在Springboot应用程序中启用HTTP/2支持。这种支持依赖于所选的Web服务器和应用程序环境,因为JDK8不支持现成的协议。

Springboot不支持h2c,它是HTTP/2协议的明文版本。所以您必须首先 配置SSL。

3.8.1. Undertow HTTP/2 配置

从Undertow的1.4.0+开始,支持HTTP/2,JDK8没有任何附加要求。

3.8.2. Jetty HTTP/2 配置

从Jetty 9.4.8开始,HTTP/2通过Conscrypt library支持。要启用这种支持,您的应用程序需要有两个附加的依赖项:org.eclipse.jetty:jetty-alpn-conscrypt-serveorg.eclipse.jetty.http2:http2-server

3.8.3. Tomcat HTTP/2 配置

默认情况下,Springboot附带了Tomcat9.0.x,在使用JDK9或更高版本时,它支持开箱即用的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会记录以下错误:

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支持开始。

3.8.4. HTTP/2 with Reactor Netty

spring-boot-webflux-starter程序默认使用reactor netty作为服务器。Reactor Netty 可以使用JDK9或更高版本配置HTTP/2。对于JDK8环境,或者为了获得最佳的运行时性能,此服务器还支持带有本机库的HTTP/2。要启用它,您的应用程序需要有一个附加的依赖项。

SpringBoot管理的io.netty:netty-tcnative-boringssl-static "uber jar"版本,包含所有平台的本地库。开发人员可以选择使用分类器只导入所需的依赖项(参见netty官方文档)。

3.9. 配置Web服务器

通常,您应该首先在使用许多可用配置选择一个,并通过在application.properties (或application.ymlenvironment等)中添加新条目来定制Web服务器。请参见“发现外部属性的内置选项”。server.名称空间在这里非常有用,它包括`server.tomcat., server.jetty.*`等名称空间,用于特定于服务器的功能。请参阅[常用应用程序属性]列表。

前面的部分已经介绍了许多常见的用例,例如压缩、SSL或HTTP/2。但是,如果您没有你想需要的配置,那么您应该查看WebServerFactoryCustomizer.。您可以声明这样一个组件并访问与您选择相关的服务器工厂:您应该为所选服务器(Tomcat、Jetty、reactor netty、undrow)和所选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组件,它将覆盖SpringBoot提供的组件。在这种情况下,您不能再依赖 server命名空间中的配置属性。

3.10. 向应用程序添加servlet、过滤器或侦听器

在servlet应用程序中,即使用Spring Boot Starter Web,有两种方法可以将Servlet, Filter, ServletContextListener和servlet API支持的其他侦听器添加到应用程序中:

增加一个Servlet, 过滤器或侦听器采用Spring Bean方式

增加一个Servlet, 过滤器或侦听器采用Classpath 扫描方式

3.10.1.增加一个Servlet, 过滤器或侦听器采用Spring Bean方式

要使用Spring Bean添加servlet、过滤器或servlet *Listener,必须为其提供@Bean 注解。但是,必须非常小心使用,使它不会导致太多其他bean的初始化,因为它们必须一开始就注入容器中,(例如,不要让它们依赖于您的 DataSource或JPA配置。)您可以通过在延迟初始化bean来绕过这些限制。

对于过滤器和servlet,还可以通过添加FilterRegistrationBeanServletRegistrationBean来添加映射和init参数,而不是添加或添加基础组件。

与其他任何SpringBean一样,您可以定义servlet过滤器bean的顺序;请确保检查“spring-boot-features.html”部分。

禁用servlet或过滤器
如前所述,任何servlet或过滤bean都会自动注册到servlet容器中。要禁用特定过滤器或servlet bean的注册,请为其创建一个注册bean并将其标记为禁用,如下例所示:

@Bean
public FilterRegistrationBean registration(MyFilter filter) {
    FilterRegistrationBean registration = new FilterRegistrationBean(filter);
    registration.setEnabled(false);
    return registration;
}

3.10.2. 增加一个Servlet, 过滤器或侦听器采用Classpath 扫描方式

Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter@WebListener注解自动注册,无需其他代码。唯一需要的就是在项目的入口类中加一个注解@ServletComponentScan,顾名思义,它会扫描所有的 @WebServlet@WebFilter@WebListener完成对象的注入。

3.11. 配置访问日志记录

可以通过各自的名称空间为Tomcat、Underow和Jetty配置访问日志。

例如,以下设置使用自定义模式记录对Tomcat的访问。

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中相对于应用程序的工作目录可用。

可以以类似的方式配置undrow的访问日志记录,如下例所示:

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

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

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

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

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

3.12. 使用代理服务器

您的应用程序可能需要发送302个重定向或将带有绝对链接的内容呈现回自身。当在代理之后运行时,调用者需要一个指向代理的链接,而不是指向应用程序的服务器的物理地址。通常,这种情况是通过与代理的协议来处理的,该协议添加请求头来告诉后端如何构造到自身的链接。

如果代理添加了传统的x-forwarded-forx-forwarded-proto头(大多数代理服务器都这样做),那么只要在application.properties中将server.use-forward-headers设置为true,就应该正确呈现绝对链接。

3.12.1. Tomcat代理配置

如果使用Tomcat,还可以配置用于携带“转发”信息的头的名称,如下面的示例所示:

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}
只有在使用properties文件进行配置时,才需要使用双反斜杠。如果使用yaml,则单个反斜杠就足够了,与前面示例中所示值相等的值将是192.168.\d{1,3}.\d{1,3} .
通过将内部代理设置为空,可以信任所有代理(但在生产环境中不这样做)。

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

3.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);
    }
}

3.14. 使用Tomcat的LegacyCookieProcessor

默认情况下,Springboot使用的嵌入式Tomcat不支持cookie格式的“Version0”,因此您可能会看到以下错误:

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

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

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

3.15. Undertow启用多个监听

UnderownBuilderCustomizer添加到UnderownServletWebServerFactory,并将侦听器添加到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;
}

3.16. 使用@ServerEndpoint创建WebSocket Endpoints

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

@Bean
public ServerEndpointExporter serverEndpointExporter() {
    return new ServerEndpointExporter();
}

前面示例中所示的bean用底层的websocket容器注册任何@serverEndpoint注释的bean。当部署到独立的servlet容器时,该角色由servlet容器初始值设定项执行,并且不需要serverendpointexporter bean。