CXF开发webservice

jopen 11年前

什么是CXF:

Apache CXF = Celtix + Xfire

            支持多种协议:

            SOAP1.1,1.2

            XML/HTTP

            CORBA(Common Object Request Broker Architecture公共对象请求代理体系结构,早期语言使用的WS。                    C,c++,C#) 

            并可以与Spring进行快速无缝的整合

            灵活的部署:可以运行在Tomcat,Jboss,Jetty(内置),IBMWS,BeaWL上面。

 

安装CXF的其他支持项目:

Ant

Tomcat

并设置以下环境变量:

            JAVA_HOME  

            CXF_HOME

            ANT_HOME

            CATALINA_HOME

            Path = %JAVA_HOME%\bin;%CXF_HOME%\bin;%CATALINA_HOME%\bin;%ANT_HOME%\bin

            CLASSPATH=.;%CXF_HOME%\lib\cxf-manifest.jar;.\build\classes

 

在Eclipse中开发CXF的JavaSE应用:

导入CXF中的 所有jar文件。可以通过WACH_JARS来观察各jar文件的用途。

建立好之后的项目如图所示:

CXF开发webservice


CXF发布服务的类:

用两个不同的类发布应用:

            ServerFactoryBean   -- FacotryBean

            JaxWsServerFactoryBean(建议使用此类)

CXF开发webservice

使用ServerFactoryBean发布服务:

    

/**   * 使用ServerFactoryBean发布CXF的javase应用   * @author zhaoqx   *   */  public class HelloService {      public String sayHello(String name){    System.out.println("sayHello called...");    return "hello " + name;   }   public static void main(String[] args) {    ServerFactoryBean bean = new ServerFactoryBean();    bean.setAddress("http://192.168.1.24:9999/Hello");    bean.setServiceClass(HelloService.class);    bean.setServiceBean(new HelloService());    bean.create();    System.err.print("启动成功");   }  }


使用ClientProxyFactoryBean客户端调用

使用ServerFactoryBean发布服务以后,在没有接口的情况下,可以使用wsimport生成的客户端代码调用成功。

但如果要使用ClientProxyFactoryBean客户端去调用服务器,则必须要先在服务器端创建一个接口。(一直以来,Spring都要求面向接口编程,而cxf与Spring能很好的整合,也就在此。),所以,必须要重写服务器端的代码。这将导致刚才使用wsimport生成的调用代码失效。

同时, ClientProxyFactoryBean由于要用到CXF环境,所以要导入CXF的所有包。

同时,如果是在本项目中调用,则可以直接使用本项目的接口。

如果在其他项目中调用,仍然需wsimport生成的类,但只需要一个接口。

 

CXF开发webservice

 

使用JaxWsServerFactoryBean发布服务: (建议使用此类)

    JaxWsServerFactoryBean是ServerFactoryBean的子类,也是功能扩展类。但在CXF的API文档中没有提供此类API,请通过查看源代码的方式获取此类的帮助。此类,必须要在被发布为服务的类上添加@WebService注解,如果不加注解,虽然不出错,但也不会对外暴露任何方法。使用此类生成的wsdl文件更加规范。

@WebService  //@javax.xml.ws.BindingType(value=javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING)  public class HiService {   public String sayHi(String name){    System.out.println("服务调用");    return "Hi"+name;   }   public static void main(String[] args) {    JaxWsServerFactoryBean bean = new JaxWsServerFactoryBean();    bean.setAddress("http://localhost:9999/two");    bean.setServiceClass(HiService.class);    bean.setServiceBean(new HiService());    bean.create();    System.err.print("服务启动成功");   }  }

使用JaxWsProxyFactoryBean客户端调用:

此调用过程与ClientProxyFactoryBean的要求一样,也必须要拥有一个接口。

此时,仍然可以使用wsimport生成接口,在其他项目的调用。

CXF开发webservice


规范的做法应该是先书写一个接口,如下:

@WebService  public interface IHelloService {   public String sayHello(String name);  }
public class IHelloServiceImpl implements IHelloService {     public String sayHello(String name) {    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    return sdf.format(new Date())+"=== hello=====" + name;   }      public static void main(String[] args) {    JaxWsServerFactoryBean bean = new JaxWsServerFactoryBean();    bean.setAddress("http://192.168.1.24:8888/sayHi");    bean.setServiceClass(IHelloService.class);    bean.setServiceBean(new IHelloServiceImpl());    //加入请求消息拦截器    bean.getInInterceptors().add(new LoggingInInterceptor());    //加入响应消息拦截器    bean.getOutInterceptors().add(new LoggingOutInterceptor());    bean.create();    System.err.print("服务启动成功");   }    }


wsdl2java生成客户代码:

在cxf中,也提供了一个用于生成客户端调用代码的工具。它的功能就如同wsimport一样。

先让我们了解一下cxf的wsdl2java工具,可以生成一堆客户端调用的代码。

此工具位于cxf_home/bin目录下。参数与wsimport有所不同。

它包含以下参数:

-d参数,指定代码生成的目录。

-p参数,指定生成的新的包结构。

需要说明的是,由于wsdl2java是根据jdk1.7生成的本地代码,所以,需要对生成的代码做一点点修改。

在命令行执行:

wsdl2java –d . http://127.0.0.1:6666/helloworld?wsdl

 

调用端代码:

CXF开发webservice


使用JavaScript也可以访问WebServie:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  <html>    <head>      <title>使用jquery的ajax调用Web服务 </title>      <script type="text/javascript" src="js/jquery-1.6.2.js"></script>      <script type="text/javascript">       $(function(){        $("#but1").click(function(){         //指定访问服务的地址         var wsUrl = "http://localhost:8090/CXF03/cxf/hi";         //构造请求体         var soap = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHi xmlns:ns2="http://kaiyi/"><arg0>abc</arg0></ns2:sayHi></soap:Body></soap:Envelope>';         $.ajax({        url:wsUrl,        type:'post',        dataType:'xml',        contentType:'text/xml;charset=UTF-8',        data:soap,        success:function(data){         alert('OK!');         var o = $(data);         alert(o.find('return').eq(0).text());        },        error:function(){         alert('error!');        }        });        });       });      </script>    </head>        <body>      <input type="button" value="发送jquery的ajax请求" id="but1">    </body>  </html>

以上均是非WEB的开发下面介绍基于web的cxf

由于cxf的web项目已经集成了Spring所以,cxf的服务类都是在spring的配置文件中完成的。以下是步骤:

第一步:建立一个web项目。

第二步:准备所有jar包。将cxf_home\lib项目下的所有jar包全部copy到新项目的lib目录下,里面已经包含了spring3.0的jar包。

第三步:在web.xml中配置cxf的核心servlet,CXFServlet。

第四步:创建(最好是Copy)cxf-servlet.xml文件。这是一个spring的配置文件。

 

cxf-servlet.xml:

<?xml version="1.0" encoding="UTF-8"?>  <beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"   xmlns:soap="http://cxf.apache.org/bindings/soap" xmlns:jaxrs="http://cxf.apache.org/jaxrs"   xsi:schemaLocation="http://www.springframework.org/schema/beans         http://www.springframework.org/schema/beans/spring-beans.xsd        http://cxf.apache.org/bindings/soap         http://cxf.apache.org/schemas/configuration/soap.xsd        http://cxf.apache.org/jaxws         http://cxf.apache.org/schemas/jaxws.xsd        http://cxf.apache.org/jaxrs         http://cxf.apache.org/schemas/jaxrs.xsd        ">   <!-- 引入CXF Bean定义如下,早期的版本中使用 -->   <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" />     <!-- 通过spring配置文件发布CXF的服务 -->     <!-- 第一种发布方式:没有接口的发布(简单发布) -->   <!--     id:唯一标识    address:访问url    implementor:提供服务的类型     -->   <jaxws:endpoint id="helloService" address="/hello"    implementor="kaiyi.HelloService">    <!-- 加入消息拦截器 -->    <jaxws:inInterceptors>     <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean>    </jaxws:inInterceptors>    <jaxws:outInterceptors>     <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>    </jaxws:outInterceptors>   </jaxws:endpoint>      <!-- 第二种发布方式:带有接口的发布 -->   <jaxws:server id="hiService" address="/hi" serviceClass="kaiyi.IHiService">    <jaxws:serviceBean>     <!-- 提供服务的实现类 -->     <bean class="kaiyi.IHiServiceImpl"></bean>    </jaxws:serviceBean>    <!-- 加入消息拦截器  -->    <jaxws:inInterceptors>     <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean>    </jaxws:inInterceptors>    <jaxws:outInterceptors>     <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>    </jaxws:outInterceptors>   </jaxws:server>      <!-- 配置restful方式的web服务 -->   <bean id="ps" class="restful.PersonServiceImpl"></bean>   <jaxrs:server id="personService" address="/p">    <jaxrs:serviceBeans>     <ref bean="ps"/>    </jaxrs:serviceBeans>    <jaxrs:inInterceptors>     <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean>    </jaxrs:inInterceptors>    <jaxrs:outInterceptors>     <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>    </jaxrs:outInterceptors>   </jaxrs:server>  </beans>


web.xml

<?xml version="1.0" encoding="UTF-8"?>  <web-app version="2.5"    xmlns="http://java.sun.com/xml/ns/javaee"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">      <!-- 通过上下文参数指定spring配置文件的位置 -->   <context-param>    <param-name>contextConfigLocation</param-name>    <param-value>classpath:cxf-servlet.xml</param-value>   </context-param>   <listener>    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>   </listener>      <!-- 配置CXF框架的核心Servlet  -->   <servlet>    <servlet-name>cxf</servlet-name>    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>    <!-- 通过初始化参数指定配置文件的位置 -->    <!--      <init-param>     <param-name>config-location</param-name>     <param-value>classpath:cxf-servlet.xml</param-value>    </init-param>-->        </servlet>     <servlet-mapping>    <servlet-name>cxf</servlet-name>    <url-pattern>/cxf/*</url-pattern>   </servlet-mapping>       <welcome-file-list>      <welcome-file>index.jsp</welcome-file>    </welcome-file-list>  </web-app>

这是我的包结构:

CXF开发webservice

/**   * 使用ServerFactoryBean发布CXF的javase应用   * @author zhaoqx   *   */  @WebService  public class HelloService {      public String sayHello(String name){    System.out.println("sayHello called...");    return "hello " + name;   }     }

package kaiyi;    import javax.jws.WebService;    @WebService  public interface IHiService {   public String sayHi(String name);  }
package kaiyi;    public class IHiServiceImpl implements  IHiService{     public String sayHi(String name) {    System.out.println("sayHi....");    return "hi " + name;   }    }

            
在地址栏访问:

CXF开发webservice

Java项目代码调用服务:

使用纯Java项目调用

1、根据客户端生成的代码来调用。(优选这种方式)请先生成然后在任意的Java项目中调用 。

2、客户端只拥有一个接口,使用JaxWsProxyFactoryBean来调用。

因为以下使用了JaxWsProxyFactoryBean,所以,仍然需要CXF的环境,而使用此环境就会造成Jar文件的大量冗余,所以大家要谨慎选择(一下的地址是随便贴的)。

CXF开发webservice

在Spring项目中,通过配置文件调用:

以下是使用Spring的配置文件调用:

新建立一个Java项目,并加载cxf的所有包。

只需要生成的接口文件.

在classpath下新建立一个ClientBeans.xml文件.

优点与缺点:

        此种情况,适合于一个Javaweb项目已经集成了Spring。并希望通过CXF配置的方式调用Web服务。

        此种情况,仍然需要导入CXF的大量jar包。

        这种情况也存在一定优点,如可以将外部的Web服务通过配置文件注入(DI)到Action类中。

 

结构图如下(带接口的方式):

CXF开发webservice

 

ClientBeans.xml文件的内容:

<?xml version="1.0" encoding="UTF-8"?>  <beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"   xmlns:soap="http://cxf.apache.org/bindings/soap"   xsi:schemaLocation="http://www.springframework.org/schema/beans         http://www.springframework.org/schema/beans/spring-beans.xsd        http://cxf.apache.org/bindings/soap         http://cxf.apache.org/schemas/configuration/soap.xsd        http://cxf.apache.org/jaxws         http://cxf.apache.org/schemas/jaxws.xsd">    <!-- 配置客户端bean -->    <!--      id:唯一标识     address:请求的服务地址     serviceClass:客户端接口     -->    <jaxws:client id="hiService" address="http://localhost:8090/CXF03/cxf/hi" serviceClass="kaiyi.IHiService"></jaxws:client>      </beans>

 

以下是调用代码:

package test;    import kaiyi.IHiService;    import org.springframework.context.ApplicationContext;  import org.springframework.context.support.ClassPathXmlApplicationContext;    public class test {     public static void main(String[] args) {    ApplicationContext ctx = new ClassPathXmlApplicationContext("ClientBeans.xml");    IHiService ihi = (IHiService) ctx.getBean("hiService");    ihi.sayHi("abc");    System.out.println(ihi.getClass().getName());   }  }

 

通过JS来调用WebService:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  <html>    <head>      <title>使用jquery的ajax调用Web服务 </title>      <script type="text/javascript" src="js/jquery-1.6.2.js"></script>      <script type="text/javascript">       $(function(){        $("#but1").click(function(){         //指定访问服务的地址         var wsUrl = "http://localhost:8090/CXF03/cxf/hi";         //构造请求体         var soap = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHi xmlns:ns2="http://kaiyi/"><arg0>abc</arg0></ns2:sayHi></soap:Body></soap:Envelope>';         $.ajax({        url:wsUrl,        type:'post',        dataType:'xml',        contentType:'text/xml;charset=UTF-8',        data:soap,        success:function(data){         alert('OK!');         var o = $(data);         alert(o.find('return').eq(0).text());        },        error:function(){         alert('error!');        }        });        });       });      </script>    </head>        <body>      <input type="button" value="发送jquery的ajax请求" id="but1">    </body>  </html>

POJO类:

package pojo;        import java.io.Serializable;    import javax.xml.bind.annotation.XmlElement;  import javax.xml.bind.annotation.XmlRootElement;    @XmlRootElement(name = "person")  public class Person implements Serializable{   /**    *     */   private static final long serialVersionUID = 1L;   private String id;   private String name;   private String desc;      @XmlElement(name = "id")   public String getId() {    return id;   }   public void setId(String id) {    this.id = id;   }   @XmlElement(name = "name")   public String getName() {    return name;   }   public void setName(String name) {    this.name = name;   }   @XmlElement(name = "desc")   public String getDesc() {    return desc;   }   public void setDesc(String desc) {    this.desc = desc;   }   @Override   public String toString() {    return "Person [desc=" + desc + ", id=" + id + ", name=" + name + "]";   }        }

现在一般来说用URL的方式调用restful服务比较流行:


它与WSDL方式完全不同,采用的是一种新型的方式,企业开发使用较为常见:

Available RESTful services:

Endpoint address: http://localhost:8090/CXF03/cxf/p
WADL : http://localhost:8090/CXF03/cxf/p?_wadl&type=xml

访问后返回的是json或者XML数据

此种方式只需要在cxf-servlet.xml配置一下即可:

<!-- 配置restful方式的web服务 -->   <bean id="ps" class="restful.PersonServiceImpl"></bean>   <jaxrs:server id="personService" address="/p">    <jaxrs:serviceBeans>     <ref bean="ps"/>    </jaxrs:serviceBeans>    <jaxrs:inInterceptors>     <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean>    </jaxrs:inInterceptors>    <jaxrs:outInterceptors>     <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>    </jaxrs:outInterceptors>   </jaxrs:server>

以下是访问方式:

package test;    import java.io.InputStream;  import java.net.HttpURLConnection;  import java.net.URL;  import java.net.URLConnection;  import java.util.List;    import net.sf.json.JSONArray;  import net.sf.json.JSONObject;    import org.junit.Test;    import pojo.Person;      // 使用URL的方式调用restful服务  public class UrlConnectionTest {        @Test   public void test1() throws Exception {    String wsUrl = "http://localhost:8090/CXF03/cxf/p/persons/id0";    URL url = new URL(wsUrl);        URLConnection conn = url.openConnection();    HttpURLConnection con = (HttpURLConnection)conn;        con.setRequestMethod("GET");    int code = con.getResponseCode();    if(code == 200){     InputStream in = con.getInputStream();     byte[] b = new byte[1024];     int len = 0;     StringBuffer sb = new StringBuffer();     while((len=in.read(b))!=-1){      String s = new String(b,0,len,"UTF-8");      sb.append(s);     }     String json = sb.toString();     JSONObject o = JSONObject.fromObject(json);     json = o.getString("person").toString();     //将String转换为Person对象     Person person = (Person) JSONObject.toBean(JSONObject.fromObject(json), Person.class);     System.out.println("----"+person);    }    con.disconnect();   }         @Test   public void test2() throws Exception {    String wsUrl = "http://localhost:8090/CXF03/cxf/p/persons";    URL url = new URL(wsUrl);        URLConnection conn = url.openConnection();    HttpURLConnection con = (HttpURLConnection)conn;        con.setRequestMethod("GET");    int code = con.getResponseCode();    if(code == 200){     InputStream in = con.getInputStream();     byte[] b = new byte[1024];     int len = 0;     StringBuffer sb = new StringBuffer();     while((len=in.read(b))!=-1){      String s = new String(b,0,len,"UTF-8");      sb.append(s);     }     String json = sb.toString();     JSONObject o = JSONObject.fromObject(json);     Object obj = o.get("person");     json = obj.toString();         List<Person> list = (List<Person>)JSONArray.toCollection(JSONArray.fromObject(json), Person.class);     for(Person p:list){      System.out.println(p);     }    }    con.disconnect();   }    }

我们可以根据被调用服务的接口上的注解来决定返回值的数据格式

//@Produces( { MediaType.APPLICATION_XML })

@Produces( { MediaType.APPLICATION_JSON })

public interface IPersonService extends Serializable 

 

以下是服务代码:

package restful;    import java.io.Serializable;  import java.util.List;    import javax.ws.rs.GET;  import javax.ws.rs.Path;  import javax.ws.rs.PathParam;  import javax.ws.rs.Produces;  import javax.ws.rs.core.MediaType;    import pojo.Person;      //@Produces( { MediaType.APPLICATION_XML })  @Produces( { MediaType.APPLICATION_JSON })  public interface IPersonService extends Serializable {      @GET   @Path(value="/persons/{id}")   public Person findPersonById(     @PathParam("id")     String id);      @GET   @Path(value="/persons")   public List<Person> findAllPerson();  }
package restful;        import java.util.ArrayList;  import java.util.HashMap;  import java.util.List;  import java.util.Map;    import pojo.Person;        public class PersonServiceImpl implements IPersonService {   /**    *     */   private static final long serialVersionUID = 1L;   private static Map<String, Person> ps = new HashMap<String, Person>();   static {    for(int i=0;i<5;i++){     Person p = new Person();     p.setId("id" + i);     p.setName("person" + i);     p.setDesc("desc" + i);     ps.put(p.getId(), p);    }   }   public List<Person> findAllPerson() {    return new ArrayList<Person>(ps.values());   }   public Person findPersonById(String id) {    return ps.get(id);   }       }

 

以下是调用代码:

package test;    import java.io.InputStream;  import java.net.HttpURLConnection;  import java.net.URL;  import java.net.URLConnection;  import java.util.List;    import net.sf.json.JSONArray;  import net.sf.json.JSONObject;    import org.junit.Test;    import pojo.Person;      // 使用URL的方式调用restful服务  public class UrlConnectionTest {        @Test   public void test1() throws Exception {    String wsUrl = "http://localhost:8090/CXF03/cxf/p/persons/id0";    URL url = new URL(wsUrl);        URLConnection conn = url.openConnection();    HttpURLConnection con = (HttpURLConnection)conn;        con.setRequestMethod("GET");    int code = con.getResponseCode();    if(code == 200){     InputStream in = con.getInputStream();     byte[] b = new byte[1024];     int len = 0;     StringBuffer sb = new StringBuffer();     while((len=in.read(b))!=-1){      String s = new String(b,0,len,"UTF-8");      sb.append(s);     }     String json = sb.toString();     JSONObject o = JSONObject.fromObject(json);     json = o.getString("person").toString();     //将String转换为Person对象     Person person = (Person) JSONObject.toBean(JSONObject.fromObject(json), Person.class);     System.out.println("----"+person);    }    con.disconnect();   }         @Test   public void test2() throws Exception {    String wsUrl = "http://localhost:8090/CXF03/cxf/p/persons";    URL url = new URL(wsUrl);        URLConnection conn = url.openConnection();    HttpURLConnection con = (HttpURLConnection)conn;        con.setRequestMethod("GET");    int code = con.getResponseCode();    if(code == 200){     InputStream in = con.getInputStream();     byte[] b = new byte[1024];     int len = 0;     StringBuffer sb = new StringBuffer();     while((len=in.read(b))!=-1){      String s = new String(b,0,len,"UTF-8");      sb.append(s);     }     String json = sb.toString();     JSONObject o = JSONObject.fromObject(json);     Object obj = o.get("person");     json = obj.toString();         List<Person> list = (List<Person>)JSONArray.toCollection(JSONArray.fromObject(json), Person.class);     for(Person p:list){      System.out.println(p);     }    }    con.disconnect();   }    }

这是调用http://localhost:8090/CXF03/cxf/p/persons 地址后被解析的数据

----Person [desc=desc0, id=id0, name=person0]

Person [desc=desc2, id=id2, name=person2]

Person [desc=desc1, id=id1, name=person1]

Person [desc=desc0, id=id0, name=person0]

Person [desc=desc4, id=id4, name=person4]

Person [desc=desc3, id=id3, name=person3]

 

目前的webservice的就到这!