WebService的CXF实现

WebService的CXF实现

Web Service技术, 能使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件, 就可相互交换数据或集成 。一种跨编程语言和跨操作系统平台的远程调用技术。

XML、SOAP和WSDL就是构成Web Service平台的三大技术。

  • Web Service采用Http协议来在客户端和服务端之间传输数据。WebService使用XML来封装数据,XML主要的优点在于它是跨平台的。
  • Web Service通过HTTP协议发送请求和接收结果时,发送的请求内容和结果内容都采用XML格式封装,并增加了一些特定的HTTP消息头,以说明HTTP消息的内容格式,这些特定的HTTP消息头和XML内容格式就是SOAP协议规定的。
  • Web Service服务器端首先要通过一个WSDL文件来说明自己有什么服务可以对外调用。简单的说,WSDL就像是一个说明书,用于描述Web Service及其方法、参数和返回值。 WSDL文件保存在Web服务器上,通过一个URL地址就可以访问到它。客户端要调用一个Web Service服务之前,要知道该服务的WSDL文件的地址。Web Service服务提供商可以通过两种方式来暴露它的WSDL文件地址:1.注册到UDDI服务器,以便被人查找;2.直接告诉给客户端调用者。

SOAPMessage的结构:

SOAP消息创建的几个步骤:

			MessageFactory mFactory = MessageFactory.newInstance();
			//2、根据消息工厂创建SOAP消息
			SOAPMessage message = mFactory.createMessage();
			//3、创建soappart
			SOAPPart part = message.getSOAPPart();
			//4、获得SOAP信封SOAPEnvelope
			SOAPEnvelope envelope = part.getEnvelope();
			//5、可以通过SoapEnvelope有效的获取相应的body和header
			SOAPBody body = envelope.getBody();
			//6、根据QName创建相应的节点(QName就是一个带有命名空间的节点)
			//<ns:add xmlns="http://service.soap.org/">
			QName qname = new QName("http://service.soap.org/", "add", "ns");
			//可通过以下方式给消息体赋值
			SOAPBodyElement ele = body.addBodyElement(qname);
			ele.addChildElement("a").setValue("3");
			ele.addChildElement("b").setValue("5");
			//打印消息信息
			message.writeTo(System.out);

@WebService

  1. serviceName:对外发布的服务名,指定Web Servcice的服务名称:wsdl:service,缺省默认为 Java类的简单名称+Service。
  2. endpointlnterface:服务接口全路径,指定做SEI( Service EndPoint Interface )服务端点接口。
  3. name:此属性的值包含XML Web Service的名称。默认情况下,该值是XML Web Service类的名称。
  4. portName:wsdl:portName,默认为 WebService.name+Port 。
  5. targetNamespace:指定你想要的名称空间,认是使用接口实现类的包名的反缀,**结尾需要加上 / **
  6. wsdlLocation:指定用于定义 Web Service 的 WSDL 文档的 Web 地址。Web 地址可以是相对路径或绝对路径。

@WebMethod

注释表示作为一项 Web Service 操作的方法,将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类。 仅支持在使用 @WebService 注释来注释的类上使用 @WebMethod 注释

  1. operationName:指定与此方法相匹配的wsdl:operation的名称。默认为java方法的名称。
  2. action:定义此操作的行为,对于SOAP绑定,此值将确定SOAPAction头的值。默认为java方法的名称。
  3. exclude:指定是否从 Web Service中排除某一方法。默认为false。

@OneWay

将一个方法表示为只有输入消息而没有输出消息的webService单向操作,将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于javaBeans端点的服务器端点实现类。

@WebParam

用于定制从单个参数至web Service消息部件和XML元素的映射。

将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类 。

  1. name:参数名称,如果操作是远程调用RPC类型并且未指定partName属性,那么这些是用于表示参数的wsdl:part属性的名称。

    如果操作是文档类型或者参数映射至某个头,那么 -name 是用于表示该参数的 XML 元素的局部名称。如果操作是文档类型、

    参数类型为 BARE 并且方式为 OUT 或 INOUT,那么必须指定此属性。(字符串)

  2. partName:定义用于表示此参数的 wsdl:part属性的名称。仅当操作类型为 RPC 或者操作是文档类型并且参数类型为BARE 时才使用此参数。(字符串)

  3. targetNamespace:指定参数的 XML 元素的 XML 名称空间。当属性映射至 XML 元素时,仅应用于文档绑定。缺省值为 Web Service 的 targetNamespace。(字符串)

  4. mode:此值表示此方法的参数流的方向。有效值为 IN、INOUT 和 OUT。(字符串)

  5. header:指定参数是在消息头还是消息体中。缺省值为 false。(布尔值)

@WebResult

