解决springboot整合webservice,使用SOAP Web服务遇到的坑

记录一下springboot整合webservice遇到的问题:

首先,项目需求是,客户那边希望通过webservice来发送数据到本地的服务器,而在本地是希望使用springboot框架,打成的war包在tomcat中启动服务。所以在这里就准备使用springboot整合webservice来完成这一需求。

在网上找了大部分的资料,大多都是关于用webservice的CXF框架,打成war包之后,在运行过程中项目直接启动失败,报了这样一个问题:

java.lang.ClassNotFoundException:com.sun.xml.ws.transport.http.servlet.WSServletContextListener

查了很多原因,发现都是与依赖相关,将相关jar包导入之后仍然报各种问题,所以此方法舍弃。

查看到springboot官网中https://spring.io/guides/gs/consuming-web-service/ 与spring-ws的列子,尝试使用此方法,由于没有跑demo,直接照着上面用了,主要的两个类如下:

首先,schema文件(xsd) service.xsd

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://webservice.server.test.com"
           targetNamespace="http://webservice.server.test.com" elementFormDefault="qualified">

    <xs:element name="getUserInfoRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="type" type="xs:string"/>
                <xs:element name="value" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>    
    <xs:element name="getUserInfoResponse" type="xs:string"/>
</xs:schema>

WebServiceConfig.java

package com.test.server.webservice;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;

/**
 *
 * @author wyj
 */
@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {

    private final static Logger LOGGER = LoggerFactory.getLogger(WebServiceConfig.class);

    @Bean
    public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
        MessageDispatcherServlet servlet = new MessageDispatcherServlet();
        servlet.setApplicationContext(applicationContext);
        servlet.setTransformWsdlLocations(true);
        return new ServletRegistrationBean(servlet, "/ws/*");
    }

    @Bean(name = "test")
    public DefaultWsdl11Definition defaultWsdl11Definition() {
        DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
        wsdl11Definition.setPortTypeName("ServicePort");
        wsdl11Definition.setLocationUri("/ws/test.wsdl");
        wsdl11Definition.setSchema(serviceSchema());
        return wsdl11Definition;
    }

    @Bean
    public XsdSchema serviceSchema() {
        return new SimpleXsdSchema(new ClassPathResource("service.xsd"));
    }

}

ServiceEndpoint.java

package com.test.server.webservice;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;

/**
 *
 * @author wyj
 */
@Endpoint
public class ServiceEndpoint {

    private final static Logger LOGGER = LoggerFactory.getLogger(ServiceEndpoint.class);

    private static final String NAMESPACE_URI = "http://webservice.server.test.com";

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getUserInfo")
    @ResponsePayload
    public String getUserInfo(@RequestPayload GetUserInfoRequest request) {
        LOGGER.info("getUserInfo type:[{}],value [{}]", request.getType(), request.getValue());
        String dataValue = Integration.getInstance().getWebServiceResult(request.getType(), request.getValue());
        LOGGER.info("getUserInfo result:[{}]", dataValue);
        return dataValue ;
    }
}

项目可以正常运行,浏览器打开能够正常访问http://localhost:8080/test/ws/test.wsdl

于是跑了个客户端的java项目,代码如下:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.test;

import com.ankon.his.integration.server.webservice.GetUserInfoRequest;
import com.ankon.his.integration.server.webservice.GetUserInfoResponse;
import com.ankon.his.integration.server.webservice.ServicePort;
import com.ankon.his.integration.server.webservice.ServicePortService;
import java.net.MalformedURLException;
import java.net.URL;

/**
 *
 * @author wyj
 */
public class NewClass {

    public static void main(String args[]) throws MalformedURLException {
        ServicePortService service = new ServicePortService(new URL("http://localhost:81/test/ws/test.wsdl"));
        GetUserInfoRequest request = new GetUserInfoRequest ();
        request.setType("aaa");
        request.setValue("aaa");
        ServicePort servicePort = service.getServicePortSoap11();
        System.err.println(servicePort );
        String response = ankonService.getUserInfo(request);
        System.err.println(response );
    }
}

报如下问题:

JAX-WS RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e: Stub for http://localhost:81/test/ws/test.wsdl
Exception in thread "main" com.sun.xml.internal.ws.client.ClientTransportException: 服务器发送了 HTTP 状态代码 404: null
	at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.checkStatusCode(HttpTransportPipe.java:310)
	at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.createResponsePacket(HttpTransportPipe.java:259)
	at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.process(HttpTransportPipe.java:217)
	at com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.processRequest(HttpTransportPipe.java:130)
	at com.sun.xml.internal.ws.transport.DeferredTransportPipe.processRequest(DeferredTransportPipe.java:95)
	at com.sun.xml.internal.ws.api.pipe.Fiber.__doRun(Fiber.java:1121)

