如何在打包和部署为WAR的Springboot应用程序中启用异步支持

Gar*_*hes 5 java war servlet-3.0 spring-boot

当我的SpringBoot应用程序从可执行JAR运行时,下面显示的以下REST端点可以按预期工作。即,它将文本“我的测试响应”返回给客户端。但是,当我打包与WAR相同的应用程序并部署到Tomcat(8.0.29)时,它将引发以下异常:

发生意外错误(类型=内部服务器错误,状态= 500)。必须在Servlet上以及对异步请求处理中涉及的所有过滤器启用异步支持。这是通过Java代码使用Servlet API或通过在Web.xml中的Servlet和过滤器声明中添加“ true”来完成的。

package my.rest.controllers;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

@RestController
@RequestMapping("/api/file")
public class FileContentRestController {

static final int BUFFER = 2048;

@RequestMapping(value = "/content", method = RequestMethod.GET)
@ResponseBody
public StreamingResponseBody getFileContent(HttpServletResponse response) {
    response.setContentType("text/plain");
    response.setCharacterEncoding("UTF-8");

        final InputStream portalFileStream = new ByteArrayInputStream("My test response".getBytes());
        return (OutputStream outputStream) -> {
            int n;
            byte[] buffer = new byte[1024];
            while ((n = portalFileStream.read(buffer)) > -1) {
                outputStream.write(buffer, 0, n);
            }
            portalFileStream.close();
        };

}

}
Run Code Online (Sandbox Code Playgroud)

从这里和其他地方我的理解是,SpringBoot在SpringBoot注册的所有过滤器和servlet上启用了异步支持。从带有嵌入式Tomcat容器的独立JAR运行时,肯定会出现这种情况。

部署为WAR时,如何确保启用了异步支持?

我的SpringBoot应用程序配置如下:

package my;

import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.boot.Banner;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class MyRestApp extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return configureApplication(builder);
    }

    public static void main(String[] args) throws JsonProcessingException {
        configureApplication(new SpringApplicationBuilder()).run(args);
    }

    private static SpringApplicationBuilder configureApplication(SpringApplicationBuilder builder) {
        return builder.sources(MyRestApp.class).bannerMode(Banner.Mode.OFF);
    }

}
Run Code Online (Sandbox Code Playgroud)

如此配置MVC:

package my;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@EnableWebMvc
@EnableAsync
public class MyMvcConfiguration extends WebMvcConfigurerAdapter {

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        configurer.setDefaultTimeout(-1);
        configurer.setTaskExecutor(asyncTaskExecutor());
    }

    @Bean
    public AsyncTaskExecutor asyncTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(500);
        executor.setThreadNamePrefix("SpringAsyncThread-");
        executor.initialize();
        return executor;
    }

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS");
            }
        };
    }

}
Run Code Online (Sandbox Code Playgroud)

最后,使用Maven和以下POM构建和打包应用程序:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.acme</groupId>
    <artifactId>my-rest-app</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>my-rest-app</name>
    <description></description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>       
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>${project.basedir}/src/main/resources</directory>
            </resource>
            <resource>
                <directory>${project.build.directory}/generated-resources</directory>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
Run Code Online (Sandbox Code Playgroud)

sms*_*eck 5

那么你解析的异常说明了一切:

出现意外错误(类型=内部服务器错误,状态=500)。必须在 servlet 和异步请求处理中涉及的所有过滤器上启用异步支持。这是使用 Servlet API 在 Java 代码中完成的,或者通过向 web.xml 中的 servlet 和过滤器声明添加“true”来完成。

因此,您需要在 web.xml 中启用它,或者(并且因为您使用的是 spring-boot 应用程序)您必须配置一个特定的 bean。

也许您 AppConfig 的这个代码片段会有所帮助

@Bean
public ServletRegistrationBean dispatcherServlet() {
    ServletRegistrationBean registration = new ServletRegistrationBean(new DispatcherServlet(), "/");
    registration.setAsyncSupported(true);
    return registration;
}
Run Code Online (Sandbox Code Playgroud)

  • “springboot”应用程序可能没有“web.xml”文件 (2认同)
  • 我的理解是SpringBoot的Auto配置创建了一个默认的调度程序servlet并默认将AsyncSupported设置为true。问题是,为什么应用程序作为 WAR 部署到外部 Tomact 容器时的行为与作为具有嵌入式 Tomcat 容器的可执行 JAR 部署时的行为不同? (2认同)