转:http://blog.csdn.net/wang379275614/article/details/47749203
这里首先做一个用CXF开发WebService的实例。然后介绍WebService的调用本质与权限控制。
一、 实例
1.1 下载apache-cxf
地址如下:http://cxf.apache.org/download.html,要下载稳定版。
1.2 使用CXF开发WebService服务端
每个WebService组件需要2个部分,接口和实现类。开发一个WebService组件,一般需要三个步骤:
Ø 开发一个WebService业务接口,该接口要用@WebService来修饰。
Ø 开发一个WebService实现类,实现类也需要用@WebService来修饰。
Ø 使用Endpoint类的静态方法来发布WebService。
具体步骤如下:
首先,新建JavaProject:01_WS_Server,即WebService的服务端,并导入需要的jar包:
开发业务接口interface HelloWorld:
- package com.tgb.cxf.ws.interfaces;
- import javax.jws.WebService;
- /**
- * 业务接口,要用@WebService来修饰
- *
- * @author wangzhipeng
- *
- */
- @WebService
- public interface HelloWorld {
- public void SayHi(String name);
- }
package com.tgb.cxf.ws.interfaces;
import javax.jws.WebService;
/**
* 业务接口,要用@WebService来修饰
*
* @author wangzhipeng
*
*/
@WebService
public interface HelloWorld {
public void SayHi(String name);
}
开发业务实现class HelloWorldWs implements HelloWorld:
- package com.tgb.cxf.ws;
- import javax.jws.WebService;
- import com.tgb.cxf.ws.interfaces.HelloWorld;
- /**
- * 业务实现,也要用@WebService来修饰
- *
- * @author wangzhipeng
- *
- */
- @WebService(endpointInterface = "com.tgb.cxf.ws.interfaces.HelloWorld", serviceName = "HelloWorldWs")
- public class HelloWorldWs implements HelloWorld {
- @Override
- public void SayHi(String name) {
- System.out.println("hello " + name);
- }
- }
package com.tgb.cxf.ws;
import javax.jws.WebService;
import com.tgb.cxf.ws.interfaces.HelloWorld;
/**
* 业务实现,也要用@WebService来修饰
*
* @author wangzhipeng
*
*/
@WebService(endpointInterface = "com.tgb.cxf.ws.interfaces.HelloWorld", serviceName = "HelloWorldWs")
public class HelloWorldWs implements HelloWorld {
@Override
public void SayHi(String name) {
System.out.println("hello " + name);
}
}
使用Endpoint类的静态方法来发布WebService:
- package com.tgb.cxf.ws.publishes;
- import javax.xml.ws.Endpoint;
- import com.tgb.cxf.ws.HelloWorldWs;
- import com.tgb.cxf.ws.interfaces.HelloWorld;
- /**
- *使用Endpoint类的静态方法来发布WebService
- *
- * @author wangzhipeng
- *
- */
- public class ServerMain {
- public static void main(String[] args) {
- HelloWorld helloWorld = new HelloWorldWs();
- // 调用Endpoint的publish方法来发布Web Service
- Endpoint.publish("http://192.168.24.144/helloworld", helloWorld);
- System.out.println("Web service 发布成功!!");
- }
- }
package com.tgb.cxf.ws.publishes;
import javax.xml.ws.Endpoint;
import com.tgb.cxf.ws.HelloWorldWs;
import com.tgb.cxf.ws.interfaces.HelloWorld;
/**
*使用Endpoint类的静态方法来发布WebService
*
* @author wangzhipeng
*
*/
public class ServerMain {
public static void main(String[] args) {
HelloWorld helloWorld = new HelloWorldWs();
// 调用Endpoint的publish方法来发布Web Service
Endpoint.publish("http://192.168.24.144/helloworld", helloWorld);
System.out.println("Web service 发布成功!!");
}
}
查看WebService是否发布成功(出现如下页面表示发布成功):
1.3 使用CXF开发WebService客户端
开发WebService客户端一般需要如下几个步骤:
首先我们新建JavaProject:01_WS_Client,作为WebService的客户端(新建WsClient类用来调用服务端):
调用CXF提供的wsdl2java工具(如下图)根据wsdl文档(发布成功的地址),生成相应的java代码。wsdl,即web service definition language。任何语言实现了WebService,都需要提供并暴露WSDL文档。
首先将Apache-cxf的bin目录加入到环境变量Path中,方便后面使用wsdl2java工具。
然后打开cmd命令窗口,并进入到01_WS_Client项目的src目录下,如下图:
然后,执行命令wsdl2java http://192.168.24.144/helloworld?wsdl(为我们发布的服务端的访问地址)
这样,即可生成服务端代码到客户端项目中,刷新我们的客户端项目就可看到:
然后,在我们上面新建的WsClient类中写如下代码即可调用WebService的服务端:
- package com.tgb.ws.client;
- import com.tgb.cxf.ws.HelloWorldWs;
- import com.tgb.cxf.ws.interfaces.HelloWorld;
- public class WsClient {
- public static void main(String[] args) {
- // HelloWorldWs为生成的com.tgb.cxf.ws包下的继承了Service的类
- HelloWorldWs factory = new HelloWorldWs();
- // 此处返回的只是远程WebService的【代理】
- HelloWorld helloWorld = factory.getHelloWorldWsPort();
- helloWorld.sayHi("wangzhipeng");// 输出:hello wangzhipeng,表示调用WebService成功
- }
- }
package com.tgb.ws.client;
import com.tgb.cxf.ws.HelloWorldWs;
import com.tgb.cxf.ws.interfaces.HelloWorld;
public class WsClient {
public static void main(String[] args) {
// HelloWorldWs为生成的com.tgb.cxf.ws包下的继承了Service的类
HelloWorldWs factory = new HelloWorldWs();
// 此处返回的只是远程WebService的【代理】
HelloWorld helloWorld = factory.getHelloWorldWsPort();
helloWorld.sayHi("wangzhipeng");// 输出:hello wangzhipeng,表示调用WebService成功
}
}
这里我们需要找到 wsdl2java所生成的服务端类中,一个继承了Service的类,该类可当做工厂来使用,利用该类的getXXXPort方法可以返回WebService的代理。实例就到此结束。
二、 调用WebService的本质原理
一次WebService调用其实并不是方法调用,而是发送SOAP消息,即xml文档片段。调用详细过程如下:
1、客户端将调用方法、参数,转换生成xml文档片段(SOAP消息,input消息),该文档片段必须符合wsdl定义的格式。
2、客户端通过网络将生成的xml文档片段传给服务器。
3、服务器接受到客户端发来的xml文档片段。
4、服务器解析xml文档片段,提取其中的数据,并将数据转换为调用WebService所需要的参数值。
5、服务器执行方法。
6、服务器将方法的执行结果再次转换为xml文档片段(SOAP消息,output消息),该文档片段必须符合wsdl定义的格式。
7、服务端通过网络将执行结果的xml文档片段通过网络发送给客户端。
8、客户端接收到执行结果的xml文档片段。
9、客户端解析执行结果的xml文档片段,提取其中的数据,并将数据转换为调用WebService的返回值。
从上面的调用本质上来看,方法的执行是在服务端,客户端只做发送xml、接收xml、解析xml。所以,一种语言支持WebService唯一的要求就是:该语言支持xml文档的解析、生成、网络传输。为什么WebService离不开xml呢?WebService的三个基础如下:
1、WSDL:Web Service Definition Language——WebService定义语言
2、SOAP:Simple Object Access Protocol——简单对象访问协议
3、UDDI:Universal Description Discovery and Integration——通用描述、发现与集成服务,是一种目录服务
其中的WSDL与SOAP都为xml,所以WebService离不开xml。
三、 CXF类型问题
发布的WebService的方法,当形参、返回值类型是String、基本数据类型时,CXF肯定可以轻松地处理,例如:public void SayHi(String name);
当形参、返回值类型是JavaBean式的复合类、List集合、数组等时,CXF也可以很好的处理,例如:public List<Cat> queryCatsByUser(User user);
还有一些像Map、非JavaBean式的复合类,CXF是处理不了的,例如:public Map<String, Cat> getAllCats();此时会报异常:javax.xml.ws.WebServiceException:org.apache.cxf.service.factory.ServiceConstructionException。
对于处理不了的类型,例如:Map<String, Cat>就需要我们手动写工具类将它转换为CXF可以处理的类型,例如我们可以将它转换为如下类型:
- package com.tgb.cxf.ws.utils;
- import java.util.List;
- import com.tgb.cxf.domain.Cat;
- /**
- * 将cxf不能处理的类型Map<String, Cat>转换为如下cxf可以处理的类型StringCat
- * @author wangzhipeng
- *
- */
- public class StringCat {
- public static class Entry {
- private String key;
- private Cat value;
- public Entry() {
- };
- public Entry(String key, Cat cat) {
- this.key = key;
- this.value = cat;
- }
- 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;
- }
- }
- private List<Entry> entries;
- public List<Entry> getEntries() {
- return entries;
- }
- public void setEntries(List<Entry> entries) {
- this.entries = entries;
- }
- }
package com.tgb.cxf.ws.utils;
import java.util.List;
import com.tgb.cxf.domain.Cat;
/**
* 将cxf不能处理的类型Map<String, Cat>转换为如下cxf可以处理的类型StringCat
* @author wangzhipeng
*
*/
public class StringCat {
public static class Entry {
private String key;
private Cat value;
public Entry() {
};
public Entry(String key, Cat cat) {
this.key = key;
this.value = cat;
}
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;
}
}
private List<Entry> entries;
public List<Entry> getEntries() {
return entries;
}
public void setEntries(List<Entry> entries) {
this.entries = entries;
}
}
转换类如下:
- package com.tgb.cxf.ws.utils;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import javax.xml.bind.annotation.adapters.XmlAdapter;
- import com.tgb.cxf.domain.Cat;
- import com.tgb.cxf.ws.utils.StringCat.Entry;
- /**
- * CXF类型转换类Map<String, Cat>——StringCat
- *
- * @author wangzhipeng
- *
- */
- public class FkXmlAdapter extends XmlAdapter<StringCat, Map<String, Cat>> {
- @Override
- public StringCat marshal(Map<String, Cat> v) throws Exception {
- StringCat tStringCat = new StringCat();
- for (String key : v.keySet()) {
- tStringCat.getEntries().add(new Entry(key, v.get(key)));
- }
- return tStringCat;
- }
- @Override
- public Map<String, Cat> unmarshal(StringCat v) throws Exception {
- Map<String, Cat> catMap = new HashMap<String, Cat>();
- List<Entry> entryLists = v.getEntries();//
- for (Entry entry : entryLists) {
- catMap.put(entry.getKey(), entry.getValue());
- }
- return catMap;
- }
- }
package com.tgb.cxf.ws.utils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import com.tgb.cxf.domain.Cat;
import com.tgb.cxf.ws.utils.StringCat.Entry;
/**
* CXF类型转换类Map<String, Cat>——StringCat
*
* @author wangzhipeng
*
*/
public class FkXmlAdapter extends XmlAdapter<StringCat, Map<String, Cat>> {
@Override
public StringCat marshal(Map<String, Cat> v) throws Exception {
StringCat tStringCat = new StringCat();
for (String key : v.keySet()) {
tStringCat.getEntries().add(new Entry(key, v.get(key)));
}
return tStringCat;
}
@Override
public Map<String, Cat> unmarshal(StringCat v) throws Exception {
Map<String, Cat> catMap = new HashMap<String, Cat>();
List<Entry> entryLists = v.getEntries();//
for (Entry entry : entryLists) {
catMap.put(entry.getKey(), entry.getValue());
}
return catMap;
}
}
然后需要我们在WebService的接口的相应的方法上加@XmlJavaTypeAdapter(转换类.class),其它保持不变即可:
- package com.tgb.cxf.ws.interfaces;
- import java.util.List;
- import java.util.Map;
- import javax.jws.WebService;
- import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
- import com.tgb.cxf.domain.Cat;
- import com.tgb.cxf.domain.User;
- import com.tgb.cxf.ws.utils.FkXmlAdapter;
- @WebService
- public interface HelloWorld {
- public void SayHi(String name);
- public List<Cat> queryCatsByUser(User user);
- // CXF不能处理Map<String,Cat>类型,于是我们采用FkXmlAdapter进行处理
- public @XmlJavaTypeAdapter(FkXmlAdapter.class)
- Map<String, Cat> getAllCats();
- }
package com.tgb.cxf.ws.interfaces;
import java.util.List;
import java.util.Map;
import javax.jws.WebService;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.tgb.cxf.domain.Cat;
import com.tgb.cxf.domain.User;
import com.tgb.cxf.ws.utils.FkXmlAdapter;
@WebService
public interface HelloWorld {
public void SayHi(String name);
public List<Cat> queryCatsByUser(User user);
// CXF不能处理Map<String,Cat>类型,于是我们采用FkXmlAdapter进行处理
public @XmlJavaTypeAdapter(FkXmlAdapter.class)
Map<String, Cat> getAllCats();
}
四、 权限控制
上面发布的WebService服务,只要我们拿到访问地址就都可以随意调用,这样显然是不合适的。所以我们还需要进行权限验证。WebService权限控制的思路可以如下:
服务端要求input消息总是携带有用户名、密码信息,如果没有携带该信息,或携带信息不正确,那么直接拒绝调用。我们可以用CXF提供的拦截器来实现此效果。就是为了程序员能够访问、并修改CXF框架所生成的SOAP消息,CXF才提供了拦截器。我们可以在客户端添加out拦截器,截获SOAP消息,并添加上用户名、密码信息,然后再发给服务端;于是我们在服务端添加in拦截器,来截获SOAP消息,进而验证用户名、密码信息;
拦截结构如下图: