基于CXF的WebServie技术应用

基于CXFWebServie技术应用

1   概述

面向服务的体系结构(service-oriented architectureSOA)是一种思想,它将应用程序的不同功能单元通过中立的契约(独立于硬件平台、操作系统、编程语言)联系起来,使各个形式的功能单元更好的集成。WebServiceSOA思想的一种较好实现方式,它和普通的web程序一样(如JSPASP)采用HTTP协议进行通讯,但是它只用POST方式通讯,它的数据是基于XML格式的,采用SOAPSimple Object Access Protocol)协议。SOAP协议实际上就是基于XML编码规范的文本协议,它是WebService特有的应用协议。

1.1      WebService体系架构


基于CXF的WebServie技术应用
 

 

1.2      CXF说明

JAVA平台上对WebService支持的成熟框架很多,如CXFAXIS1&2XFire等,本文使用CXF

Apache CXF = Celtix + XFire,前身叫 Apache CeltiXfire,已经正式更名为 Apache CXF 了,以下简称为 CXFCXF 继承了 Celtix XFire 两大开源项目的精华,是Apache基金会组织下的一个顶级项目,简化了WebService服务端的创建过程。

CXF实现了JAX-WS2.0规范,并通过了JAX-WS2.0 TCK

CXF可以和Spring无缝集成;

CXF支持多种传输协议(HTTP JMS Corba等);

CXF支持多种Binding数据格式(SOAPXMLJSON等);

CXF 支持多种DataBinding数据类型(JAXB Aegis)

CXF基于Interceptor的架构,使得整个框架非常易于扩展。本次我主要讨论CXFHTTP/SOAP模式下的处理机制。

CXF的下载地址为:http://cxf.apache.org/download.html,下载后解压压缩包,从lib中至少要拿出如下jar到应用:

cxf-2.2.5.jar

antlr-2.7.7.jar

commons-codec-1.3.jar

commons-collections-3.2.1.jar

commons-lang-2.4.jar 

commons-logging-1.1.1.jar

commons-pool-1.5.2.jar 

geronimo-annotation_1.0_spec-1.1.1.jar

geronimo-jaxws_2.1_spec-1.0.jar 

geronimo-ws-metadata_2.0_spec-1.1.2.jar

jaxb-api-2.1.jar

xalan-2.7.1.jar

wsdl4j-1.6.2.jar 

XmlSchema-1.4.5.jar

2      构建服务端

2.1      Servlet构建的简单服务端

1)         定义webservice接口和实现类

publicinterface IHelloWorld {

             public String sayHello(String name);

}

publicclass HelloWorldImpl implements IHelloWorld {

            public String sayHello(String name) {

                 return"Hello " + name + ".";

            }

}

 

2)         定义webservice

publicclass HelloWorldServlet extends CXFNonSpringServlet {

privatestaticfinallongserialVersionUID = 1L;

@Override

protectedvoid loadBus(ServletConfig sc) {

    super.loadBus(sc);

     Bus bus = getBus();

     //如果我们要发布多个WebService,仅仅需要修改WebService的接口定义、发布地址、实现类,然后不断重复下面的代码即可

     ServerFactoryBean factory = new ServerFactoryBean();

     factory.setBus(bus);

     factory.setServiceClass(IHelloWorld.class);

     factory.setAddress("/HelloWorld");

     factory.setServiceBean(new HelloWorldImpl());

     factory.create();

}

}

         首先CXF的内部上下文对象Bus,然后我再使用CXF提供的ServerFactoryBean工具类把自己写的一个普通的JavaBean发布成WebService

ServerFactoryBean需要以下参数:

1) Bus。也就是上面我们所得到的Bus对象。

2) WebService的接口定义,也就是指自己的WebService实现了哪个Java Interface

3) WebService的发布地址,也就是访问WebServiceURL地址。

4) WebService的实现类。

web.xml中配置HelloWorldServlet

<servlet>    

<servlet-name> HelloWorldServlet </servlet-name>

<servlet-class> HelloWorldServlet</servlet-class>

<load-on-startup>1</load-on-startup>

       </servlet> 

<servlet-mapping>

<servlet-name>HelloWorldServlet</servlet-name>

