Wildfly 8.x 上带有 Spring Boot 的 Restful 服务

Posted

技术标签:

【中文标题】Wildfly 8.x 上带有 Spring Boot 的 Restful 服务【英文标题】:Restful services with Spring Boot on Wildfly 8.x 【发布时间】:2016-11-18 09:38:14 【问题描述】:

我在让一个简单的 hello world 应用程序在 Wildfly 8.x 上运行时遇到问题。我收到的堆栈跟踪是获取响应标头的空指针异常:

2016-07-15 04:14:28,488 ERROR [org.springframework.boot.context.web.ErrorPageFilter] (default task-4) Forwarding to error page from request [/hello] due to exception [null]: java.lang.NullPointerException
    at java.util.ArrayList.<init>(ArrayList.java:177) [rt.jar:1.8.0_92]
    at io.undertow.servlet.spec.HttpServletResponseImpl.getHeaders(HttpServletResponseImpl.java:248) [undertow-servlet-1.0.15.Final.jar:1.0.15.Final]
    at javax.servlet.http.HttpServletResponseWrapper.getHeaders(HttpServletResponseWrapper.java:303) [jboss-servlet-api_3.1_spec-1.0.0.Final.jar:1.0.0.Final]
    at org.springframework.http.server.ServletServerHttpResponse$ServletResponseHttpHeaders.get(ServletServerHttpResponse.java:160) [spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.http.server.ServletServerHttpResponse$ServletResponseHttpHeaders.containsKey(ServletServerHttpResponse.java:142) [spring-web-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.addContentDispositionHeader(AbstractMessageConverterMethodProcessor.java:346) [spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:238) [spring-webmvc-4.2.7.RELEASE.jar:4.2.7.RELEASE]

我拼凑了我能想象到的最快最简单的 Spring Boot RESTful 服务:

package com.tryme;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TrymeController 
    @RequestMapping(value="/hello")
    public ResponseEntity<String> hello()
    
        return new ResponseEntity<String>("Hello world.", HttpStatus.OK);
    

应用程序主要是:

package com.tryme;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan
public class TrymeApplication extends SpringBootServletInitializer 

    public static void main(String[] args) 

        SpringApplication.run(TrymeApplication.class, args);

    

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) 
        return application.sources(TrymeApplication.class);
    

我阅读了几篇关于如何更改 pom 以支持 restful spring 服务的博客文章,这是我创建的 POM(我尝试使用 3.1 servlet 和 2.5)。

<?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.tryme</groupId>
    <artifactId>tryme</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>tryme</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.6.RELEASE</version>
    </parent>

    <properties>
        <start-class>com.tryme.TrymeApplication</start-class>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.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-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--
        <dependency>  
             <groupId>javax.servlet</groupId>  
             <artifactId>servlet-api</artifactId>  
             <version>2.5</version>  
             <scope>provided</scope>
        </dependency>        
         -->
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-el</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <!--We are building spring boot application with maven-->
    <build>
        <finalName>tryme</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
    <!--Repositories for spring libs-->
    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>http://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>http://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <!--Repositories for needed plugins -->
    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>http://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>http://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>    
</project>

我尝试在我的 ResponseEntities 上强制使用标头,但没有帮助。谁能告诉我是否有什么可以改变的,让这个简单的应用程序在 8.x Wildfly 上使用 Spring Rest 启动并运行?我验证它在 9.x 和 10.x 上运行良好

编辑:这是我最近的战争的样子。

【问题讨论】:

请注意:您必须提供 servlet-api 依赖项,否则您将把它部署为应用程序的一部分,而您真的不希望这样做。 您是否有应用程序的有效上下文,即按这样的类:@ApplicationPath("rest") public class ConfigApp extends Application public ConfigApp() start-class 错误。应该是:com.tryme.TrymeApplication而不是tryme.TrymeApplication 生成的 war 包含tomcat-embed-el.jar,即使您已将spring-boot-starter-tomcat.jar 标记为提供。您必须明确排除它。 好的,我已经更正了 pom 以反映这些建议,并且得到了相同的结果。对于它的价值,当我在 Spring MVC 中使用休息服务而不是 Spring Boot 时,我得到了相同的结果。如果我在 Wildfly 9.x 或 10.x 上部署相同的应用程序,它就可以工作。但是,我最终会尝试将我的应用程序部署到 JBPM,该应用程序位于 8.2 或 8.1 上,具体取决于版本。 【参考方案1】:

Undertow

Untertow 1.0.x 中,getHeaders(String) 方法检查 null 的响应标头。

// from Undertow 1.0.15-final 
// http://grepcode.com/file/repo1.maven.org/maven2/io.undertow/undertow-servlet/1.0.15.Final/io/undertow/servlet/spec/HttpServletResponseImpl.java#HttpServletResponseImpl.getHeaders%28java.lang.String%29
@Override
public Collection<String> getHeaders(final String name) 
    return new ArrayList<String>(exchange.getResponseHeaders().get(name));

Undertow 1.1.0 向上到 getHeaders(String) 方法检查 null 的 responseHeaders

// from Undertow 1.1.0-final 
// http://grepcode.com/file/repo1.maven.org/maven2/io.undertow/undertow-servlet/1.1.0.Final/io/undertow/servlet/spec/HttpServletResponseImpl.java#HttpServletResponseImpl.getHeader%28java.lang.String%29
@Override
public Collection<String> getHeaders(final String name) 
   HeaderValues headers = exchange.getResponseHeaders().get(name);
    if(headers == null) 
        return Collections.emptySet();
    
    return new ArrayList<>(headers);

很遗憾,我在 JBoss / Undertow 问题工具中没有找到相应的错误报告。

所以克服这个问题的唯一方法是修补/覆盖io.undertow.servlet.spec.HttpServletResponseImpl

用新的更新 modules/system/layers/base/io/undertow/ 中的旧 jar

io.undertow.core io.undertow.servlet

你要修改对应​​目录下的module.xml

不确定

io.undertow.jsp io.undertow.websocket

对于您的示例,更新核心和 servlet 就足够了。 如果可能的话,最好升级到最新的 8.2 版本。

另见JBoss forum上的讨论

【讨论】:

非常感谢!我知道一定有一个错误。请原谅我的无知,但我自 2005 年以来就没有加入过 JBoss。我将如何修补 undertow? :) 谷歌搜索,听起来你只是在你的 pom 中添加依赖项,或者下载更新并将它们放到你的服务器中?这就是我一开始就考虑做的事情,但这似乎有点疯狂。 :) 是的,这就是修补它的方法。 实际上,JBPM 6.4 附带了可与 Spring REST 一起使用的 Wildfly (8.2.1) 版本。但是,如果您将 Spring Tool Suite 添加到配置为下载 (MARS) 的 Eclipse 版本中,则需要从更新站点本身而不是市场下载 Spring Tool Suite:dist.springsource.com/release/TOOLS/update/e4.5 非常感谢这个解决方案。拯救了我的一天。这就是我猜issues.jboss.org/browse/WFLY-3474的问题

以上是关于Wildfly 8.x 上带有 Spring Boot 的 Restful 服务的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 应用程序未部署在 Wildfly 上

Spring eureka 仪表板在 Wildfly 上返回 XML

在 Wildfly 上运行时 Spring Boot 应用程序问题(bean 创建错误)

在 WildFly 8.0.0.Beta 1 上部署 spring 应用程序错误

运行带有嵌入式容器和 JNDI 的 Spring Boot 应用程序

在wildfly上运行war spring roo 2.0.0 RC1