【Apache CXF】CXF對JAX-WS的支持

相關dependency,我使用的版本是2.7.11:

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>${cxf.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>${cxf.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http-jetty</artifactId>
    <version>${cxf.version}</version>
</dependency>

以一個簡單的Service爲例:

import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public interface MyCxfService {
    @WebMethod
    String saySth(String content);
}

以及其實現:

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import pac.testcase.ws.MyCxfService;
public class MyCxfServiceImpl implements MyCxfService {
    public String saySth(String content) {
        return "I say "+content;
    }
}


啓動服務:

JaxWsServerFactoryBean server = new JaxWsServerFactoryBean();
server.setServiceClass(MyCxfServiceImpl.class);
server.setAddress("http://localhost:8686/ws/service");
server.create();

調用服務:

JaxWsProxyFactoryBean client = new JaxWsProxyFactoryBean();
client.setServiceClass(MyCxfService.class);
client.setAddress("http://localhost:8686/ws/service");
MyCxfService service = (MyCxfService)client.create();
System.out.println(service.saySth("nothing but performance!!"));


CXF是通過Spring爲service提供XML配置的。
需要用Servlet Listener裝載Spring後加入CXF相關的Servlet。
也就是說:

<servlet>
  <servlet-name>CXFServlet</servlet-name>
  <display-name>CXF Servlet</display-name>
  <servlet-class>
     org.apache.cxf.transport.servlet.CXFServlet
  </servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>CXFServlet</servlet-name>
    <url-pattern>/services/*</url-pattern>
</servlet-mapping>


在spring的配置文件中加入xmlns:

xmlns:jaxws="http://cxf.apache.org/jaxws"

和xsi:schemaLocation:

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

繼續用上一個例子中的Service接口,簡單做一下配置:

<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"/>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
<jaxws:endpoint implementor="pac.king.webservice.impl.MyCxfServiceImpl" address="/MyCxfService"  />

訪問:http://localhost:8080/runtrain/services/,會出現下面的效果:wKiom1NrUp2y1qB5AADWMrELLE0335.jpg


客戶端方面,可以使用jaxws:client配置讓他調用本地bean那樣簡單:

<jaxws:client id="MyCxfClient" address="http://localhost:8080/runtrain/services/MyCxfService" serviceClass="pac.king.webservice.MyCxfService" />

如果不使用則相當於:

<bean id="MyCxfClient" factory-bean="clientFactory" factory-method="create"/>
<bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean"
    p:serviceClass="pac.king.webservice.MyCxfService"
    p:address="http://localhost:8080/runtrain/services/MyCxfService"
/>

遠程調用變得透明:

ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:applicationContext*.xml");
MyCxfService client = (MyCxfService)context.getBean("MyCxfClient");
System.out.println(client.saySth("nothing but show!!"));


曾經想過一個問題,爲什麼我自己定義個User類什麼的都可以傳輸,而Map卻不可以?
天天堆砌API導致很多人把這個Map想得太簡單了,換個立場想想,XSD如何去表示Map這種東西?
JAXB(Java Architecture for XML Binding)可以解決這個問題!
簡單說來就是:
Java Architecture for XML Binding (JAXB) allows Java developers to map Java classes to XML representations. JAXB provides two main features: the ability to marshal Java objects into XML and the inverse, i.e. to unmarshal XML back into Java objects.

這裏引用一下wiki中XSD與JAXB對對照:

XML Schema TypeJava Data Type
xsd:stringjava.lang.String
xsd:integerjava.math.BigInteger
xsd:positiveIntegerjava.math.BigInteger
xsd:intint
xsd:longlong
xsd:shortshort
xsd:decimaljava.math.BigDecimal
xsd:floatfloat
xsd:doubledouble
xsd:booleanboolean
xsd:bytebyte
xsd:QNamejavax.xml.namespace.QName
xsd:dateTimejavax.xml.datatype.XMLGregorianCalendar
xsd:base64Binarybyte[]
xsd:hexBinarybyte[]
xsd:unsignedIntlong
xsd:unsignedShortint
xsd:unsignedByteshort
xsd:unsignedLongjava.math.BigDecimal
xsd:timejavax.xml.datatype.XMLGregorianCalendar
xsd:datejavax.xml.datatype.XMLGregorianCalendar
xsd:gjavax.xml.datatype.XMLGregorianCalendar
xsd:anySimpleTypejava.lang.Object
xsd:anySimpleTypejava.lang.String
xsd:durationjavax.xml.datatype.Duration
xsd:NOTATIONjavax.xml.namespace.QName

簡單記錄一下操作步驟。
首先我需要寫一個Adapter來進行marsal/unmarshal。
可以使用javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType>
簡單說來就是用前者解釋後者,引用一下javaDoc中對ValueType與BoundType的說明:

* @param <BoundType>
*      The type that JAXB doesn't know how to handle. An adapter is written
*      to allow this type to be used as an in-memory representation through
*      the <tt>ValueType</tt>.
* @param <ValueType>
*      The type that JAXB knows how to handle out of the box.


我現在試着寫一個返回Map的方法,但是我不能用java.util.Map,因爲JAXB無法處理interface。
於是我這樣定義我的服務:

import java.util.HashMap;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import pac.king.pojo.User;
import pac.king.webservice.utils.MyMapAdapter;
@WebService
public interface MyCxfService {
    @WebMethod
    @XmlJavaTypeAdapter(MyMapAdapter.class)
    public @WebResult HashMap<String,String> convertUserInfoToMap(@WebParam User user);
}

以及實現:

import java.util.HashMap;
import pac.king.pojo.User;
import pac.king.webservice.MyCxfService;
public class MyCxfServiceImpl implements MyCxfService {
    public HashMap<String, String> convertUserInfoToMap(User user) {
        HashMap<String,String> result = new HashMap<String, String>();
        result.put("name", user.getName());
        result.put("id", user.getId());
        result.put("password", user.getPassword());
        return result;
    }
}


寫User時需要提供一個沒有參數的constructor:

package pac.king.pojo;
public class User {
                                                                                                                                                                    
    private String id;
    private String name;
    private String password;
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public User() {}
    public User(String id, String name, String password) {
        super();
        this.id = id;
        this.name = name;
        this.password = password;
    }
}


注意服務方法上的註解@XmlJavaTypeAdapter(MyMapAdapter.class)。
這是繼承XmlAdapter寫的一個Adapter:

import java.util.HashMap;
import java.util.Map.Entry;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class MyMapAdapter extends XmlAdapter<MyGeneralBean[], HashMap<String,String>>{
    @Override
    public HashMap<String, String> unmarshal(MyGeneralBean[] v)
            throws Exception {
        HashMap<String,String> resultMap = new HashMap<String, String>();
        for (MyGeneralBean e : v) {
            resultMap.put(e.getKey(), e.getValue());
        }
        return resultMap;
    }
    @Override
    public MyGeneralBean[] marshal(HashMap<String, String> v) throws Exception {
        MyGeneralBean[] m = new MyGeneralBean[10];
        int i=0;
        for (Entry<String, String> entry : v.entrySet()) {
            m[++i] = new MyGeneralBean(entry.getKey(), entry.getValue());
        }
        return m;
    }
                                                                                                                     
}


MyGeneralBean是用來解釋Map結構的一個簡單類型,這個也需要提供一個無參數的constructor:

public class MyGeneralBean {
    private String key;
    private String value;
    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
    public String getValue() {
        return value;
    }
    public void setValue(String value) {
        this.value = value;
    }
    public MyGeneralBean() {}
    public MyGeneralBean(String key, String value) {
        super();
        this.key = key;
        this.value = value;
    }
                                                 
}


這樣就可以調用了,繼續使用上一個例子中的jaxws:client配置,直接使用服務。

ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:applicationContext*.xml");
        MyCxfService client = (MyCxfService) context.getBean("MyCxfClient");
        Map<String,String> map = client.convertUserInfoToMap(new User("100001","King.","t;stmdtkg"));
        System.out.println(map.get("id"));
        System.out.println(map.get("name"));
        System.out.println(map.get("password"));

輸出:
wKiom1Nsi6KTRpReAAAdIzI3FqQ464.jpg


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章