注释用于定制从返回值至 WSDL 部件或 XML 元素的映射。将此注释应用于客户机或服务器服务端点接口(SEI)上的方法,或者应用于 JavaBeans 端点的服务器端点实现类。

  1. name:当返回值列示在 WSDL 文件中并且在连接上的消息中找到该返回值时,指定该返回值的名称。对于 RPC 绑定,这是用于表示返回值的 wsdl:part属性的名称。对于文档绑定,-name参数是用于表示返回值的 XML 元素的局部名。对于 RPC 和 DOCUMENT/WRAPPED 绑定,缺省值为 return。对于 DOCUMENT/BARE 绑定,缺省值为方法名 + Response。(字符串)

  2. targetNamespace:指定返回值的 XML 名称空间。仅当操作类型为 RPC 或者操作是文档类型并且参数类型为 BARE 时才使用此参数。(字符串)

  3. header:指定头中是否附带结果。缺省值为false。(布尔值)

  4. partName:指定 RPC 或 DOCUMENT/BARE 操作的结果的部件名称。缺省值为@WebResult.name。(字符串)

@HandlerChain

注释用于使 Web Service 与外部定义的处理程序链相关联。只能通过对 SEI 或实现类使用 @HandlerChain 注释来配置服务器端的处理程序。

但是可以使用多种方法来配置客户端的处理程序。可以通过对生成的服务类或者 SEI 使用 @HandlerChain 注释来配置客户端的处理程序。此外,可以按程序在服务上注册您自己的 HandlerResolver 接口实现,或者按程序在绑定对象上设置处理程序链。

  1. file:指定处理程序链文件所在的位置。文件位置可以是采用外部格式的绝对 java.net.URL,也可以是类文件中的相对路径。(字符串)

  2. name:指定配置文件中处理程序链的名称。

AbstractPhaseInterceptor拦截器

CXF拦截器是功能的主要实现单元,也是主要的扩展点,可以在不对核心模块进行修改的情况下,动态添加功能。当服务被调用时,会经过多个拦截器链(Interceptor Chain)处理,拦截器链在服务输入(IN)或输出(OUT)阶段实现附加功能,拦截器可以在客户端加入,也可以在服务端加入。 

拦截器有多个阶段,每个阶段都有多个拦截器。拦截器在哪个阶段起作用,可以在拦截器的 构造函数 中声明。

基本实现

pom依赖(注意版本)

	<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
    		<version>2.0.1.RELEASE</version>
   	</dependency>
<!-- 集成cxf -->
    <dependency>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
        <version>3.2.4</version>
    </dependency>
    <!-- 服务端:用来生成JSONArray -->
    <dependency>
        <groupId>net.sf.json-lib</groupId>
        <artifactId>json-lib</artifactId>
        <classifier>jdk15</classifier>
        <version>2.4</version>
    </dependency>
    <!-- 客户端:用来解析JSONArray -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.6.2</version>
    </dependency>

application.properties配置

webservices.username=admin
webservices.password=root
webservices.service.endpoint=/user
webservices.service.prefix=/mySoap

service方法接口类

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

@WebService
public interface IMessageService {

    @WebMethod
    public String getMessage(@WebParam(name = "param") String param);

}

service方法实现类

package com.cxfwebserver.demo.service;

import net.sf.json.JSONArray;

import javax.jws.WebService;
import java.util.*;

@WebService(name = "textCXF",targetNamespace = "http://service.demo.cxfwebserver.com/",
endpointInterface = "com.cxfwebserver.demo.service.IMessageService")
public class MessageServiceImpl implements IMessageService{

    @Override
    public String getMessage(String param) {
        List<Map<String, String>> list = new ArrayList<>();
        Map<String, String> messaryMap1 = new HashMap<>();
        messaryMap1.put("param", param);
        messaryMap1.put("name", "张三");
        messaryMap1.put("sex", "男");
        messaryMap1.put("age", "20");
        Map<String, String> messaryMap2 = new HashMap<String, String>();
        messaryMap2.put("param", param);
        messaryMap2.put("name", "李四");
        messaryMap2.put("sex", "女");
        messaryMap2.put("age", "18");
        list.add(messaryMap1);
        list.add(messaryMap2);
        // 返回JSON数组字符串
        return JSONArray.fromObject(list).toString();
    }
}

CXF客户端拦截器

package com.cxfwebserver.demo.filter;

import java.util.List;

import javax.xml.namespace.QName;

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 ClientInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

    private String username;
    private String password;

    public ClientInterceptor(String username, String password) {
        // 发送请求之前进行拦截
        super(Phase.PREPARE_SEND);
        this.username = username;
        this.password = password;
    }

    @Override
    public void handleMessage(SoapMessage soapMessage) throws Fault {
        List<Header> headers = soapMessage.getHeaders();
        Document doc = DOMUtils.createDocument();
        Element auth = doc.createElement("authrity");
        Element username = doc.createElement("username");
        Element password = doc.createElement("password");
        username.setTextContent(this.username);
        password.setTextContent(this.password);
        auth.appendChild(username);
        auth.appendChild(password);
        headers.add(0, new Header(new QName("tiamaes"), auth));
    }
}