<url-pattern>/services/*</url-pattern>

</servlet-mapping>

启动服务器,访问http://127.0.0.1:8080/CXFDemo/services/?wsdl,可以看到服务列表,http://127.0.0.1:8080/CXFDemo/services/HelloWorld?wsdl就是我们的服务地址。

2.2      java注解的简单服务端

webservice实现类加上注解

@WebService

public class HelloWorldImpl implements IHelloWorld {

public String sayHello(@WebParam(name = "text") String name) {

           return "Hello " + name + ".";

}

}

构建服务端

public class SimpleServer {

public SimpleServer() {

           System.out.println("Starting Server");

           IHelloWorld helloService = new HelloWorldImpl();

           String address = "http://localhost:9000/helloWorld";

           Endpoint.publish(address, helloService);

}

public static void main(String[] args) throws InterruptedException {

          

           //System.out.println(System.getProperty("java.endorsed.dirs"));

           new SimpleServer();

           System.out.println("Server ready...");

           Thread.sleep(6000000);

           System.out.println("Server exiting");

           System.exit(0);

}

}

           运行应用程序,访问http://localhost:9000/helloWorld?wsdl,已经发布成功,可以看到服务没有在web服务器中发布,这是因为CXF内置了Jetty应用服务器,可以直接在Jetty上发布。

2.3      结合spring构建web服务

1)         声明实体

@XmlType(name = "User")

@XmlAccessorType(XmlAccessType.FIELD)

publicclass User implements Serializable {

    privatestaticfinallongserialVersionUID = 1L;

    private String username;

    private String password;

 

    public String getUsername() {

        returnusername;

    }

    publicvoid setUsername(String username) {

        this.username = username;

    }

    public String getPassword() {

        returnpassword;

    }

    publicvoid setPassword(String password) {

        this.password = password;

    }

}

2)         声明接口

import javax.jws.WebService;

import javax.jws.soap.SOAPBinding;

import javax.jws.soap.SOAPBinding.Style;

 

@WebService

@SOAPBinding(style = Style.RPC)

publicinterface IHelloService {

  User saveUser(User user);

  User getMaxLongNameUser(User u1, User u2);

}

 

3)         声明实现

import javax.jws.WebService;

import javax.jws.soap.SOAPBinding;

import javax.jws.soap.SOAPBinding.Style;

import org.apache.log4j.Logger;

 

@WebService

@SOAPBinding(style = Style.RPC)

publicclass HelloServiceImpl implements IHelloService {

    privatestatic Logger log = Logger.getRootLogger();

    public User saveUser(User user) {

        log.debug("saveUser is called!");

        return user;

    }

    public User getMaxLongNameUser(User u1, User u2) {

        log.debug("getMaxLongNameUser is called!");

        return u1;

    }

}

4)         spring配置

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"

    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jaxws="http://cxf.apache.org/jaxws"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd

    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd

    http://cxf.apache.org/jaxws

    http://cxf.apache.org/schemas/jaxws.xsd">

 

    <!-- 还需要引入以下3个关于CXF的资源文件,这三个文件在cxf.jar -->

    <import resource="classpath:META-INF/cxf/cxf.xml" />

    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />

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

 

    <bean id="inInter" class="org.apache.cxf.interceptor.LoggingInInterceptor" />

    <bean id="outInter" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />

    <jaxws:endpoint id="userManager" address="/UserManager"

        implementorClass="com.pgw.test.webservice.IHelloService">

        <jaxws:implementor>

            <bean id="userServiceImpl" class="com.pgw.test.webservice.HelloServiceImpl">

            </bean>

        </jaxws:implementor>

        <!-- 加入拦截器 -->

        <jaxws:inInterceptors>

            <ref bean="inInter" />

            <ref bean="outInter" />

        </jaxws:inInterceptors>

    </jaxws:endpoint>

</beans>

5)         web.xml配置

    <context-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>/WEB-INF/config/*.xml</param-value>

    </context-param>

    <listener>

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

    </listener>

    <listener>

        <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>

    </listener>

    <servlet>

        <servlet-name>CXFService</servlet-name>

        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>CXFService</servlet-name>

        <url-pattern>/*</url-pattern>

    </servlet-mapping>

完成上述操作后发布web程序,在浏览器中输入

http://127.0.0.1:8080/CXFDemo/UserManager?wsdl查看服务。另外在部署中可能会遇到JAXB 2.0 API is being loaded from the bootstrap classloader这个错误,这是因为cxf2.2.5需要jaxb-api-2.1.jar支持,而低版本的jdk1.6默认的是jaxb-api-2.0.jar,解决办法如下:

重新构建一个相同类型的项目(不同的项目类型,输出的结果不一样),在项目中打印输出System.getProperty("java.endorsed.dirs"),如果文件系统中不存在输出的目录则创建,把jaxb-api-2.1.jar放置到该目录下,问题解决。

3      构建客户端

3.1      WSDL2Java构建客户端

WSDL2Java位于cxfbin目录下,可以根据wsdl生成java客户端代码。在命令行中输入 WSDL2JAVA –help 可以获取该指令的帮助。

如下所示:

wsdl2java -frontend jaxws21

-p com.ailk.demo.webservice.client.wsdl2Java

-d D:\workspace\olcom5\CXFDemo\src\ http://localhost:8080/CXFDemo/services/HelloWorld?wsdl

 

客户端生成后进行调用:

publicclass ClientMain {

 

publicstaticvoid main(String[] args) {

     IHelloWorld helloWorld = new IHelloWorld();

     IHelloWorldPortType client = helloWorld.getIHelloWorldPort();

     String resultStr = client.sayHello("test");

     System.out.println("HelloWorld webService response :" + resultStr);

}

}

3.2      JAX-WS Proxy构建客户端

import java.net.MalformedURLException;

import java.net.URL;

import javax.xml.namespace.QName;

import javax.xml.ws.Service;

import com.ailk.demo.service.IHelloWorld;

 

publicclass ClientMain {

publicstaticvoid main(String[] args) throws MalformedURLException {

     URL wsdlURL = new URL("http://localhost:9000/helloWorld?wsdl");

     QName SERVICE_NAME = new QName("http://impl.service.demo.ailk.com/", "HelloWorldImplService");

     QName PORT_NAME = new QName("http://impl.service.demo.ailk.com/", "HelloWorldImplPort");

     Service service = Service.create(wsdlURL, SERVICE_NAME);

     IHelloWorld client = service.getPort(PORT_NAME, IHelloWorld.class);

     String result = client.sayHello("test");

     System.out.println("HelloWorld webService response :" + result);

}

}

3.3      JaxWsProxyFactoryBean构建客户端

JaxWsProxyFactoryBean简化了Proxy

publicclass ClientMain {

publicstaticvoid main(String[] args) {

     JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

     factory.setServiceClass(IHelloWorld.class);

     factory.setAddress("http://localhost:9000/helloWorld?wsdl");

     IHelloWorld client = (IHelloWorld) factory.create();

     String result = client.sayHello("test");

     System.out.println("HelloWorld webService response :" + result);

}

}

3.4      JaxWsClientProxy动态客户端

DynamicClientFactory factory = DynamicClientFactory.newInstance();

     Client client = factory.

createClient("http://localhost:8080/CXFDemo/services/HelloWorld?wsdl");

     try {

         Object[] objs = client.invoke("sayHello",

new Object[] { "test" });

         System.out.println("HelloWorld webService response :" + objs[0]);

     } catch (Exception e) {

         e.printStackTrace();

}

3.5      JaxWsDynamicClientFactory动态客户端

 

import org.apache.cxf.endpoint.Client;

import org.apache.cxf.endpoint.ConduitSelector;

import org.apache.cxf.endpoint.Endpoint;

import org.apache.cxf.endpoint.UpfrontConduitSelector;

import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;

import org.apache.cxf.transport.http.HTTPConduit;

import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;

 

publicclass ClientMain {

 

publicstaticvoid main(String[] args) throws Exception {

     JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();

     Client client = dcf.createClient("http://localhost:8080/CXFDemo/services/HelloWorld?wsdl");

     // sayHello 为接口中定义的方法名称 test为传递的参数返回一个Object数组

     Object[] objects = client.invoke("sayHello", "test");

     // 输出调用结果

     System.out.println("HelloWorld webService response :" + objects[0]);

     // 动态设置Address

     Endpoint ep = client.getEndpoint();

ep.getEndpointInfo().setAddress("http://localhost:8080/CXFDemo/services/HelloWorld");

     ConduitSelector cs = new UpfrontConduitSelector();

     cs.setEndpoint(ep);

     client.setConduitSelector(cs);

 

     // 设置连接超时时间、相应超时时间

     HTTPConduit http = (HTTPConduit) client.getConduit();

     HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();

     httpClientPolicy.setConnectionTimeout(60000);

     httpClientPolicy.setAllowChunking(false);

     httpClientPolicy.setReceiveTimeout(600000);

     http.setClient(httpClientPolicy);

 

     objects = client.invoke("sayHello", "test");

     // 输出调用结果

     System.out.println("HelloWorld webService response :" + objects[0]);

}

}

客户端创建的过程中,生产服务下所有的方法类、参数类、返回值类,耗时比较惊人,建议在实际应用中预先初始化。

 

在实际的项目应用中,有这样的场景,提供的服务是相同的,但是服务地址会随着业务的需求动态的变化,就需要我们动态的设置服务地址。

你可能感兴趣的:(java,webservice,CXF)