网上找了很多地方,没有找到解决方案,于是与官网上的demo仔细比对,发现ServiceEndpoint.java中localPart 设置为getUserInfo没有与xsd文件中的定义的request对应,导致了这一问题,修改为getUserInfoRequest问题解决。

继续调试,遇到了新的问题:

JAX-WS RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e: Stub for http://localhost:81/test/ws/test.wsdl
Exception in thread "main" com.sun.xml.internal.ws.fault.ServerSOAPFaultException: Client received SOAP Fault from server: No adapter for endpoint [public java.lang.String com.ankon.test.server.webservice.ServiceEndpoint.getUserInfo(com.ankon.test.server.webservice.GetUserInfoRequest)]: Is your endpoint annotated with @Endpoint, or does it implement a supported interface like MessageHandler or PayloadEndpoint? Please see the server log to find more detail regarding exact cause of the failure.
	at com.sun.xml.internal.ws.fault.SOAP11Fault.getProtocolException(SOAP11Fault.java:178)
	at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:116)
	at com.sun.xml.internal.ws.client.sei.StubHandler.readResponse(StubHandler.java:238)
	at com.sun.xml.internal.ws.db.DatabindingImpl.deserializeResponse(DatabindingImpl.java:189)
	at com.sun.xml.internal.ws.db.DatabindingImpl.deserializeResponse(DatabindingImpl.java:276)
	at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:104)
	at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:77)
	at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:147)
	at com.sun.proxy.$Proxy30.getUserInfo(Unknown Source)

再回过头来运行官网的demo并无此问题,多次尝试后,发现与返回值有关:

定义的方法getUserInfo返回值为String,而官网上不管是request还是response都是定义了一个简单对象,所有再次尝试,将String的返回值重新包装成一个对象,修改了xsd文件及ServiceEndpoint,问题解决。

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://webservice.server.test.com"
           targetNamespace="http://webservice.server.test.com" elementFormDefault="qualified">

    <xs:element name="getUserInfoRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="type" type="xs:string"/>
                <xs:element name="value" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>    
    <xs:element name="getUserInfoResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="data" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>    
</xs:schema>
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getUserInfoRequest")
    @ResponsePayload
    public GetUserInfoResponse getUserInfo(@RequestPayload GetUserInfoRequest request) {
        LOGGER.info("getUserInfo type:[{}],value [{}]", request.getType(), request.getValue());
        GetUserInfoResponse data = new GetUserInfoResponse ();
        String dataValue = Integration.getInstance().getWebServiceResult(request.getType(), request.getValue());
        LOGGER.info("getUserInfo result:[{}]", dataValue);
        data.setData(dataValue);
        return data;
    }

 

### 集成 Web Service 到 Spring Boot 项目 为了在 Spring Boot 中集成 Web Service,通常有两种主要方式:JAX-WS 和 JAX-RS。这里重点介绍基于 JAX-WSSOAP Web Services 及其配置方法。 #### 添加 Maven 或 Gradle 依赖项 对于使用 Apache CXF 来创建和发布 WebService 接口的情况,在 `pom.xml` 文件中加入以下依赖: ```xml <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-spring-boot-starter-jaxws</artifactId> <version>${cxf.version}</version> </dependency> ``` 或者如果是采用 Gradle 构建工具,则应在 build.gradle 文件里添加相应语句[^1]。 #### 定义 Web Service 类 定义一个带有 `@WebService` 注解的服务类来实现业务逻辑。例如: ```java import javax.jws.WebMethod; import javax.jws.WebService; @WebService(serviceName = "HelloWorld") public class HelloWorldImpl { @WebMethod(operationName = "sayHi") public String sayHi(String text){ return "Hello "+text; } } ``` 此代码片段展示了如何通过 Java 编写简单的 Web Service 方法[^2]。 #### 发布 Web Service 为了让上述编写的接口能够被外界调用,还需要将其注册到应用上下文中并指定端点地址。这可以通过编写 Bean 初始化器完成: ```java @Configuration public class WebServiceConfig { @Bean public Endpoint endpoint(){ Endpoint.publish("/hello", new HelloWorldImpl()); return null; } } ``` 这段程序负责将之前定义好的服务暴露出去供客户端请求访问[^3]。 #### 测试 Web Service 功能 当一切设置完毕之后,可以启动应用程序并通过浏览器或其他 HTTP 工具测试发布的 Web Service 是否正常工作。一般情况下,默认会监听于 `/ws/hello?wsdl` 路径下获取 WSDL 文档描述信息[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值