On this page
73. 嵌入式 Servlet 容器
73.1 将 Servlet,过滤器或侦听器添加到应用程序
有两种方法可将 Servlet 规范支持的Servlet
,Filter
,ServletContextListener
和其他侦听器添加到您的应用程序。您可以为它们提供 Spring Bean,或者启用对 Servlet 组件的扫描。
73.1.1 使用 Spring bean 添加 Servlet,Filter 或 Listener
要添加Servlet
,Filter
或 Servlet *Listener
,请为其提供@Bean
定义。当您要注入配置或依赖项时,这可能非常有用。但是,您必须非常小心,以免引起过多其他 bean 的急切初始化,因为必须在应用程序生命周期的早期就将它们安装在容器中(例如,让它们取决于DataSource
或 JPA 配置)。您可以通过在第一次使用时(而不是在初始化时)延迟初始化它们来解决类似的限制。
在Filters
和Servlets
的情况下,您还可以通过添加FilterRegistrationBean
或ServletRegistrationBean
而不是基础组件或添加基础组件来添加 Map 和 init 参数。
Note
如果在过滤器注册上未指定dispatcherType
,它将匹配FORWARD
,INCLUDE
和REQUEST
。如果已启用异步,它将也匹配ASYNC
。
如果要迁移在web.xml
中没有dispatcher
元素的过滤器,则需要自己指定dispatcherType
:
@Bean
public FilterRegistrationBean myFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
....
return registration;
}
禁用 Servlet 或过滤器的注册
作为described above,任何Servlet
或Filter
bean 都将自动注册到 servlet 容器中。要禁用特定Filter
或Servlet
bean 的注册,请为其创建注册 bean 并将其标记为已禁用。例如:
@Bean
public FilterRegistrationBean registration(MyFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
73.1.2 使用 Classpath 扫描添加 Servlet,过滤器和侦听器
通过使用@ServletComponentScan
Comments@Configuration
类并指定包含要注册的组件的软件包,可以自动将@WebServlet
,@WebFilter
和@WebListener
Comments 的类注册到嵌入式 servlet 容器中。默认情况下,@ServletComponentScan
将从带 Comments 的类的包中进行扫描。
73.2 更改 HTTP 端口
在独立应用程序中,主 HTTP 端口默认为8080
,但可以使用server.port
设置(例如,在application.properties
中或作为系统属性)。得益于Environment
值的轻松绑定,您还可以使用SERVER_PORT
(例如,作为 OS 环境变量)。
要完全关闭 HTTP 端点,但仍创建WebApplicationContext
,请使用server.port=-1
(这有时对测试很有用)。
有关更多详细信息,请参阅“ Spring Boot 功能”部分中的* 第 27.3.4 节“自定义嵌入式 servlet 容器” *或ServerProperties源代码。
73.3 使用随机未分配的 HTTP 端口
要扫描可用端口(使用 os 本机来防止冲突),请使用server.port=0
。
73.4 在运行时发现 HTTP 端口
您可以从日志输出或EmbeddedWebApplicationContext
通过其EmbeddedServletContainer
访问服务器正在运行的端口。最好的方法是确保它已初始化,是添加类型为ApplicationListener<EmbeddedServletContainerInitializedEvent>
的@Bean
,并在事件发布时将其拉出事件。
使用@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
的测试还可以使用@LocalServerPort
注解将实际端口注入字段。例如:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class MyWebIntegrationTests {
@Autowired
EmbeddedWebApplicationContext server;
@LocalServerPort
int port;
// ...
}
Note
@LocalServerPort
是@Value("${local.server.port}")
的元 Comments。不要尝试在常规应用程序中注入端口。正如我们所看到的,只有在容器初始化后才设置该值。与测试相反,应用程序代码回调将在早期(即在该值实际可用之前)进行处理。
73.5 配置 SSL
可以通过设置各种server.ssl.*
属性(通常在application.properties
或application.yml
中)来声明性地配置 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示例项目。
73.6 配置访问日志
可以通过它们各自的名称空间为 Tomcat 和 Undertow 配置访问日志。
例如,以下日志使用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
目录,默认情况下该目录是临时目录,因此您可能需要修复 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
进行自定义。
73.7 在前端代理服务器后面使用
您的应用程序可能需要发送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
。
73.7.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
设置为空来信任所有代理(但在生产环境中不要这样做)。
您可以通过关闭自动功能(即设置server.use-forward-headers=false
)并在TomcatEmbeddedServletContainerFactory
bean 中添加新的 Valve 实例来完全控制 Tomcat RemoteIpValve
的配置。
73.8 配置 Tomcat
通常,您可以遵循* 第 72.8 节“发现外部属性的内置选项” *关于@ConfigurationProperties
的建议(这里主要是ServerProperties
),但也可以查看EmbeddedServletContainerCustomizer
和各种特定于 Tomcat 的*Customizers
,您可以在其中之一中添加。 Tomcat API 非常丰富,因此一旦您可以访问TomcatEmbeddedServletContainerFactory
,就可以通过多种方式对其进行修改。或核选择是添加自己的TomcatEmbeddedServletContainerFactory
。
73.9 使用 Tomcat 启用多个连接器
将org.apache.catalina.connector.Connector
添加到TomcatEmbeddedServletContainerFactory
可以允许多个连接器,例如 HTTP 和 HTTPS 连接器:
@Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
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);
}
}
73.10 使用 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
的EmbeddedServletContainerCustomizer
bean:
@Bean
public EmbeddedServletContainerCustomizer cookieProcessorCustomizer() {
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof TomcatEmbeddedServletContainerFactory) {
((TomcatEmbeddedServletContainerFactory) container)
.addContextCustomizers(new TomcatContextCustomizer() {
@Override
public void customize(Context context) {
context.setCookieProcessor(new LegacyCookieProcessor());
}
});
}
}
};
}
73.11 使用 Jetty 代替 Tomcat
Spring Boot 启动器(尤其是spring-boot-starter-web
)默认情况下将 Tomcat 用作嵌入式容器。您需要排除这些依赖项,而改为包含 Jetty。 Spring Boot 提供了 Binding 在一起的 Tomcat 和 Jetty 依赖关系,作为独立的启动程序,以帮助简化此过程。
Maven 中的示例:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
Gradle 中的示例:
configurations {
compile.exclude module: "spring-boot-starter-tomcat"
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:1.5.9.RELEASE")
compile("org.springframework.boot:spring-boot-starter-jetty:1.5.9.RELEASE")
// ...
}
73.12 配置 Jetty
通常,您可以遵循* 第 72.8 节“发现外部属性的内置选项” *关于@ConfigurationProperties
的建议(这里主要是ServerProperties
),但也可以参考EmbeddedServletContainerCustomizer
。 Jetty API 非常丰富,因此一旦您可以访问JettyEmbeddedServletContainerFactory
,就可以通过多种方式对其进行修改。或核选择是添加自己的JettyEmbeddedServletContainerFactory
。
73.13 使用 Undertow 代替 Tomcat
使用 Undertow 代替 Tomcat 与使用 Jetty 代替 Tomcat非常相似。您需要排除 Tomcat 依赖项,而应包括 UndertowStarter 程序。
Maven 中的示例:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
Gradle 中的示例:
configurations {
compile.exclude module: "spring-boot-starter-tomcat"
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:1.5.9.RELEASE")
compile("org.springframework.boot:spring-boot-starter-undertow:1.5.9.RELEASE")
// ...
}
73.14 配置 Underwow
通常,您可以遵循* 第 72.8 节“发现外部属性的内置选项” *关于@ConfigurationProperties
的建议(这里主要是ServerProperties
和ServerProperties.Undertow
),但也可以查看EmbeddedServletContainerCustomizer
。一旦可以访问UndertowEmbeddedServletContainerFactory
,就可以使用UndertowBuilderCustomizer
修改 Undertow 的配置以满足您的需求。或核选择是添加自己的UndertowEmbeddedServletContainerFactory
。
73.15 使用 Undertow 启用多个侦听器
将UndertowBuilderCustomizer
添加到UndertowEmbeddedServletContainerFactory
并将侦听器添加到Builder
:
@Bean
public UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory() {
UndertowEmbeddedServletContainerFactory factory = new UndertowEmbeddedServletContainerFactory();
factory.addBuilderCustomizers(new UndertowBuilderCustomizer() {
@Override
public void customize(Builder builder) {
builder.addHttpListener(8080, "0.0.0.0");
}
});
return factory;
}
73.16 使用 Tomcat 7.x 或 8.0
Tomcat 7 和 8.0 可与 Spring Boot 一起使用,但默认设置是使用 Tomcat 8.5. 如果您不能使用 Tomcat 8.5(例如,因为使用 Java 1.6),则需要更改 Classpath 以引用其他版本。
73.16.1 将 Tomcat 7.x 或 8.0 与 Maven 结合使用
如果您使用的是 Starters 和 parent,则可以更改 Tomcat 版本属性并另外导入tomcat-juli
。例如。对于简单的 Web 应用程序或服务:
<properties>
<tomcat.version>7.0.59</tomcat.version>
</properties>
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-juli</artifactId>
<version>${tomcat.version}</version>
</dependency>
...
</dependencies>
73.16.2 将 Tomcat 7.x 或 8.0 与 Gradle 一起使用
使用 Gradle,您可以通过设置tomcat.version
属性来更改 Tomcat 版本,然后另外包含tomcat-juli
:
ext['tomcat.version'] = '7.0.59'
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web'
compile group:'org.apache.tomcat', name:'tomcat-juli', version:property('tomcat.version')
}
73.17 使用 Jetty9.2
Jetty 9.2 与 Spring Boot 一起使用,但默认设置是使用 Jetty 9.3. 如果您不能使用 Jetty 9.3(例如,因为您使用的是 Java 7),则需要将 Classpath 更改为引用 Jetty 9.2.
73.17.1 在 Maven 中使用 Jetty 9.2
如果您使用启动器和父级,则只需添加 Jetty 启动器并覆盖jetty.version
属性:
<properties>
<jetty.version>9.2.17.v20160517</jetty.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
</dependencies>
73.17.2 将 Jetty 9.2 与 Gradle 一起使用
您可以设置jetty.version
属性。例如,对于简单的 Web 应用程序或服务:
ext['jetty.version'] = '9.2.17.v20160517'
dependencies {
compile ('org.springframework.boot:spring-boot-starter-web') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
}
compile ('org.springframework.boot:spring-boot-starter-jetty')
}
73.18 使用 Jetty8
Jetty 8 可与 Spring Boot 一起使用,但默认值为使用 Jetty 9.3. 如果您不能使用 Jetty 9.3(例如,因为使用 Java 1.6),则需要将 Classpath 更改为引用 Jetty8.您还需要排除与 Jetty 的 WebSocket 相关的依赖。
73.18.1 将 Jetty 8 与 Maven 结合使用
如果您使用的是 Starters 和 parent,则只需添加具有所需 WebSocket 排除项的 JettyStarters 并更改版本属性,例如对于简单的 Web 应用程序或服务:
<properties>
<jetty.version>8.1.15.v20140411</jetty.version>
<jetty-jsp.version>2.2.0.v201112011158</jetty-jsp.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
73.18.2 将 Jetty 8 与 Gradle 一起使用
您可以设置jetty.version
属性,并排除 WebSocket 依赖项,例如对于简单的 Web 应用程序或服务:
ext['jetty.version'] = '8.1.15.v20140411'
dependencies {
compile ('org.springframework.boot:spring-boot-starter-web') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'
}
compile ('org.springframework.boot:spring-boot-starter-jetty') {
exclude group: 'org.eclipse.jetty.websocket'
}
}
73.19 使用@ServerEndpoint 创建 WebSocket 端点
如果要在使用嵌入式容器的 Spring Boot 应用程序中使用@ServerEndpoint
,则必须声明一个ServerEndpointExporter
@Bean
:
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
该 bean 将在基础 WebSocket 容器中注册任何带有@ServerEndpoint
Comments 的 bean。当部署到独立的 servlet 容器中时,此角色由 servlet 容器初始化程序执行,并且不需要ServerEndpointExporter
bean。
73.20 启用 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
可以使用server.compression.mime-types
属性进行配置。