• 使用CXF实现基于Soap协议的WebService


    本文介绍使用CXF实现基于Soap协议的WebService(CXF的版本是3.0.0)

    一. 前言

    Java有三种WebService规范:Jax-WS,Jax-RS,Jaxm

    1. Jax-WS(Java Api for XML-Based WebService):实现Soap协议(Simple Object Access Protocol)(用的也不多了)
    2. Jax-RS(Java Api for Resource-Based WebService):实现Rest方式(Representational State Transfer)(推荐)
    3. Jaxm支持文件传输,暴露更多底层细节(不推荐)

    二. 引入依赖

    <!-- Jax-WS前端控制模块,处理服务业务的请求 -->
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-frontend-jaxws</artifactId>
      <version>3.0.0</version>
    </dependency>

    <!-- 数据传输模块(与Rest一样) -->
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-transports-http</artifactId>
      <version>3.0.0</version>
    </dependency>

    <!-- 引入内置的Jetty,如在Tomcat中发布服务可以不引入(与Rest一样)  -->
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-transports-http-jetty</artifactId>
      <version>3.0.0</version>
    </dependency>

    三. 编写SEI(Service Endpoint Interface)

    1. 接口

    @WebService
    public interface HelloWorld {

      //方法名和接口名可以不一致,默认一致,如果一致,可以省略operationName,甚至省略WebMethod
      @WebMethod(operationName = "sayHiString")
      public String sayHelloString(@WebParam(name = "name") String name);

      //对象参数User代码略,User的定义可能不在HelloWorld的包中,在或不在,对后期调用会产生影响
      @WebMethod(operationName = "sayHiUser")
      public User sayHelloUser(User user);

    }

    2. 实现类

    //endpointInterface的值是接口的全名

    @WebService(endpointInterface = "net.jmystudio.cxf.soap.HelloWorld", serviceName = "HelloWorld")
    public class HelloWorldImpl implements HelloWorld {

      @Override
      public String sayHelloString(String name) {
        return "Hello, (String) " + name;
      }

      @Override
      public User sayHelloUser(User user) {
        return new User("Soap_" + user.getName());
      }

    }

    四. 发布服务

    1. 发布方式一:使用默认的Jetty时,使用Java内置的Endpoint

    javax.xml.ws.Endpoint.publish("http://localhost:8088/testcxf/cxf/soap/hello1", new HelloWorldImpl());

     (http://localhost:8088/testcxf/cxf/soap/hello1?wsdl)

     2. 发布方式二:使用默认的Jetty时,使用CXF提供的JaxWsServerFactoryBean(与Rest类似)

    // 1). 服务端工厂类
    JaxWsServerFactoryBean server = new JaxWsServerFactoryBean();

    // 2). 设置了二个属性(setServiceClass可省)
    server.setAddress("http://localhost:8088/testcxf/cxf/soap/hello2");
    server.setServiceBean(new HelloWorldImpl());

    // 添加输入&输出日志(可选)
    server.getInInterceptors().add(new LoggingInInterceptor());
    server.getOutInterceptors().add(new LoggingOutInterceptor());

    // 3). 创建并发布服务,会发起一个http服务,默认使用Jetty
    server.create();

    (http://localhost:8088/testcxf/cxf/soap/hello2?wsdl)

    3. 发布方式三:在Web应用中,使用CXFNonSpringServlet发布(实际是显式调用了发布方式一(或二)

    a. 在web.xml中添加CXFNonSpringServlet的实现类(与Rest一样)

    <servlet>
      <servlet-name>CXFNonSpring</servlet-name>
      <servlet-class>net.jmystudio.servlet.WebServiceNonSpringServlet</servlet-class>
    </servlet>
    <servlet-mapping>
      <servlet-name>CXFNonSpring</servlet-name>
      <url-pattern>/cxfns/*</url-pattern>
    </servlet-mapping>

    b. 实现WebServiceNonSpringServlet类(与Rest类似)

    import org.apache.cxf.transport.servlet.CXFNonSpringServlet;

    public class WebServiceNonSpringServlet extends CXFNonSpringServlet {

      @Override
      protected void loadBus(ServletConfig servletConfig) {
        super.loadBus(servletConfig);
        //使用Endpoint,代码类似发布方式一,也可以使用JaxWsServerFactoryBean,代码类似发布方式二,此处略
        javax.xml.ws.Endpoint.publish("/soap/hello3", new HelloWorldImpl());

      }

    }

    (http://localhost:8090/testcxf/cxfns/soap/hello3?wsdl)(应用包名是testcxf)

    4. 发布方式四:在Web应用中,整合Spring+CXFServlet发布(实际是隐式调用了发布方式一(或二)(最常用)

    a. 需要引入Spring相关的Jar包(与Rest一样)

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>4.1.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>4.1.6.RELEASE</version>
    </dependency>

    b. 在web.xml中添加Spring配置和CXFServlet(与Rest一样)

    <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring*.xml</param-value>
    </context-param>
    <listener>

      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
      <servlet-name>CXF</servlet-name>
      <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    </servlet>
    <servlet-mapping>
      <servlet-name>CXF</servlet-name>
      <url-pattern>/cxf/*</url-pattern>
    </servlet-mapping>

    c.添加关于cxf的spring配置文件(例spring-cfx-soap.xml)(与Rest类似)

    <!-- 初始化cxf servlet -->
    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

    <!-- 日志拦截器bean -->
    <bean id="loggingInInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
    <bean id="loggingOutInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>

    <!-- 发布方式1:使用Endpoint -->
    <jaxws:endpoint address="/soap/hello5" implementor="net.jmystudio.cxf.soap.HelloWorldImpl" >
      <jaxws:inInterceptors>
        <ref bean="loggingInInterceptor"/>
      </jaxws:inInterceptors>
      <jaxws:outInterceptors>
        <ref bean="loggingOutInterceptor"/>
      </jaxws:outInterceptors>
    </jaxws:endpoint>

    <!-- 发布方式2:使用JaxWsServerFactoryBean -->
    <jaxws:server address="/soap/hello6" serviceClass="net.jmystudio.cxf.soap.HelloWorldImpl" >
      <jaxws:inInterceptors>
        <ref bean="loggingInInterceptor"/>
      </jaxws:inInterceptors>
      <jaxws:outInterceptors>
        <ref bean="loggingOutInterceptor"/>
      </jaxws:outInterceptors>
    </jaxws:server>

    (http://localhost:8090/testcxf/cxf/soap/hello5?wsdl) (应用包名是testcxf)
    (http://localhost:8090/testcxf/cxf/soap/hello6?wsdl) 

    五. 调用方式

    无论使用哪种发布方式,发布成功后,在浏览器中输入 $address+"?wsdl",均可看到该WebService的接口定义的详细内容

    例如 http://localhost:8088/testcxf/cxf/soap/hello1?wsdl

    代码调用主要有2种方式

    1. 调用方式一:使用JaxWsProxyFactoryBean获得静态Client,显式依赖WebService接口,需要引入服务提供方提供的jar包)(不推荐)(与Rest类似)

     // 1). 客户端工厂类
    JaxWsProxyFactoryBean proxy = new JaxWsProxyFactoryBean();

     // 2). 设置了两个属性
    proxy.setAddress("http://localhost:8088/testcxf/cxf/soap/hello1");
    proxy.setServiceClass(HelloWorld.class);

    // 添加输入&输出日志(可选)
    proxy.getInInterceptors().add(new LoggingInInterceptor());
    proxy.getOutInterceptors().add(new LoggingOutInterceptor());

    // 3). 创建会发起一个http请求
    HelloWorld  staticClient =  (HelloWorld) proxy.create();

    // 4). 调用方法,获得数据
    System.out.println(staticClient.sayHelloString("Jimmy"));
    System.out.println(staticClient.sayHelloUser(new User("Tony")));

    2. 调用方式二:使用JaxWsDynamicClientFactory获得动态Client,(无需引入服务提供方提供的jar包)

    // 1). 根据接口定义url,获得Client
    JaxWsDynamicClientFactory clientFactory = JaxWsDynamicClientFactory.newInstance();
    Client dynamicClient = clientFactory.createClient("http://localhost:8088/testcxf/cxf/soap/hello1?wsdl");

    // 2). 调用接口,获得数据 
    //sayHiString是WebMethod中定义的接口名,不是方法名
    Object[] result = dynamicClient.invoke("sayHiString", "Kevin"); 
    System.out.println(result[0]);
    result = dynamicClient.invoke("sayHiUser", new User("Cherry"));
    System.out.println(result[0]);

    Note: 调用方式二中,参数是对象的情况需要特别注意,User的定义是否在SEI(此例是HelloWorld)的包中?

    如果发布时不指定特定的targetNamespace的话,默认的targetNamespace是SEI的所在包名(逆序),而不是参数User所在包名(2个包名可以不一样的),这时调用的时候,必须传递定义在targetNamespace的User(不是发布时代码中的User,具体可查看该接口的wsdl,会发现此User非彼User),不然会报两个User的java.lang.ClassCastException

    解决办法是:

    a.最简单:发布时,定义的User和SEI同在一个包中(推荐)
    b.动态解析wsdl,需要获得ServiceInfo/BindingInfo/BindingOperationInfo/BindingMessageInfo/MessagePartInfo/PropertyDescriptor等一系列对象,逐步获得接口定义的详细内容后,动态生成参数对象,难度很大(不推荐)

    3. 其它调用方式:使用HttpClient或HttpURLConnection等连接,与常规的URL获得数据的方式一致,详情略。(与Rest一样)

     

  • 相关阅读:
    poj 1753 -- Flip Game
    hdu 2209 -- 翻纸牌游戏
    文件系统的挂载与卸载挂载
    我的vim配置(一)
    Poj 3687 -- Labeling Balls
    主动激发“onclick”事件;prompt
    this
    函数嵌套
    调用函数时传递的实参个数arguments.length; ,函数定义时的形参个数sum.length
    回调函数,用户定义的排序规则
  • 原文地址:https://www.cnblogs.com/zjm701/p/6839560.html
Copyright © 2020-2023  润新知