CXF服务端拦截器

package com.cxfwebserver.demo.filter;

import org.apache.cxf.binding.soap.SoapHeader;
import org.apache.cxf.binding.soap.SoapMessage;
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.util.StringUtils;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import javax.xml.soap.SOAPException;
import java.util.List;

public class AuthInterceptor extends AbstractPhaseInterceptor<SoapMessage> {

    String USERNAME;
    String PASSWORD;

    public AuthInterceptor(String username,String password) {
        //定义在什么阶段拦截,pre-protocol
        super(Phase.PRE_PROTOCOL);
        this.PASSWORD = password;
        this.USERNAME = username;
    }

    @Override
    public void handleMessage(SoapMessage soapMessage) throws Fault {
        String username = null;
        String password = null;
        List<Header> headers = soapMessage.getHeaders();
        if (headers == null){
            throw  new Fault(new IllegalAccessException("header未找到,无法验证"));
        }
        // 获取客户端传递的用户名和密码
        for (Header header : headers) {
            SoapHeader soapHeader = (SoapHeader) header;
            Element e = (Element) soapHeader.getObject();
            NodeList usernameNode = e.getElementsByTagName("username");
            NodeList passwordNode = e.getElementsByTagName("password");
            username = usernameNode.item(0).getTextContent();
            password = passwordNode.item(0).getTextContent();
            if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
                throw new Fault(new IllegalArgumentException("用户信息为空!"));
            }
        }
        // 校验客户端用户名密码是否和服务端一致
        if(!(username.equals(USERNAME) && password.equals(PASSWORD))) {
            throw new Fault(new SOAPException("用户信息认证失败!"));
        }
    }
}

配置类CXFConfig

package com.cxfwebserver.demo.config;

import javax.xml.ws.Endpoint;

import com.cxfwebserver.demo.filter.AuthInterceptor;
import com.cxfwebserver.demo.service.IMessageService;
import com.cxfwebserver.demo.service.MessageServiceImpl;
import org.apache.cxf.Bus;
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.Value;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CXFConfig {

    /**
    * 从配置文件application.properties中获取参数
    */
    @Value("${webservices.service.prefix}")
    private String prefix;
    @Value("${webservices.service.endpoint}")
    private String endpoint;
    @Value("${webservices.username}")
    private String username;
    @Value("${webservices.password}")
    private String password;


    /**
     * 作用:改变服务名的前缀名为prefix的值,默认是services
     * 此方法被注释后:wsdl访问(默认)地址为http://127.0.0.1:8080/services/user?wsdl
     * 去掉注释后:wsdl访问地址为:http://127.0.0.1:8080/mySoap/user?wsdl
     *
     * @return
     */
    @SuppressWarnings("all")
    @Bean(name = "cxfServlet")	//指定bean的名称,防止扫描时没有被加载
    public ServletRegistrationBean dispatcherServlet() {
        return new ServletRegistrationBean(new CXFServlet(), prefix + "/*");	//指定前缀后面必须跟上 /*
    }

    @Bean(name = Bus.DEFAULT_BUS_ID)
    public SpringBus springBus() {
        return new SpringBus();
    }

    //注入要调用的方法接口类
    @Bean
    public IMessageService messageServiceImpl() {
        return new MessageServiceImpl();
    }

    @Bean
    public Endpoint endpoint() {
        EndpointImpl endpointImpl = new EndpointImpl(springBus(), messageServiceImpl());
        // 服务端添加自定义拦截器:用户密码
        endpointImpl.getInInterceptors().add(new AuthInterceptor(username, password));
        endpointImpl.publish(endpoint);	//endpoint访问路径 /user
        return endpointImpl;
    }
}

客户端连接

package com.cxfwebserver.demo;

import com.cxfwebserver.demo.filter.ClientInterceptor;
import com.google.gson.Gson;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class ClientMain {

    private static String address = "http://localhost:8080/mySoap/user?wsdl";

    @SuppressWarnings("all")
    public static void main(String[] args) {
        // 创建动态客户端
        JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
        Client client = dcf.createClient(address);
        // 添加用户信息验证
        client.getOutInterceptors().add(new ClientInterceptor("admin", "root"));
        // 取返回值
        Object[] objects = new Object[0];
        try {
            // 接口方法、参数
            objects = client.invoke("getMessage", "clientParam");
            Map[] maps = new Gson().fromJson(objects[0].toString(),Map[].class);
            List<Map<String, String>> list = Arrays.asList(maps);
            System.out.println("返回的数据:");
            System.out.println(list);
        } catch (Exception e) {
            System.out.println("客户端接口访问失败!!");
            e.printStackTrace();
        }
    }
}

访问路径查看WSDL文件

1646033975169
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值