引入:
雖然已經用了Apache CXF一段時間了,但是畢竟隻是在項目中運用其部分能力,沒有系統的學習,其實CXF還有許多強大的功能,這裡我準備用一些文章系統的介紹Apache CXF的各個特征。
例子介紹:
示範用Apache CXF對JAX-WS的支援來建立“code first”的web service.
實踐
首先我們進行架構設計,我們假設按照傳統慣例,搭建一個maven應用,是以它的pom.xml應該如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.charles.cxfstudy</groupId>
<artifactId>cxf_jaxws_server</artifactId>
<packaging>war</packaging>
<name>CXF demo using JAX-WS APIs</name>
<description>>CXF demo using JAX-WS APIs</description>
<version>1.0.0</version>
<properties>
<cxf.version>${project.version}</cxf.version>
<cxf.release.base>${basedir}/../..</cxf.release.base>
<spring.version>3.0.7.RELEASE</spring.version>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.1</version>
<configuration>
<webXml>src/main/webapp/WEB-INF/web.xml</webXml>
</configuration>
</plugin>
</plugins>
<finalName>cxf_jaxws_server</finalName>
</build>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>2.7.10</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>2.7.10</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
這裡沒什麼技術含量,主要就是添加一些Apache CXF的依賴的jar包的支援,當然了,我們還考慮到用了spring web(接下來會講),是以也添加了對spring web的支援。
因為是web應用,是以我們去編輯web.xml,這裡特别要注意的是,我們的Apache CXF架構的入口Servlet是CXFServlet,它是一個基于Spring架構的Servlet,它符合攔截和轉發web service的請求,并且調用業務方法進行服務,按照國際慣例,我們還必須為其配置url-pattern,進而讓系統知道它會攔截何種請求。(我們這裡設為攔截所有 /services/開頭的請求):
<?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">
<display-name>cxf demo for "code first" webservice</display-name>
<servlet>
<description>Apache CXF Endpoint</description>
<servlet-name>cxf-endpoint</servlet-name>
<!-- CXFServlet 用于攔截和轉發web service 請求-->
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>cxf-endpoint</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>60</session-timeout>
</session-config>
</web-app>
下面我們要寫一個spring 配置檔案,叫WEB-INF/cxf-servlet.xml,為什麼要寫這個檔案呢?我們來看下Apache CXF的核心Servlet ,CXFServlet的實作:
public class CXFServlet extends CXFNonSpringServlet
implements ApplicationListener<ContextRefreshedEvent> {
private static final long serialVersionUID = -5922443981969455305L;
private static final String BUS_PARAMETER = "bus";
private boolean busCreated;
private XmlWebApplicationContext createdContext;
public CXFServlet() {
}
@Override
protected void loadBus(ServletConfig servletConfig) {
ApplicationContext wac = WebApplicationContextUtils.
getWebApplicationContext(servletConfig.getServletContext());
if (wac instanceof AbstractApplicationContext) {
addListener((AbstractApplicationContext)wac);
}
String configLocation = servletConfig.getInitParameter("config-location");
if (configLocation == null) {
try {
InputStream is = servletConfig.getServletContext().getResourceAsStream("/WEB-INF/cxf-servlet.xml");
if (is != null && is.available() > 0) {
is.close();
configLocation = "/WEB-INF/cxf-servlet.xml";
}
} catch (Exception ex) {
//ignore
}
}
if (configLocation != null) {
wac = createSpringContext(wac, servletConfig, configLocation);
}
可以看出,它會去讀取config-location的配置檔案路徑,預設為/WEB-INF/cxf-servlet.xml,進而建立Spring的上下文。是以我們可以想象,這個cxf-servlet.xml(或者其他名字的配置檔案)肯定是配置了關于web服務的定義 ,并且将這些服務定義為spring的bean,如下:
<?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">
<jaxws:server id="cxfJaxwsService" serviceClass="com.charles.cxfstudy.server.services.IGreetingService" address="/greeting">
<jaxws:serviceBean>
<bean class="com.charles.cxfstudy.server.services.GreetingServiceImpl" />
</jaxws:serviceBean>
</jaxws:server>
</beans>
是以我們這裡就用jaxws的名字空間來聲明提供服務的服務全限定接口,服務的位址和服務的實作全限定類,是以一旦web service部署在spring容器中,就可以為外界提供服務了。
接下來就是編碼工作,我們必須讓我們的代碼和我們在cxf-servlet.xml中的配置一樣。是以,我們定義了IGreetingService的接口:
/**
* 這是 web service的接口
*/
package com.charles.cxfstudy.server.services;
import javax.jws.WebService;
import com.charles.cxfstudy.server.vo.Person;
/**
* @author charles.wang
*
*/
@WebService
public interface IGreetingService {
/**
* 對某個Person發起問候
*/
String sayGreetingToPerson(Person person);
}
并且在這個接口中提供業務方法。因為我們的服務是Web服務,是以必須用@WebService注解将其标示。 我們的接口中可以出現非java内定類型的類,比如自定義類(這裡的Person類),他們會被JAXB架構(Apache CXF預設支援的綁定架構)來轉為對應的xml類型定義。
是以我們的 Person 類就如下:
/**
* 這個一個VO,我們定義了一個Person類型,接下來,我們會用JAXB架構将其映射為wsdl檔案中的類型定義
*/
package com.charles.cxfstudy.server.vo;
import javax.xml.bind.annotation.XmlType;
/**
* 我們定義一個有姓名(name)和年齡(age)的Person類
* @author charles.wang
*
*/
@XmlType(name="person")
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
可以看出,這個Person類和一般的Bean沒有差別,唯一差別在于我們用了注解@XmlType,它會被JAXB識别并且把這個類轉為對應的xml格式的類型定義。
接下來我們就來編寫服務實作bean了,根據我們在cxf-servlet.xml中的定義,我們開發了GreetingServiceImpl的實作類:
/**
* 這是web service的實作類
*/
package com.charles.cxfstudy.server.services;
import javax.jws.WebService;
import com.charles.cxfstudy.server.vo.Person;
/**
* @author charles.wang
*
*/
@WebService (endpointInterface="com.charles.cxfstudy.server.services.IGreetingService", serviceName="GreetingService")
public class GreetingServiceImpl implements IGreetingService {
/**
* 對某個Person發起問候
*/
public String sayGreetingToPerson(Person person) {
System.out.println("calling SayGreetingToPerson(Person) method");
String name = person.getName();
int age = person.getAge();
return "Hello ,this is greeting from Charles to: "+name+", and his age is: "+age;
}
}
從這裡看出,它也和一般具體類沒差別,就是多了一個@WebService注解來表示自己是一個服務實作類。這裡看到其中還有endpointInterface和serviceName屬性,他們都會映射到最終的wsdl檔案。
開發完了之後,就足夠了(因為我們的邏輯太簡單了),我們maven建構war包,然後部署在tomcat容器上(或者其他web容器), 就可以通過URL來測試我們的應用了。從伺服器日志可以清楚的看到釋出Web服務的過程:
比如通路http://localhost:8080/cxf_jaxws_server/services/greeting?wsdl (因為/services請求會被CXFServlet攔截作為web service請求, /greeting是我們開發的web service的具體請求,定義在cxf-servlet.xml中)