75. 内嵌的Web服务器
每个Spring Boot web应用包含一个内置的web服务器。这个特性导致许多how-to问题,包括怎么改变内置服务器,怎么配置内置服务器。这一章回答这些问题。
75.1 使用另一个web服务器
许多Spring Boot启动装置(starter)包含默认的内嵌容器。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例子展示了怎么为Spring MVC排除Tomcat并包含Jetty:
- //
- <dependency>
- <groupId>org.springframework.boot
- <artifactId>spring-boot-starter-web
- <exclusions>
- <!-- Exclude the Tomcat dependency -->
- <exclusion>
- <groupId>org.springframework.boot
- <artifactId>spring-boot-starter-tomcat
- </exclusion>
- </exclusions>
- </dependency>
- <!-- Use Jetty instead -->
- <dependency>
- <groupId>org.springframework.boot
- <artifactId>spring-boot-starter-jetty
- </dependency>
Gradle例子:
略~(๑╹◡╹)ノ”””
75.2 禁用web服务器
如果你的classpath包含必需的位(bits)去启动一个web服务器,Spring Boot将会自动启动它。为了禁用这个行为,在application.properties
文件中配置WebApplicationType
,就像下面的例子:
- spring.main.web-application-type=none
75.3 配置Jetty
略~٩(๑>◡<๑)۶
75.4 为一个应用添加Servlet,过滤器(Filter)或监听器
有两种方式为你的应用添加Servlet、过滤器、ServletContextListener
以及其余Servlet配置(spec)支持的监听器。
75.4.1 通过Spring bean添加Servlet,过滤器或监听器
为了通过使用Spring bean添加Servlet,过滤器或者Servlet*Listener
,你必须为它提供一个@Bean
定义。这样做非常有用,当你想注入配置或者依赖的时候。然而,你必须非常小心,they do not cause eager initialization of too many other beans,因为它们必须在应用生命周期中被很早地安装(install)在容器中。(举个例子,让它们依赖你的DataSource
或者JPA配置不是一个好主意)。You can work around such restrictions by initializing the beans lazily when first used instead of on initialization.
至于过滤器和servlet,你也可以添加映射表(mapping),并通过添加FilterRegistrationBean
或者用ServletRegistrationBean
代替或者除了下层的组件初始化参数。(原文:In the case of Filters and Servlets, you can also add mappings and init parameters by adding a FilterRegistrationBean or a ServletRegistrationBean instead of or in addition to the underlying component)
note:If no dispatcherType is specified on a filter registration, REQUEST is used. This aligns with the Servlet Specification’s default dispatcher type.
就像其他的Spring bean一样,你可以定义Servlet filter beans的顺序,请一定查阅XX节
禁用Servlet或者过滤器的注册
如前所述,任何Servlet或者过滤器bean自动地在servlet容易中注册。为了禁用特别的(particular)过滤器或者Servlet bean注册,为它创建一个registration bean,并把它标记为disabled,就像下面例子所展示的:
- @Bean
- public FilterRegistrationBean registration(MyFilter filter) {
- FilterRegistrationBean registration = new FilterRegistrationBean(filter);
- registration.setEnabled(false);
- return registration;
- }
75.4.2 通过classpath扫描添加Servlet,过滤器或者监听器
通过把@ServletComponentScan
注解到@Configuration
类并指定包含你想要注册的组件的包(package),@WebServlet
,
@WebFilter
和
@WebListener
注解的类可以自动被内嵌的servlet容器注册。默认地,@ServletComponentScan
从带注释的类的包中扫描。
75.5 更改HTTP端口
在一个脱机(standalone)的应用,主要的HTTP端口默认为8080,但是可以用@server.port
(比如,在application.properties文件或者作为一个系统属性)。由于Environment
值得轻松绑定,你也可以使用SERVER_PORT(例如,作为一个OS环境变量)。
为了完全地关掉HTTP端点,但仍然创建一个WebApplicationContext
,使用server.port=-1
。(这么做有时对测试很有用。)更多细节请查阅XX。
75.6 使用一个随机未分配的端口
使用server.port=0
去扫描一个空闲端口(使用OS本地端口(natives)以防止冲突)。
75.7 在运行时发现HTTP端口
通过它的WebServer
,你可以从日志输出或者ServletWebServerApplicationContext
访问服务器正在运行的端口。访问端口和确信它已经被初始化的最好方式是添加一个ApplicationListener
类型的@Bean
,当它发布时把容器从事件中拉出来(pull)。
使用@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}”)
的元注解(meta-annotation)。不要尝试在正规应用中注入端口。就像我们刚才看到的,这个值只在容器初始化后才设置。Contrary to a test,应用代码回调被更早地执行。(在这个值实际可用前)
75.8 配置SSL
通过设置不同的server.ssl.*
属性,SSL可以被声明式的配置。通常在application.properties
或者application.yml
。下面的例子展示了在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
查阅XX了解所有支持的SSL属性的详情。
使用配置,就像前面的例子,意味着这个应用不再支持在8080端口的简单(plain)HTTP连接。Spring Boot不支持通过在application.properties
中配置一个HTTP连接和一个HTTPS连接(connector),如果你想有这两种,你需要编程式地配置它们中的一个。我们鼓励使用application.properties
配置HTTPS,因为HTTP连接是两个中较易编程式配置的那个。查询XX例子。
75.9 配置HTTP/2
你可以在你的Spring Boot应用中用server.http2.enabled
配置属性提供HTTP/2支持。这个支持依赖你选择的web服务器和应用环境,因为这个协议不支持JDK8提供的开箱即用。
note:Spring Boot不支持h2c
,HTTP/2协议的cleartext版本,所以你必须先配置SSL。
75.9.1 Undertowz中配置HTTP/2
就Undertow 1.4.0+而言,HTTP/2在JDK8中无任何额外要求的被支持。
75.9.2 在Jetty中配置HTTP/2
(^▽^)略~
75.9.3 Tomcat中配置HTTP/2
Spring Boot ships by default with Tomcat 8.5.x。在这个版本,HTTP/2只在libtcnative
库和它的依赖安装在主机上时才被支持。
如果还没有,库文件夹必须有效对于JVM库路径。你可以用JVM参数这么做,如:
- -Djava.library.path=/usr/local/opt/tomcat-native/lib
更多这方面信息移步Tomcat官方文档。
在没有本地支持下启动Tomcat 8.5.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.
这个错误不是致命的(fatal),应用仍然使用 HTTP/1.1 SSL支持启动。
使用Tomcat 9.0.x和JDK9运行你的应用不要求安装任何本地(native)库。为了使用Tomcat 9,你可以用你选择的版本覆盖tomcat.version
构建(build)属性。
75.10 配置访问日志
通过Tomcat,Undertow,Jetty各自的命名空间可以配置访问日志。
比如,下面使用自定义样式(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
目录是一个临时目录,因此你可能想固定(fix)Tomcat的基础目录或者为日志使用一个绝对路径。在之前的例子,在应用相对于工作目录的my-tomcat/logs
中日志是可获得的。
some info about undertow and Jetty is omt here
75.11 在前端代理服务器后运行
75.12 配置Tomcat
一般地,你可以遵循关于@ConfigurationProperties
(这里ServerProperties
是主要的那个)XX的建议。然而,你也应该看WebServerFactoryCustomizer
和不同的你可以添加的Tomcat规格说明*Customizers
。Tomcat APIs非常丰富。所以,一旦你获取到TomcatServletWebServerFactory
,你可以以多种方式修改它。或者,如果你需要更多的控制和定制,你可以添加自己的TomcatServletWebServerFactory
。
75.13 使用Tomcat提供多种连接
你可以给TomcatServletWebServerFactory
添加一个org.apache.catalina.connector.Connector
,这可以多种连接,包括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);
- }
- }
75.14 使用Tomcat的LegacyCookieProcessor
++
75.15 配置Undertow
略~
75.16 在Undertow中提供多种监听器
略~
75.17 使用@ServerEndpoint创建WebSocket端点
如果你想在一个使用内嵌容器的Spring Boot应用中使用@ServerEndpoint
,你必须声明一个单个的ServerEndpointExporter
@Bean
,如下例子所示:
- @Bean
- public ServerEndpointExporter serverEndpointExporter() {
- return new ServerEndpointExporter();
- }
The bean shown in the preceding example registers any @ServerEndpoint annotated beans with the underlying WebSocket container. When deployed to a standalone servlet container, this role is performed by a servlet container initializer, and the ServerEndpointExporter bean is not required.
75.18 使HTTP回复压缩(compression)
Jetty,Undertow,Tomcat都支持HTTP回复压缩。它可以在application.properties
中被激活(enabled),如下:
- server.compression.enabled=true
默认地,回复至少要2048字节才会被执行压缩,你可以通过设置server.compression.min-response-size
属性来配置这个行为。
默认地,只有回复内容类型是下列之一时才会被压缩:
- text/html
- text/xml
- text/plain
- text/css
你可以使用server.compression.mime-types
属性配置这个行为。