CXF開發webservice
什么是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發布服務的類:
用兩個不同的類發布應用:
ServerFactoryBean -- FacotryBean
JaxWsServerFactoryBean(建議使用此類)

使用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生成的類,但只需要一個接口。

使用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生成接口,在其他項目的調用。

規范的做法應該是先書寫一個接口,如下:
@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
調用端代碼:

使用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> 這是我的包結構:

/**
* 使用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;
}
}
在地址欄訪問:

Java項目代碼調用服務:
使用純Java項目調用
1、根據客戶端生成的代碼來調用。(優選這種方式)請先生成然后在任意的Java項目中調用 。
2、客戶端只擁有一個接口,使用JaxWsProxyFactoryBean來調用。
因為以下使用了JaxWsProxyFactoryBean,所以,仍然需要CXF的環境,而使用此環境就會造成Jar文件的大量冗余,所以大家要謹慎選擇(一下的地址是隨便貼的)。

在Spring項目中,通過配置文件調用:
以下是使用Spring的配置文件調用:
新建立一個Java項目,并加載cxf的所有包。
只需要生成的接口文件.
在classpath下新建立一個ClientBeans.xml文件.
優點與缺點:
此種情況,適合于一個Javaweb項目已經集成了Spring。并希望通過CXF配置的方式調用Web服務。
此種情況,仍然需要導入CXF的大量jar包。
這種情況也存在一定優點,如可以將外部的Web服務通過配置文件注入(DI)到Action類中。
結構圖如下(帶接口的方式):

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的就到這!