首先定义一个接口如下:
import java.util.Map;
import javax.jws.WebService;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@WebService
public interface HelloCXFServer {
public String sayHi(String name);
//@XmlJavaTypeAdapterMap使用该注解需要转换器
@XmlJavaTypeAdapter(FKXmlAdapter.class) Map<String,Cat> getAllCats();
}
编写实现类如下:
import java.util.HashMap;
import java.util.Map;
import javax.jws.WebService;
import com.cxf.server.Cat;
import com.cxf.server.HelloCXFServer;
@WebService(serviceName="helloCXF",endpointInterface="com.cxf.server.HelloCXFServer")
public class HelloCXFServerImpl implements HelloCXFServer{
@Override
public String sayHi(String name) {
return name+"你好!";
}
@Override
public Map<String, Cat> getAllCats() {
Map<String,Cat> cats=new HashMap<String,Cat>();
cats.put("cat1", new Cat("cat1","red"));
cats.put("cat2", new Cat("cat2","blue"));
return cats;
}
}
处理webservice无法转换的数据类型:
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import com.cxf.server.StringCat.Entry;
//第二个泛型为无法处理的类型
//第一个为能够处理的类型
//改转换器负责完成类型转换
public class FKXmlAdapter extends XmlAdapter<StringCat, Map<String,Cat>>{
@Override
public Map<String, Cat> unmarshal(StringCat v) throws Exception {
Map<String, Cat> map=new HashMap<String, Cat>();
List<Entry> entries = v.getEntries();
for (Entry entry : entries) {
map.put(entry.getKey(), entry.getValue());
}
return map;
}
@Override
public StringCat marshal(Map<String, Cat> v) throws Exception {
StringCat stringCat=new StringCat();
Iterator<String> it=v.keySet().iterator();
while(it.hasNext()){
String key=it.next();
Cat value=v.get(key);
stringCat.getEntries().add(new Entry(key,value));
}
return stringCat;
}
}
public class Cat {
private String name;
private String color;
public Cat() {
super();
}
public Cat(String name, String color) {
super();
this.name = name;
this.color = color;
}
<span style="white-space:pre"> </span>...
}
定义一个封装map的类如下:
import java.util.ArrayList;
import java.util.List;
public class StringCat {
private List<Entry> entries=new ArrayList<Entry>();
public List<Entry> getEntries() {
return entries;
}
public void setEntries(List<Entry> entries) {
this.entries = entries;
}
public static class Entry{
private String key;
private Cat value;
public Entry() {
}
public Entry(String key, Cat value) {
this.key=key;
this.value=value;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Cat getValue() {
return value;
}
public void setValue(Cat value) {
this.value = value;
}
}
}
服务器端邀请客户端传入用户名和密码认证代码如下:
import java.util.List;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public class AuthInterceptro extends AbstractPhaseInterceptor<SoapMessage> {
public AuthInterceptro() {
super(Phase.PRE_INVOKE);//改拦截器会在调用之前拦截soap消息
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
//得到soap消息的所有header
List<Header> headers=message.getHeaders();
if(headers==null||headers.isEmpty()){
throw new Fault(new IllegalArgumentException("没有header不能调用..."));
}
//假如邀请第一个header携带用户名和密码信息
Header firstHeader=headers.get(0);
Element ele=(Element)firstHeader.getObject();
NodeList userIds=ele.getElementsByTagName("userId");
NodeList userPasss=ele.getElementsByTagName("userPass");
if(userIds.getLength()!=1){
throw new Fault(new IllegalArgumentException("用户名格式不对..."));
}
if(userPasss.getLength()!=1){
throw new Fault(new IllegalArgumentException("密码格式不对..."));
}
String userId=userIds.item(0).getTextContent();
String userPass=userPasss.item(0).getTextContent();
//检查数据中用户名密码是否被授权
if(!userId.equals("chenjihong")||!userPass.equals("chen1989CHEN")){
throw new Fault(new IllegalArgumentException("用户名密码不正确..."));
}
}
}
接下来发布webservice,这里有两种方式:
第一种代码如下:
import javax.xml.ws.Endpoint;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.jaxws.EndpointImpl;
import com.cxf.server.HelloCXFServer;
import com.cxf.server.impl.HelloCXFServerImpl;
public class ServerPublish {
public static void main(String[] args) {
HelloCXFServer implementor=new HelloCXFServerImpl();
EndpointImpl ep = (EndpointImpl)Endpoint.create(implementor);
ep.publish("http://localhost:8080/cxfServer");
//这里使用in拦截器,即为每次请求进入服务器之前进行拦截
ep.getInInterceptors().add(new AuthInterceptro());
ep.getInInterceptors().add(new LoggingInInterceptor());//cxf自带的日志拦截器
}
}
第二中在web应用中部署,这里我们采用使用了spring的web应用中部署:
首先在web.xml中配置spring的文件信息和cxf的请求处理信息如下:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value> /WEB-INF/config/applicationContext.xml</param-value>
</context-param>
<!-- cxf的处理servlet -->
<servlet>
<servlet-name>cxf</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>cxf</servlet-name>
<url-pattern>/cxfService/*</url-pattern>
</servlet-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<!-- 对应发布接口的实现类 -->
<bean id="helloService" class="com.cxf.server.impl.HelloCXFServerImpl" />
<!-- #helloService应用了上面bean的依赖注入 -->
<jaxws:endpoint id="helloWorld" implementor="#helloService"
address="/HelloWorld" >
<jaxws:inInterceptors>
<bean class="com.cxf.server.AuthInterceptro"/>
<bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
</jaxws:inInterceptors>
</jaxws:endpoint>
</beans>
在浏览器中输入对应的发布地址即可访问wsdl文档。
客户端使用的两种方式:
可以先使用cxf的wsdl2java wsdl地址来生成客户端代码,之后
第一种代码如下:
import java.util.List;
import javax.xml.namespace.QName;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
//客户端拦截器 改拦截器会自动在soap消息发送的时候调用,并且在头部生成一个认证xml片段插入到soap头部中
public class AuthHeaderInterceptor extends AbstractPhaseInterceptor<SoapMessage>{
private String username;
private String password;
public AuthHeaderInterceptor(String username,String password) {
super(Phase.PREPARE_SEND);//准备发送soap消息时调用拦截器
this.username=username;
this.password=password;
}
@Override
public void handleMessage(SoapMessage message) throws Fault {
List<Header> headers=message.getHeaders();//soap头
Document doc=DOMUtils.createDocument();//创建xml文档
Element ele=doc.createElement("authHeader");
//此处的元素按照服务器要求
Element userIdEle=doc.createElement("userId");
userIdEle.setTextContent(username);
Element userPassEle=doc.createElement("userPass");
userPassEle.setTextContent(password);
ele.appendChild(userIdEle);
ele.appendChild(userPassEle);
QName qname=new QName("test");
Header header=new Header(qname, ele);
headers.add(header);//将其添加到头集合中
}
}
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import com.cxf.server.Entry;
import com.cxf.server.HelloCXFServer;
import com.cxf.server.StringCat;
import com.cxf.server.impl.HelloCXF;
public class CXFClient {
public static void main(String[] args) {
HelloCXF client=new HelloCXF();
HelloCXFServer server=client.getHelloCXFServerImplPort();
Client c=ClientProxy.getClient(server);
//添加out拦截器 为客户端在发送消息的时候的拦截器
c.getOutInterceptors().add(new AuthHeaderInterceptor("chenjihong","chen1989CHEN"));
StringCat sc=server.getAllCats();
List<Entry> ls=sc.getEntries();
for (Entry entry : ls) {
System.out.println(entry.getKey()+"[cat="+entry.getValue().getName()+","+entry.getValue().getColor()+"]");
}
System.out.println(server.sayHi("chenjihong"));
}
}
第二种为web应用中集成了spring的整合,认证类都为同一个:
只需要在spring的配置文件中配置如下信息即可:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<!-- 对应的接口类 通过<span style="font-family: Arial, Helvetica, sans-serif;">helloService即可得到对应的实例调用服务器端接口方法</span><span style="font-family: Arial, Helvetica, sans-serif;"> --></span>
<jaxws:client id="helloService"
serviceClass="com.cxf.server.HelloCXFServer"
address="http://localhost:8080/cxf_spring/cxfService/HelloWorld"
>
<jaxws:outInterceptors>
<bean class="com.cxf.server.AuthHeaderInterceptor">
<constructor-arg value="xxxx"/>
<constructor-arg value="xxxxx"/>
</bean>
</jaxws:outInterceptors>
</jaxws:client>
</beans>
调用代码为:
public class ClientTest {
private ApplicationContext ap;
@Before
public void init(){
ap=new ClassPathXmlApplicationContext("file:D:/soft/common/workspace/cxf_client_spring/WebContent/WEB-INF/config/applicationContext.xml");
}
@Test
public void test1(){
HelloCXFServer service=ap.getBean("helloService", HelloCXFServer.class);
StringCat sc=service.getAllCats();
List<Entry> es=sc.getEntries();
for (Entry entry : es) {
System.out.println(entry.getKey()+"="+entry.getValue().getName());
}
}
}
下面是一组加了头的soap消息:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<authHeader>
<userId>xxxx</userId>
<userPass>xxxxx</userPass>
</authHeader>
</soap:Header>
<soap:Body>
<ns2:sayHi xmlns:ns2="http://server.cxf.com/">
<arg0>chenjihong</arg0>
</ns2:sayHi>
</soap:Body>
</soap:Envelope>