SpringBoot集成WebService

Spring集成Webservice(JAX·WS)

初步尝试

  1. 引入依赖
kotlin 复制代码
dependencies {
    // 引入Spring Boot Web Starter,提供基础的Web应用支持,包括Tomcat服务器、Spring MVC等组件 implementation("org.springframework.boot:spring-boot-starter-web")
    // 引入Spring Boot Web Services Starter,为构建基于Spring的Web服务提供支持,包括对SOAP、RESTful等协议的集成
    implementation("org.springframework.boot:spring-boot-starter-web-services")

    // 引入Apache CXF Spring Boot Starter for JAX-WS,用于快速配置和启用CXF作为JAX-WS(SOAP Web服务)实现
    // 版本号为4.0.4,确保与项目其他组件兼容并获取最新稳定功能及修复
    implementation("org.apache.cxf:cxf-spring-boot-starter-jaxws:4.0.4")

    // 引入Spring Boot Test Starter,提供测试所需的各类依赖,如JUnit、Mockito、Spring Test等,用于编写和运行应用程序的单元测试和集成测试
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

maven

xml 复制代码
<!-- pom.xml -->

<project>
  <!-- ... -->
  <dependencies>
    <!-- 引入Spring Boot Web Starter -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- 引入Spring Boot Web Services Starter -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web-services</artifactId>
    </dependency>

    <!-- 引入Apache CXF Spring Boot Starter for JAX-WS -->
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
      <version>4.0.4</version>
    </dependency>

    <!-- 引入Spring Boot Test Starter -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <!-- ... -->
</project>
  1. 实现服务类的接口,以及实现
java 复制代码
import jakarta.jws.WebMethod;
import jakarta.jws.WebService;

@WebService(
        name = "MyWebService",
        targetNamespace = "http://hacoj.com/mywebservice"
)
public interface MyWebService {
    @WebMethod
    String sayHello(String name);
}
java 复制代码
import com.hacoj.springwebservice.service.MyWebService;
import jakarta.jws.WebService;
import org.springframework.stereotype.Service;

@Service
@WebService
public class MyWebServiceImpl implements MyWebService {

    @Override
    public String sayHello(String name) {
        System.err.println("sayHello is called..."); // 只是为了更明显的输出,采用err

        return "Hello, " + name + "!";
    }
}
  1. 配置Spring Boot的Web服务
java 复制代码
import jakarta.xml.ws.Endpoint;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * CXF配置类,负责初始化CXF相关组件、发布Webservice服务以及配置CXF Servlet。
 */
@Configuration
public class CxfConfig {

    /**
     * 自动注入Spring Bus实例,它是CXF的核心组件之一,用于管理和配置CXF运行时环境。
     */
    @Autowired
    private SpringBus bus;

    /**
     * 自动注入实现了MyWebService接口的服务实现类实例,该实例将被发布为Webservice供外部调用。
     */
    @Autowired
    private MyWebService myWebServiceImpl;

    /**
     * 创建并返回Webservice端点(Endpoint)实例,用于发布MyWebService服务。
     * 将服务实现类与Spring Bus关联,并指定发布地址为"/1"。
     *
     * @return Webservice端点实例
     */
    @Bean
    public Endpoint endpoint() {

        EndpointImpl endpoint = new EndpointImpl(bus, myWebServiceImpl);
        endpoint.publish("/1"); // 发布地址
        return endpoint;
    }

    /**
     * 创建并返回CXF Servlet的ServletRegistrationBean实例,用于注册CXF Servlet到Spring Boot的Servlet容器中。
     * 设置CXF Servlet的映射路径为"/services/*",表示所有以"/services/"开头的HTTP请求都将由CXF Servlet处理。
     *
     * @return CXF Servlet的ServletRegistrationBean实例
     */
    @Bean
    public ServletRegistrationBean wsServlet() {
        return new ServletRegistrationBean(new CXFServlet(), "/services/*");
    }
}
  1. 启动Spring Boot应用

测试

访问

http://localhost:8080/services/1?wsdl

结果如下

xml 复制代码
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://impl.service.springwebservice.hacoj.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns2="http://schemas.xmlsoap.org/soap/http" xmlns:ns1="http://hacoj.com/mywebservice" name="MyWebServiceImplService" targetNamespace="http://impl.service.springwebservice.hacoj.com/">
    <wsdl:import location="http://localhost:8080/services/1?wsdl=MyWebService.wsdl" namespace="http://hacoj.com/mywebservice"> </wsdl:import>
    <wsdl:binding name="MyWebServiceImplServiceSoapBinding" type="ns1:MyWebService">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="sayHello">
            <soap:operation soapAction="" style="document"/>
            <wsdl:input name="sayHello">
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output name="sayHelloResponse">
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="MyWebServiceImplService">
        <wsdl:port binding="tns:MyWebServiceImplServiceSoapBinding" name="MyWebServiceImplPort">
            <soap:address location="http://localhost:8080/services/1"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

调用

实现一个客户端,调用Webservice服务

客户端的实现代码也比较简单,只需要调用服务端的接口,并处理返回结果即可。

  1. 接口定义类:定义客户端的接口,用于调用服务端的接口。
  2. 客户端启动类:启动客户端,并调用服务端的接口。

依赖

kotlin 复制代码
dependencies {
    // 测试依赖配置
    testImplementation(platform("org.junit:junit-bom:5.9.1")) // JUnit依赖
    testImplementation("org.junit.jupiter:junit-jupiter") // JUnit Jupiter测试框架

    // 主要依赖配置
    implementation("com.sun.xml.bind:jaxb-impl:4.0.5") // JAXB实现库
    implementation("javax.xml.bind:jaxb-api:2.3.1") // JAXB API库
    implementation("jakarta.activation:jakarta.activation-api:2.1.3") // Jakarta Activation API库
    implementation("jakarta.jws:jakarta.jws-api:3.0.0") // Jakarta JWS API库
    implementation("jakarta.xml.ws:jakarta.xml.ws-api:4.0.1") // Jakarta XML Web Services API库
    implementation("jakarta.xml.bind:jakarta.xml.bind-api:4.0.1") // Jakarta XML Binding API库

    // Apache CXF相关依赖
    implementation("org.apache.cxf:cxf-rt-transports-http-jetty:4.0.4") // CXF Jetty HTTP传输实现
    implementation("org.apache.cxf:cxf-rt-frontend-jaxws:4.0.4") // CXF JAX-WS前端支持
    implementation("org.slf4j:slf4j-reload4j:2.1.0-alpha1") // 使用Reload4J作为SLF4J的后端日志实现

}

// 输出字符集为UTF-8
tasks.withType<JavaExec> {
    jvmArgs = listOf(
        "-Dfile.encoding=UTF-8",
        "-Dsun.stdout.encoding=UTF-8",
        "-Dsun.stderr.encoding=UTF-8"
    )
}

// 编译时使用UTF-8字符集
tasks.withType<JavaCompile> {
    options.encoding = "UTF-8"
}

maven形式

xml 复制代码
<!-- pom.xml -->

<project>
  <!-- ... -->
  <dependencies>
    <!-- 测试依赖配置 -->
    <dependency>
      <groupId>org.junit</groupId>
      <artifactId>junit-bom</artifactId>
      <version>5.9.1</version>
      <type>pom</type>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>5.9.1</version>
      <scope>test</scope>
    </dependency>

    <!-- 主要依赖配置 -->
    <dependency>
      <groupId>com.sun.xml.bind</groupId>
      <artifactId>jaxb-impl</artifactId>
      <version>4.0.5</version>
    </dependency>
    <dependency>
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
      <version>2.3.1</version>
    </dependency>
    <dependency>
      <groupId>jakarta.activation</groupId>
      <artifactId>jakarta.activation-api</artifactId>
      <version>2.1.3</version>
    </dependency>
    <dependency>
      <groupId>jakarta.jws</groupId>
      <artifactId>jakarta.jws-api</artifactId>
      <version>3.0.0</version>
    </dependency>
    <dependency>
      <groupId>jakarta.xml.ws</groupId>
      <artifactId>jakarta.xml.ws-api</artifactId>
      <version>4.0.1</version>
    </dependency>
    <dependency>
      <groupId>jakarta.xml.bind</groupId>
      <artifactId>jakarta.xml.bind-api</artifactId>
      <version>4.0.1</version>
    </dependency>

    <!-- Apache CXF相关依赖 -->
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-transports-http-jetty</artifactId>
      <version>4.0.4</version>
    </dependency>
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-frontend-jaxws</artifactId>
      <version>4.0.4</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-reload4j</artifactId>
      <version>2.1.0-alpha1</version>
    </dependency>
  </dependencies>

  <!-- ... -->
</project>
java 复制代码
import jakarta.jws.WebService;

@WebService // Webservice注解表明是一个Webservice的服务类
        (targetNamespace = "http://hacoj.com/mywebservice") // 指定服务的命名空间
public interface HelloService { // 接口名一样

    String sayHello(String name); // 方法定义名一样
}
java 复制代码
/**
 * 客户端调用类,用于通过JAX-WS代理方式访问HelloService Web服务。
 */
public class Client {

    /**
     * 程序主入口方法。
     *
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        // 创建JAX-WS代理工厂对象
        JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();

        // 设置要访问的服务地址
        jaxWsProxyFactoryBean.setAddress("http://localhost:8899/ws/hello");

        // 设置服务接口类,即HelloService
        jaxWsProxyFactoryBean.setServiceClass(HelloService.class);

        // 使用工厂对象创建HelloService接口的代理实例
        HelloService helloService = jaxWsProxyFactoryBean.create(HelloService.class);

        System.out.println(helloService.getClass());

        // 调用代理实例的方法,向服务端发送请求,并打印返回结果
        System.out.println(helloService.sayHello("hacoj"));
    }
}

结果如下

class jdk.proxy2.$Proxy36

Hello, hacoj!

深入了解

返回自定义的数据类

直接定义,在Server,Client定义一样的类即可

WebService注解与WebMethod注解

@WebService 注解支持若干属性,用于定制服务的相关特性。主要属性及其作用:

  1. name
  • 作用:指定Web服务的全局唯一标识(endpoint name)。这个名称通常用于客户端调用时构造服务URL的一部分。
  • 类型:String
  • 默认值:如果不显式设置,会根据类名和服务发布时的配置自动生成。
  1. targetNamespace
  • 作用:定义Web服务的命名空间(namespace),用于在WSDL(Web Services Description Language)文档中标识服务及其元素。
  • 类型:String
  • 默认值:如果没有指定,可能会根据服务发布的上下文自动确定,或者使用默认值(如不带前缀的类名)。
  1. serviceName
  • 作用:指定Web服务在WSDL文档中的服务名。这对于当WSDL由多个端点组成的服务(例如,具有多个接口实现的单个服务)尤其有用,可以明确区分各个端点的服务名。
  • 类型:String
  • 默认值:如果不指定,服务名通常根据类名生成。
  1. portName
  • 作用:指定Web服务端口在WSDL文档中的名称。端口定义了服务的访问点,包括绑定协议和地址信息。
  • 类型:String
  • 默认值:如果不指定,端口名通常根据类名和方法名生成。
  1. endpointInterface
  • 作用:
    endpointInterface 属性接收一个字符串值,该值是服务接口的完全限定类名。这个接口定义了服务对外提供的操作(方法)签名和契约。当您在服务实现类上使用 @WebService 注解并指定了 endpointInterface 时,意味着:
    服务实现类必须实现该接口:服务实现类(如 MyWebService)必须实现您在 endpointInterface 中指定的接口(如 com.hacoj.springwebservice.service.MyWebService)。这意味着服务实现类需要提供接口中所有方法的具体实现。
    接口方法作为Web服务操作:接口中声明的所有公有方法(通常带有 @WebMethod 注解)将作为Web服务对外提供的操作。客户端可以通过这些操作与服务进行交互。接口方法的签名(包括参数类型、返回类型和异常)决定了SOAP消息的结构和交互模式。
  • 类型:Class<?>
  • 默认值:如果不指定,JAX-WS会自动生成一个基于类中公共非静态方法的SEI。
  1. wsdlLocation
  • 作用:提供一个指向WSDL文件的URL或路径。这个属性允许开发者指定一个预先存在的WSDL文件,用于替代JAX-WS自动生成的WSDL。这对于已经存在WSDL设计或者需要严格遵循特定WSDL规范的场景非常有用。
  • 类型:String
  • 默认值:如果不指定,JAX-WS会自动生成WSDL文件。

示例

java 复制代码
@WebService(
        name = "MyWebService",
        targetNamespace = "http://hacoj.com/mywebservice",
        serviceName = "MyWebServiceService",
        portName = "MyWebServicePort",
        endpointInterface = "com.hacoj.springwebservice.service.MyWebService"
)
public interface MyWebService {
    @WebMethod
    String sayHello(@WebParam(name = "id")String name);

    @WebMethod(operationName = "getModel")
    @WebResult(name = "result")
    SimpleModel getSimpleModel(String name);
}

@WebService 注解通常与 @WebMethod、@WebResult、@WebParam 等其他JAX-WS注解一起使用,以精细控制服务操作、参数和结果的映射。

此时如果你还在使用原来的Client,会报错

Caused by: org.apache.cxf.binding.soap.SoapFault: Unmarshalling Error: 意外的元素 (uri:"", local:"arg0")。所需元素为<{}id>

这时修改为一下内容就会正常进行

java 复制代码
@WebService
        (targetNamespace = "http://hacoj.com/mywebservice")
public interface HelloService {

    @WebMethod
    String sayHello(@WebParam(name = "id") String name); // 通过注解指定id

    @WebMethod()
    @WebResult(name = "result") 
    SimpleModel getModel(String name); // 直接改名字,使得它与服务端的@WebMethod(operationName = "getModel")对应
}

SpringBus起什么作用?

SpringBus通常与Spring Web Service结合使用更多

SpringBus 是Apache CXF框架中一个核心组件,用于管理和配置CXF运行时环境。在集成Spring框架的应用中,SpringBus扮演着至关重要的角色,它将CXF的基础设施与Spring的依赖注入(DI)、生命周期管理等功能紧密结合起来,使得CXF服务的配置、发布、管理更加方便且符合Spring编程模型。

SpringBus的主要特点和作用

  • CXF上下文管理: SpringBus充当CXF内部的上下文容器,类似于Spring的ApplicationContext。它存储和管理CXF服务相关的各种对象,如服务端点(Endpoints)、数据绑定器(DataBinders)、拦截器(Interceptors)、消息处理器(MessageHandlers)等。通过SpringBus,这些组件能够共享配置、协作处理请求,并遵循统一的生命周期规则。

  • 与Spring集成: SpringBus无缝集成了Spring框架,允许CXF服务和其他组件以Spring Beans的形式存在。这意味着你可以利用Spring的依赖注入、AOP(面向切面编程)、事务管理、资源管理等特性来配置和管理CXF服务。例如,你可以使用@Autowired注解来注入CXF服务实例,或使用@Bean注解在Spring配置类中定义CXF相关的组件。

  • 配置加载: SpringBus可以加载并解析CXF的配置信息,无论是从Spring的XML配置文件、Java配置类还是从特定的CXF配置文件(如cxf.xml)。这些配置可能包括服务端点的地址、绑定、安全设置、拦截器链等。通过SpringBus,这些配置可以与Spring的其他应用配置一起管理,形成统一的应用配置体系。

  • 服务发布与注册: 在使用CXF发布Web服务时,通常会创建一个Endpoint对象,并将其与SpringBus关联。这样做可以使服务端点获得SpringBus提供的上下文支持。此外,SpringBus还可以帮助管理服务端点的生命周期,如发布、更新、撤销等操作。

相关推荐
虫小宝21 分钟前
如何在Java中实现PDF生成
java·开发语言·pdf
Java4ye21 分钟前
Netty 是如何解析 Redis RESP 协议的——请求篇
后端
菜鸡且互啄691 小时前
在线教育平台,easyexcel使用案例
java·开发语言
八月林城1 小时前
JAVA导出数据库字典到Excel
java·数据库·excel
浅念同学3 小时前
算法-常见数据结构设计
java·数据结构·算法
杰哥在此5 小时前
Java面试题:讨论持续集成/持续部署的重要性,并描述如何在项目中实施CI/CD流程
java·开发语言·python·面试·编程
咖啡煮码6 小时前
深入剖析Tomcat(十五、十六) 关闭钩子,保证Tomcat的正常关闭
java·tomcat
C.C6 小时前
java IO流(1)
java·开发语言
刘铸纬7 小时前
Golang中defer和return顺序
开发语言·后端·golang
黑头!7 小时前
Tomcat注册为服务之后 运行时提示JVM异常
java·jvm·tomcat