1.简单描述

WebService简单理解就是用http发送接收xml数据,但这个xml得遵守系统的规范。这个规范就是WSDL(Web服务描述语言,Web Services Description Language)。

在WebService中传输的xml有一个正式的名称叫Soap(简单对象访问协议 Simple Object Access Protocol)。

WebService分为客户端和服务端。这两个都可以做数据源提供数据,比如说客户端发送大量数据给服务端,服务端接收大量数据。也可以是客户端发起请求获取服务端提供的大量数据。所有谁生产谁消费这事对Webservice不必纠结。

2.代码实现

SpringBoot项目中还是推荐使用注解的方式实现WebService,这样比较优雅。

1.首先要引入大量的依赖。目前部分jar可能有漏斗,如果项目对安全有较高的要求请引入没有漏洞版本的jar

<!--			webService依赖-->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-core</artifactId>
            <version>${cxf-core.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>${cxf-core.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
            <version>${cxf-core.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>${cxf-core.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web-services</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
            <version>${cxf-core.version}</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-ri</artifactId>
            <version>2.3.5</version>
            <type>pom</type>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.cxf.services/cxf-services -->
        <dependency>
            <groupId>org.apache.cxf.services</groupId>
            <artifactId>cxf-services</artifactId>
            <version>3.4.3</version>
            <type>pom</type>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.cxf/cxf-rt-bindings-soap -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-bindings-soap</artifactId>
            <version>${cxf-core.version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.cxf/cxf-api -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-api</artifactId>
            <version>2.7.18</version>
        </dependency>

接着实现服务器端的代码

服务器端的代码有两部分一部分是WebService接口,一部分是发布WebService的配置类

接口定义(规范WSDL)

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

/**
 * Webservice 发送数据
 * @author dhh
 */
@WebService(serviceName = "WebServiceSender",
        targetNamespace="http://service.modules.jeecg.org/",
        endpointInterface = "org.jeecg.modules.service.WebServiceServer")
public interface WebServiceServer {


    @WebMethod(operationName = "Invoke")
    void invoke(@WebParam(name="from",targetNamespace = "http://service.modules.jeecg.org/")String from,
                              @WebParam(name="token",targetNamespace = "http://service.modules.jeecg.org/")String token,
                              @WebParam(name="funcName",targetNamespace = "http://service.modules.jeecg.org/")String funcName,
                              @WebParam(name="parameters",targetNamespace = "http://service.modules.jeecg.org/")String parameters) throws Exception;
}

实现类(接收到请求后服务器端做处理)

import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.service.WebServiceServer;
import org.springframework.stereotype.Service;

/**
 * @author dhh
 */
@Slf4j
@Service
public class WebServiceSenderServiceImpl implements WebServiceServer {

    @Override
    public void invoke(String from, String token, String funcName, String parameters) throws Exception {
        if("数据来源".equals(from)){
            switch (funcName) {
            }
        }else{
            log.info("未知来源的数据{},禁止写入!",from);
            throw new Exception("未知来源的数据,禁止写入");
        }
    }
}

配置类(发布服务)

import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.jeecg.modules.service.WebServiceServer;
import org.jeecg.modules.service.impl.WebServiceSenderServiceImpl;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.xml.ws.Endpoint;


/**
 * @author dhh
 */
@Configuration
public class WebServiceConfig {

    @Bean(name = "cxfServlet")  // 注入servlet bean name不能dispatchlet ,否则会覆盖dispatcherServlet
    public ServletRegistrationBean<CXFServlet> cxfServlet() {
        return new ServletRegistrationBean<CXFServlet>(new CXFServlet(), "/webservice/*");
    }
    @Bean
    public WebServiceServer webServiceSender() {
        return new WebServiceSenderServiceImpl();
    }

    @Bean(name = Bus.DEFAULT_BUS_ID)
    public SpringBus springBus() {
        return new SpringBus();
    }
    @Bean
    public Endpoint endpoint() {
        // 参数二,是SEI实现类对象
        EndpointImpl endpoint = new EndpointImpl(this.springBus(),this.webServiceSender());
        // 发布服务
        endpoint.publish("/server");
        return endpoint;
    }

}

接下来启动项目访问/webservice/server?wsdl 就可以看看WSDL的内容了。

然后搭建客户端

客户端搭建很简单,就是用http发送一个xml,困难在xml数据的格式要遵守服务端定义的wsdl要求。比如命名空间要和服务端的一致(http://service.modules.jeecg.org/),参数数量和名称也要和服务端定义的一致方可

import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.jeecg.modules.bean.WebServiceRequest;
import org.jeecg.modules.entity.AttachmentInfo;
import org.jeecg.modules.entity.ProjectInfo;
import org.jeecg.modules.entity.ProjectScope;
import org.jeecg.modules.service.WebServiceClientService;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
 * @author dhh
 */
@Slf4j
@Service
public class WebServiceClientServiceImpl implements WebServiceClientService {
    /**
     * 发送请求,发送请求时,将数据封装起来
     */
    @Override
    public String send(WebServiceRequest request, String xmlEntry) {
        try {
            URL url = new URL(request.getRequestUrl());
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
            connection.setDoOutput(true);
            connection.setDoInput(true);
            String xml = this.requestXmlBuilder(request.getEsbCode(), request.getComp(),request.getFrom(),request.getToken(), request.getFuncName(), xmlEntry);
            connection.setRequestProperty("Content-Length",String.valueOf(xml.length()));
            OutputStream out = connection.getOutputStream();
            out.write(xml.getBytes(StandardCharsets.UTF_8));
            int responseCode = connection.getResponseCode();
            if(responseCode==200){
                InputStream in = connection.getInputStream();
                String result = getResult(in);
                log.info(result);
                return result;
            }
        } catch (IOException e) {
            log.error(e.getMessage());
        }
        return "ERROR";
    }

这里我将构建xml单独写了一个方法,可以根据服务端WSDL规范自行定义

private String requestXmlBuilder(String esbCode,String COMP,String from ,String token,String funcName,String xml){
        StringBuffer buffer= new StringBuffer();
        buffer.append("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:boi=\"http://service.modules.jeecg.org/\">");
        buffer.append("<soapenv:Header>");
        buffer.append("<extraParams>");
        buffer.append("<esbCode>");
//       esbCode:数据
        buffer.append(esbCode);
        buffer.append("</esbCode>");

        buffer.append("<COMP>");
//       COMP:数据
        buffer.append(COMP);
        buffer.append("</COMP>");
        buffer.append("</extraParams>");
        buffer.append("</soapenv:Header>");
        buffer.append("<soapenv:Body>");
        buffer.append("<boi:Invoke>");
        buffer.append("<boi:from>");
        buffer.append(from);
        buffer.append("</boi:from>");
        buffer.append("<boi:token>");
        buffer.append(token);
        buffer.append("</boi:token>");
        buffer.append("<boi:funcName>");
//        funcName:数据
        buffer.append(funcName);
        buffer.append("</boi:funcName>");
        buffer.append("<boi:parameters><![CDATA[");
//        XML:数据
        buffer.append(xml);
        buffer.append("]]></boi:parameters>");
        buffer.append("</boi:Invoke>");
        buffer.append("</soapenv:Body>");
        buffer.append("</soapenv:Envelope>");
        return buffer.toString();

    }

后面直接调用客户端方法请求本地服务端就可以测试客户端了,以上结束。

参考连接:webservice关于入参掉用各种报错信息及解决方法汇总org.apache.cxf.interceptor.Fault: Unmarshalling Error: 意外的元素...... - 走看看

实例篇——webservice实现互相传递数据 - 走看看

Apache CXF -- Developing a Service

后面还有拦截器的用法,感兴趣的可以了解一下!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