SpringBoot集成CXF框架,实现WebService

SpringBoot官网地址:https://spring.io/projects/spring-ws

1、WebService服务端搭建

Maven依赖

XML 复制代码
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.17</version>
    </parent>

    <dependencies>
        <!-- web启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- webService-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web-services</artifactId>
        </dependency>

        <!-- CXF webservice -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
            <version>3.5.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>3.5.0</version>
        </dependency>
    </dependencies>

yml配置文件

复制代码
server:
  port: 9180
cxf:
  #默认根目录是 /services
  path: /ws

WebService拦截器

java 复制代码
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.saaj.SAAJInInterceptor;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.springframework.stereotype.Component;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Element;

import java.util.List;

@Component
public class WebServiceInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
    private static final String NAME = "YXQZ";
    private static final String PASSWORD = "123";

    public WebServiceInterceptor() {
        super(Phase.PRE_PROTOCOL);
        super.getAfter().add(SAAJInInterceptor.class.getName());
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {
        Header authHeader = null;
        List<Header> headers = message.getHeaders();
        for (Header h : headers) {
            if (h.getName().toString().contains("auth")) {
                authHeader = h;
                break;
            }
        }
        if (authHeader == null) {
            throw new Fault(new IllegalArgumentException("获取不到有效信息"));
        }
        Element auth = (Element) authHeader.getObject();
        NodeList childNodes = auth.getChildNodes();
        String username = null;
        String password = null;
        for (int i = 0, len = childNodes.getLength(); i < len; i++) {
            Node item = childNodes.item(i);
            if (item.getNodeName().contains("user")) {
                username = item.getTextContent();
            }
            if (item.getNodeName().contains("password")) {
                password = item.getTextContent();
            }
        }

        if (!NAME.equals(username) || !PASSWORD.equals(password)) {
            throw new Fault(new IllegalArgumentException("用户账号、密码不正确"));
        }
    }
}

接口请求参数

java 复制代码
public class User {
    private String username;
    private Integer age;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

定义接口和它的实现类

java 复制代码
public interface UserService {

    String queryUserInfo(String username, String password);

    User saveUser(User user);
}
java 复制代码
import org.springframework.stereotype.Service;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;


@WebService(name = "UserWebServiceDemo", serviceName = "UMR",targetNamespace = "http://impl.service.com/")
@Service
public class UserServiceImpl implements UserService {

    @WebMethod
    @WebResult(name = "resultCode")
    public String queryUserInfo(@WebParam(name = "username") String username,
                                @WebParam(name = "password") String password) {
        System.out.println("用户名:" + username + "密码:" + password);
        return "success";
    }

    @WebMethod(operationName = "saveFunction")
    @WebResult
    public User saveUser(@WebParam(name = "accountInfo") User user) {
        System.out.println("保存用户信息");
        return user;
    }
}

CXF配置类

java 复制代码
import com.interceptor.WebServiceInterceptor;
import com.service.impl.UserServiceImpl;
import org.apache.catalina.servlets.CGIServlet;
import org.apache.cxf.Bus;
import org.apache.cxf.jaxws.EndpointImpl;
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;

import javax.xml.ws.Endpoint;

@Configuration
public class CXFConfig {
    //注入 CXF总线 实例
    @Autowired
    private Bus bus;

    //注入业务实例
    @Autowired
    private UserServiceImpl userService;

    //添加拦截器
    @Autowired
    private WebServiceInterceptor webServiceInterceptor;

    //WebService访问的父路径,会展示所有的wsdl
    //默认访问地址 http://localhost:9180/services
    // 由于,我在配置文件重新设置了路径,所有,访问路径会有一点变化
//    @Bean
//    public ServletRegistrationBean getServletRegistrationBean() {
//        return new ServletRegistrationBean(new CGIServlet(), "/services/");
//    }

    @Bean
    public Endpoint messageEndpoint() {
        EndpointImpl endpoint = new EndpointImpl(bus, userService);
        endpoint.publish("/hello");  //发布服务
        endpoint.getInInterceptors().add(webServiceInterceptor);
        return endpoint;
    }
}

2、访问wsdl说明书

springboot启动成功之后,查看发布的所有WSDL文件

默认的WSDL访问地址 http://localhost:9180/services

但是,我们在配置文件添加了自定义路径 http://localhost:9180/ws

如果想访问某一个WSDL文件,就需要在后面添加具体的服务地址,以及wsdl参数。

比如 http://localhost:9180/ws/hello?wsdl

后面,如果真要使用WebService,可以使用动态发布(通过自定义注解,或其他方式)。不然每有一个服务,就要在Config类里面配置一次,比较麻烦

3、WebService客户端搭建

maven依赖 和 接口请求参数 ,跟服务端保持一致

定义客户端接口,不需要定义实现类

java 复制代码
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;

@WebService(name = "UserWebServiceDemo", serviceName = "UMR", targetNamespace = "http://impl.service.com/")
public interface UserClient {
    @WebMethod
    @WebResult(name = "resultCode")
    String queryUserInfo(@WebParam(name = "username") String username,
                         @WebParam(name = "password") String password);

    @WebMethod(operationName = "saveFunction")
    @WebResult
    User saveUser(@WebParam(name = "accountInfo") User user);
}

发起远程调用,访问服务端

java 复制代码
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

public class UserConsumer {

    public static void main(String[] args) {
        //创建cxf代理工厂
        JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
        //设置服务端的地址
        factory.setAddress("http://localhost:9180/ws/hello");
        //设置接口类型
        factory.setServiceClass(UserClient.class);
        //生成代理对象
        UserClient userClient = factory.create(UserClient.class);
        String result = userClient.queryUserInfo("hello", "123");
        System.out.println(result);
    }
}

控制台打印结果如下

可以看见,因为客户端传入的时候,没有在头信息放入用户名和密码,导致在服务端的拦截器报错,说明,服务端的拦截器是生效了。

4、定义客户端的拦截器,在拦截器里设置头信息

java 复制代码
import java.util.List;

import javax.xml.namespace.QName;

import org.apache.cxf.binding.soap.SoapHeader;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class AuthHeaderInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
    private String username;
    private String password;

    public AuthHeaderInterceptor(String username, String password) {
        super(Phase.PREPARE_SEND);
        this.username = username;
        this.password = password;
    }


    public void handleMessage(SoapMessage message) throws Fault {
        // SoapHeader部分待添加的节点
        QName qName = new QName("auth");
        Document doc = DOMUtils.createDocument();

        Element root = doc.createElement("auth");  //认证信息

        Element pwdEl = doc.createElement("password");
        pwdEl.setTextContent(password);
        Element userEl = doc.createElement("username");
        userEl.setTextContent(username);


        root.appendChild(userEl);
        root.appendChild(pwdEl);
        // 新增一个头信息
        SoapHeader header = new SoapHeader(qName, root);

        List<Header> headers = message.getHeaders();

        // 添加SoapHeader内容
        headers.add(header);
    }

}

5、调整客户端的远程调用,新增一个拦截器进去

java 复制代码
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

public class UserConsumer {

    public static void main(String[] args) {
        //创建cxf代理工厂
        JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
        //设置服务端的地址
        factory.setAddress("http://localhost:9180/ws/hello?wsdl");
        //映射的接口
        factory.setServiceClass(UserClient.class);
        //客户端添加到outInterceptors
        factory.getOutInterceptors().add(new AuthHeaderInterceptor("YXQZ", "123"));
        UserClient userClient = factory.create(UserClient.class);
        User user = new User();
        user.setUsername("123");
        user.setAge(18);
        user.setPassword("666");
        User result = userClient.saveUser(user);
        //打印远程调用返回结果
        System.out.println(result);
    }
}

客户端远程调用正常获取

相关推荐
oliveira-time7 分钟前
java单元测试代码
java·windows·单元测试
zfj32113 分钟前
用spring-boot-maven-plugin打包成单个jar有哪些缺点&优化方案
java·maven·jar·springboot
柚个朵朵13 分钟前
RabbitMQ
java·rabbitmq·java-rabbitmq
程序员曼布33 分钟前
ThreadLocal源码深度剖析:内存管理与哈希机制
java·开发语言·哈希算法
程序员小刚35 分钟前
基于SpringBoot + Vue 的火车票订票系统
vue.js·spring boot·后端
hac132239 分钟前
IDEA快速上手Maven项目:模板选择 + 多模块拆分
java·maven·intellij-idea
钢铁男儿1 小时前
C# 方法(局部函数和参数)
java·数据库·c#
遇见你的雩风1 小时前
Java---Object和内部类
java·开发语言
逊嘘1 小时前
【纪念我的365天】我的创作纪念日
java
vibag1 小时前
启发式算法-禁忌搜索算法
java·算法·启发式算法·禁忌搜索