闲来无事,根据教学视频学下WS,做下简单的记录…
现行的 WS主要有 SOAP XML 、axis2、xfire、CXF 等…
不管是哪种接口,哪种平台基于哪种语言开发的WS 都严格按照wsdl标准和soap协议。
以下的学习 主要都是基于注解的形式开发,jdk1.6,开发工具eclipse
一、第一个简单的示例
首先创建一个WS项目。
代码:
Java代码- //定义接口
- @WebService(targetNamespace="com.trylin.ws.service")
- public interface ITest {
- public int add(int a,int b);
- public int minus(int a,int b);
- }
- //定义实现类
- @WebService(endpointInterface="com.trylin.ws.service.ITest")
- public class TestImpl implements ITest{
- public int add(int a, int b) {
- return a+b;
- }
- public int minus(int a, int b) {
- return a-b;
- }
- }
- //开启服务
publicstaticvoid main(String[] args) {
String address = "http://localhost:8888/WS";
Endpoint.publish(address, new TestImpl());
}
验证服务
打开浏览器,输入http://localhost:8888/WS?wsdl 显示发布服务的wsdl
代码解释
上述代码中实现了一个简单的WS的发布。
首先定义一个接口和实现类 ITest,TestImpl
然后加入@WebService 注解。
targetNamespace 属性表示发布后生成WSDL文件的命名空间(默认值:当前类的包路径),对应namespace
<import namespace="http://com.trylin.ws.service/" location="http://localhost:8888/WS?wsdl=1"/>
这里有个小细节,注意到这个namespace是不是觉得有点别扭呢,先想想包命名原则,下面生成客户端调用的时候,简单再说明一下。
endpointInterface 属性表示实现的接口
Endpoint.publish(address, new TestImpl());
根据地址 和实现类,将接口发布。
发布后的WS地址就是 address ,可以直接访问,在address后加入 ?wsdl 可以查看接口发布后的wsdl。
客户端调用
上面就算是发布了一个简单的WS,简单吧,然后客户端怎么调用呢?
这里就用到了jdk 1.6自带的 wsimport命令
打开cmd命令窗口 ,输入 wsimport, 如图:(ps:请保证在你运行电脑的jdk环境变量设置正确,没有jdk配置环境变量,百度下)
简单的介绍几个主要的命令
-d 指明生成WS客户端文件本地存放目录
-verbose 查看生成的详细信息(生成日志)
-p 指定ws客户端生成文件的包名(默认按照 @webService定义的命名空间)
-keep 是否生成源文件(加 表示生成的文件中 包含java和 class ,不加只有class)
创建本地客户端存放目录
D:/webservice/test
(记得开服务哦运行上面的main)
在cmd中 输入 wsimport -d D:\webservice\test -keep -verbose http://localhost:8888/WS?wsdl
generating code... 表示创建的java文件
compiling code... 表示编译java文件
上面不是说过 wsdl上的命名空间有点问题,这下看日志有看出来吗?哈哈,应该有发现吧,生成的java源文件的目录是不是和我们在服务端的源文件目录刚好颠倒了呢。我也是看到这里才发现这个问题的。简单的说下我个人对这个问题的想法吧。
Java关于包,有个简单的命名原则,就是按照域名反向命名,比如 baidu.com 包名就叫com.baidu。我们生成的客户端文件是根据wsdl生成的,包名是根据wsdl中的namespace定义,只不过会将namespace视为一个域名,然后反向生成客户端类的包名。我在第一个示例中的接口中定义了@WebService(targetNamespace="com.trylin.ws.service") 按照反向,生成的包名就是service.ws.trylin.com 然后就出现了我们刚才的客户端文件和服务端文件包名相反的现象了。
这个只是我个人的一个凭空猜想,没什么依据可言,呵呵。
OK,按照我上面的理解,我把命名空间 反向定义 service.ws.trylin.com
重新生成客户端。
然后再创建一个新的clinet项目,把生成的客户端复制到项目中,创建客户端测试类。
- /**
- * @ClassName: ITestCase
- * @Description:
- * @author Try_Lin
- * @date 2013-7-15 下午11:45:06
- *
- */
- public class ITestCase {
- /**
- * 根据java提供的ws调用
- * @throws Exception
- */
- @Test
- public void testClient1() throws Exception{
- URL url = new URL("http://localhost:8888/WS?wsdl"); //wsdl路径
- //可以从 产生的wsdl中definitions 节点属性中 找到 targetNamespace 和 serviceName
- String targetNamespace = "http://impl.service.ws.trylin.com/";//WS指向命名空间
- String serviceName = "TestImplService";//WS发布服务名称
- QName qname = new QName(targetNamespace, serviceName);
- Service service = Service.create(url,qname);
- ITest test = service.getPort(ITest.class);
- System.out.println(test.add(1, 2));
- }
- /**
- * 根据wsdl生成的客户端调用的
- */
- @Test
- public void testClient2(){
- TestImplService service = new TestImplService();
- ITest test = service.getTestImplPort();
- System.out.println(test.add(3, 2));
- }
- }
两种调用方式都可以,只是第二种方式看上去更加简洁。
可以看下 第二种方式的 TestImplService代码 ,其实只是对第一种方式的部分封装。
Java代码- public TestImplService() {
- super(TESTIMPLSERVICE_WSDL_LOCATION, new QName("http://impl.service.ws.trylin.com/", "TestImplService"));
- }
- /**
- *
- * @return
- * returns ITest
- */
- @WebEndpoint(name = "TestImplPort")
- public ITest getTestImplPort() {
- return super.getPort(new QName("http://impl.service.ws.trylin.com/", "TestImplPort"), ITest.class);
- }
嗯 这样一个简单的WS就实现了
WSDL和SOAP相关,待续…
我也是看教学视频,边看边琢磨然后写下,方便以后自己忘记的时候,可以参考下。
如果有大神看到这里,还希望指点下错误的地方,或者提供下学习建议。