WebService知识整理

WebService学习笔记

友情提示,本文档的图床使用极简图床进行图片存储,默认存储到七牛云空间

本学习贴是根据blog.java1234.com.cn的webservice视频教程整理和总结的,多处引用了Java1234_小锋老师的原文,感谢小峰老师的开源精神.致敬!!

前言

webservice是一种跨平台跨语言的规范,用于不同平台,不同语言开发的应用之间的交互。

开发人员一般就是在具体平台开发webservice接口,以及调用webservice接口;每种开发语言都有自己的webservice实现框架。比如Java 就有 Apache Axis1Apache Axis2Codehaus XFireApache CXFApache WinkJboss RESTEasyd等等...

第一节: 使用CXF开发WebService服务端接口

一.使用传统的jdk实现webservice功能

1.1 **新建maven project 填入项目名称 **

选择第一个 构建maven管理项目 而不是生成具体的java文件(只有目录)

使用的packeage为 jar (传统的project)

项目 结构如下:

本章节先展示如何使用javaee原生的jar开发webservice功能

1.2 更换j2ee的版本 使用maven生成的默认使用j2ee1.5 这里使用webservice功能需要使用1.7及以上

1.3 新建接口

1.4 编辑接口

package com.ztgeo.webservice;

import javax.jws.WebService;

@WebService //使用webservice进行声明类
public interface HelloWorld {
    
    public String say(String str);
}

1.5 新建并编辑实现类

package com.ztgeo.webservice.impl;

import javax.jws.WebService;

import com.ztgeo.webservice.HelloWorld;

@WebService//同样适用webservice进行注解
public class HelloWorldImpl implements HelloWorld {

    public String say(String str) {//接口发布后由对接方出入参数
        return "对接方传过来的参数是:"+str;
    }

}

1.6 编写主类service进行暴露接口(这里暂时在impl包中书写)
package com.ztgeo.webservice.impl;

import javax.xml.ws.Endpoint;

import com.ztgeo.webservice.HelloWorld;

public class Service {
    
    public static void main(String[] args) {
        System.out.println("webservice开始工作!!");
        HelloWorld hl = new HelloWorldImpl();//1. 暴露的实现类
        String address = "http://172.29.152.143/baolu"; //2. 暴露的地址 其中http必须 地址为本机地址 /后随便
        Endpoint.publish(address, hl); //3.发布
        System.out.println("webservice已经开始!!");
    }

}

1.7 运行

1.8 访问

这里需要特别注意的是 需要使用url?wsdl访问 否则无法访问

WSDL的概述
这里的wsdlWeb Services Description Language的缩写,是一个用来描述Web服务和说明如何与Web服务通信XML语言。WSDL是Web Service的描述语言,用于描述Web Service的服务,接口绑定等,为用户提供详细的接口说明书

二.使用cxf框架 实现webservice

2.1 pom.xml中添加和下载apache cxf框架 中央仓库


        org.apache.cxf
        cxf-rt-frontend-jaxws
        3.1.12
  

2.2 这边使用jetty发布(类似于tomcat)web


    org.apache.cxf
    cxf-rt-transports-http-jetty
    3.1.12

2.3 修改代码 使用cxf实现

package com.ztgeo.webservice.impl;

import org.apache.cxf.jaxws.JaxWsServerFactoryBean;

import com.ztgeo.webservice.HelloWorld;

public class Service {
    
    public static void main(String[] args) {
        System.out.println("webservice开始工作!!");
        HelloWorld hl = new HelloWorldImpl();//1. 暴露的实现类
        String address = "http://localhost/baolu"; //2. 暴露的地址 其中http必须 地址为本机地址 /后随便
        //Endpoint.publish(address, hl); //3.发布
        JaxWsServerFactoryBean factoryBean = new JaxWsServerFactoryBean();
        factoryBean.setAddress(address);//设置发布地址  
        factoryBean.setServiceClass(HelloWorld.class);//设置接口类
        factoryBean.setServiceBean(hl);//设置实现类
        factoryBean.create();//启动
        System.out.println("webservice已经开始!!");
    }

}

2.4 请求页面 这里使用框架来构建 url无需加?wsdl也不会报错

第二章 使用cxf 开发客户端

客户端开发用来解析url
2.1 新建maven项目

使用wsdl2java可以根据wsdl动态生成代码
这个工具在cxf的源码包中,点击xcf链接下载

下载后主要使用bin下的wsdl2java命令工具

使用该命令前需要在系统变量中将该bin设置为系统变量.


2.2 使用wsdl2java根据wsdl生成对应代码

cd 切换目录到项目文件夹


使用过程中 如果报指令不符合...确定加入环境变量后重开cmd

2.3 refresh ecilipse 查看生成的代码

暴露的节后生成类后依然是接口,具体的实现由HelloWorldService来实现

2.4 编写Client类,使用该接口 该类位于生成类的同包下

package com.ztgeo.webservice;

public class Client {

    //客户端调用
    public static void main(String[] args) {
        HelloWorldService service = new HelloWorldService();//实体化服务类
        HelloWorld hl = service.getHelloWorldPort();//获得接口类 (HelloWorld)
        System.out.println(hl.say("你好,服务端!我是客户端")); //调用该接口的方法
    }

}

2.5 结果展示

使用同样的操作可以解密贵公司提供的解密服务(c++编写)


第三章 使用CXF处理JavaBean以及复合类型

需求升级描述 由客户端传入字符串改为传入User类验证拥有的权限

3.1 服务端增加User类
重要字段:

private Integer id;//编号
private String usernamee;//用户名
private String password;//密码    

3.2 服务端增加Role类
重要字段:

private Integer id;//编号
private String roleName;//角色名称  

3.3 在暴露的接口(HelloWorld)体现需求 (过来用户 返回角色集合)

//给用户 返回角色集合  
public List getRolesByUser(User user);  

3.4 实现类中进行接口的实现

public List getRolesByUser(User user) {
    List roles = new ArrayList();
    if("wh".equals(user.getUsername())&&"123456".equals(user.getPassword())){
        roles.add(new Role(1,"系统管理员"));
        roles.add(new Role(2, "程序员"));
    
    }else{
        roles.add(new Role(3,"普通用户"));
    }
    return roles;
}

3.5 重新发布下

3.6 客户端使用wsdl2java 重新更新生成类(无需删除)

相应的实体类也生成了

3.7 更改客户端的调用

public static void main(String[] args) {
    HelloWorldService service = new HelloWorldService();//实体化服务类
    HelloWorld hl = service.getHelloWorldPort();//获得接口类 (HelloWorld)
    //System.out.println(hl.say("你好,服务端!我是客户端")); //调用该接口的方法
    User user = new User();//这里的user只能通过无参构造  
    user.setUsername("wh");
    user.setPassword("123456");
    
    List roles = hl.getRolesByUser(user);//调用实现
    
    for (Role role : roles) {
        System.out.println(role.getId()+"角色民称:"+role.getRoleName());
    }
}

3.8 运行后输出

![](http://or316jtnw.bkt.clouddn.com//17-7-11/57009298.jpg)  

第四章 CXF 处理复杂类型(MAP)

问题暴露:
4.1 接口需求编写

public Map> getAllRoles();

4.2 方法的实现

public Map> getAllRoles() {
    Map> roles = new HashMap>();
    List roles1 = new ArrayList();
    roles1.add(new Role(1,"角色一"));
    roles1.add(new Role(2,"角色二"));
    List roles2 = new ArrayList();
    roles1.add(new Role(3,"角色三"));

    roles.put("1",roles1);
    roles.put("2",roles2);

    return roles;
}

4.3 发布后发现问题

这时候需要特殊处理map类

4.4 暴露的接口中使用适配器进行处理

//返回所有用户角色和权限  
@XmlJavaTypeAdapter(MapAdapte.class)//加入适配器类(不能处理的类的具体实现)
public Map> getAllRoles(); 

其中 具体来处理适配器的类是MapAdate 是自己需要编写的

4.5 编写适配器类的适配规则

//该类必须继承XmlAdapter 参数一 被转换的类型(数组 自行创建 map 已存在 )
public class MapAdapte extends XmlAdapter>>{
    
}

其中MyRole自行创建 [] 代表多个类

4.6 创建 转换成的类 MyRole
主要属性:

private String key;
private List value;

其实就相当于 将key value转换成了具体的实体类 然后map转换成了实体类数组

4.7 实现继承需要被重写的方法
就是具体的转换过程

//适配转换 MyRole[]-->Map
@Override
public Map> unmarshal(MyRole[] v) throws Exception {
//实现的方式
Map> map = new HashMap>();
for (int i = 0; i < v.length; i++) {
map.put(v[i].getKey(), v[i].getValue());
}
return map;
}

//适配转换 Map-->MyRole[]
@Override
public MyRole[] marshal(Map> v) throws Exception {
    MyRole[] myroles = new MyRole[v.size()];
    int i =0;
    for (String key  : v.keySet()) {
        //必须实例化 否则 空指针 
        myroles[i] = new MyRole();
        myroles[i].setKey(key);
        myroles[i].setValue(v.get(key));
        i++;
    }
    return myroles;
}

4.8 再次发布运行

发布成功!!无报错

且真正的返回类型在wsdl中已有改变 为MyRole


4.9 客户端重新更新代码

4.10 客户端解析代码

public static void main(String[] args) {
    HelloWorldService service = new HelloWorldService();//实体化服务类
    HelloWorld hl = service.getHelloWorldPort();//获得接口类 (HelloWorld)
    
    MyRoleArray myRoleArray = hl.getAllRoles();
    
    //编译array
    
    List roles = myRoleArray.getItem();
    for (MyRole myRole : roles) {
        //便利里面的角色名称
        for (Role role : myRole.getValue()) {
            System.out.println("角色id:"+role.getId()+"角色名称:"+role.getRoleName());
        }
    }
}

输出结果 :

第五章 CXF添加拦截器 (内置拦截器)

5.1 添加cxf内置in log拦截器(可以看到soap消息) __服务器端

    factoryBean.getInInterceptors().add(new LoggingInInterceptor());//in日志拦截器 进入启用  

5.2 添加cxf内置out log拦截器(可以看到soap消息) __服务器端

    factoryBean.getOutInterceptors().add(new LoggingOutInterceptor());//out日志拦截器 退出启用  

5.3 服务器端启动-->客户端调用 查看日志

webservice开始工作!!
七月 11, 2017 9:00:49 下午 org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
信息: Creating Service {http://webservice.ztgeo.com/}HelloWorldService from class com.ztgeo.webservice.HelloWorld
七月 11, 2017 9:00:51 下午 org.apache.cxf.endpoint.ServerImpl initDestination
信息: Setting the server's publish address to be http://localhost:80/baolu
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
webservice已经开始!!
七月 11, 2017 9:00:58 下午 org.apache.cxf.services.HelloWorldService.HelloWorldPort.HelloWorld
信息: Inbound Message
----------------------------
ID: 1
Address: http://localhost/baolu?wsdl
Http-Method: GET
Content-Type: 
Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], Content-Type=[null], Host=[localhost], Pragma=[no-cache], User-Agent=[Apache-CXF/3.1.12]}
--------------------------------------
七月 11, 2017 9:01:00 下午 org.apache.cxf.services.HelloWorldService.HelloWorldPort.HelloWorld
信息: Inbound Message
----------------------------
ID: 2
Address: http://localhost/baolu
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml; charset=UTF-8
Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], Content-Length=[168], content-type=[text/xml; charset=UTF-8], Host=[localhost], Pragma=[no-cache], SOAPAction=[""], User-Agent=[Apache-CXF/3.1.12]}
Payload: 
--------------------------------------
七月 11, 2017 9:01:00 下午 org.apache.cxf.services.HelloWorldService.HelloWorldPort.HelloWorld
信息: Outbound Message
---------------------------
ID: 2
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml
Headers: {}
Payload: 11角色一2角色二3角色三2
--------------------------------------

其中id2 是返回的拦截器.里面有soap消息

Client端实现拦截

因为client端要使用拦截器,所以 jar包还是需要的
5.4 pom.xml中配置相应的jar

  
        org.apache.cxf
        cxf-rt-frontend-jaxws
        3.1.12
  
  
    
        org.apache.cxf
        cxf-rt-transports-http-jetty
        3.1.12
    

5.5 使用client代理类进行编写

public static void main(String[] args) {
    HelloWorldService service = new HelloWorldService();//实体化服务类
    HelloWorld hl = service.getHelloWorldPort();//获得接口类 (HelloWorld)
    
    //MyRoleArray myRoleArray = hl.getAllRoles();//这个位置拦截不到信息 因为方法执行后才拦截
    org.apache.cxf.endpoint.Client client = ClientProxy.getClient(hl);//使用代理类 获取客户端  
    client.getInInterceptors().add(new LoggingInInterceptor());//进入拦截器
    client.getOutInterceptors().add(new LoggingOutInterceptor());//检出拦截器
    //编译array
    MyRoleArray myRoleArray = hl.getAllRoles();//这边一定要注意顺序 拦截器必须在方法前 否则 拦截不到
    List roles = myRoleArray.getItem();
    for (MyRole myRole : roles) {
        //便利里面的角色名称
        for (Role role : myRole.getValue()) {
            System.out.println("角色id:"+role.getId()+"角色名称:"+role.getRoleName());
        }
    }
}

和服务器端不同的是 先有检出 后进入

5.6 控制台监控的信息如下 :

七月 11, 2017 9:11:56 下午 org.apache.cxf.wsdl.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL
信息: Creating Service {http://webservice.ztgeo.com/}HelloWorldService from WSDL: http://localhost/baolu?wsdl
七月 11, 2017 9:11:58 下午 org.apache.cxf.services.HelloWorldService.HelloWorldPort.HelloWorld
信息: Outbound Message
---------------------------
ID: 1
Address: http://localhost/baolu
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml
Headers: {Accept=[*/*], SOAPAction=[""]}
Payload: 
--------------------------------------
七月 11, 2017 9:11:58 下午 org.apache.cxf.services.HelloWorldService.HelloWorldPort.HelloWorld
信息: Inbound Message
----------------------------
ID: 1
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml; charset=UTF-8
Headers: {content-type=[text/xml; charset=UTF-8], Date=[Tue, 11 Jul 2017 13:11:58 GMT], Server=[Jetty(9.2.21.v20170120)], transfer-encoding=[chunked]}
Payload: 11角色一2角色二3角色三2
--------------------------------------
角色id:1角色名称:角色一
角色id:2角色名称:角色二
角色id:3角色名称:角色三

这边需要注意的是.对于客户端来讲,先有出(访问请求),才有进(返回请求), 在返回端可以看到soap信息

第六节 CXF添加自定义拦截器

添加自定义拦截器的思路和内置拦截器相同 ,只需要替换内置拦截器,自己写就好..看内置的实现了哪些方法

内置拦截器的继承关系

public class LoggingInInterceptor extends AbstractLoggingInterceptor

6.1 编写服务端自定义拦截器

package com.ztgeo.interceptor;

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;

public class MyInInterceptor extends  AbstractPhaseInterceptor{

public MyInInterceptor(String phase) {//实现父类的方法 ,这里是个构造方法实现
    super(Phase.PRE_INVOKE);//给静态变量赋值 在进入方法前调用
    
}

public void handleMessage(SoapMessage message) throws Fault {
    //soap消息就是客户端传入的信息
    //全部封装在headers中 进行头部的解析
    List
headers = message.getHeaders();//获取全部头信息 //判断如果没有头部信息 那么一定有问题 if(headers==null|| headers.size()==0){ throw new Fault(new IllegalArgumentException("头信息不存在,in拦截器抛出了异常!!")); } //存在头信息 获取头信息 (是xml形式) Header header = headers.get(0); //使用w3c的dom解析该header Element ele =(Element)header.getObject(); //获取其中的username和password节点 String username = ele.getElementsByTagName("username").item(0).getTextContent(); String password = ele.getElementsByTagName("password").item(0).getTextContent(); //约定允许访问的条件 if("wh".equals(username)){ if("123456".equals(password)){ }else{ throw new Fault(new IllegalArgumentException("用户秘钥异常~!!")); } }else{ throw new Fault(new IllegalArgumentException("当前用户无访问权限!")); } } }

6.1 客户端添加检出自定义拦截器 用于往头部添加信息

 client.getOutInterceptors().add(new AddHeaderInInterceptor("wh","12456"));//自己编写的类

6.2 编写客户端自定义拦截器
这边的自定义类 和服务端一样 只是触发拦截的时机改为了准备发送,soap message的处理 不是解析头部信息 而是往头部信息里添加username和password
package com.ztgeo.webservice;

import java.util.List;

import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;

import com.ztgeo.interceptor.AddHeaderInInterceptor;

public class Client {

//客户端调用
public static void main(String[] args) {
    HelloWorldService service = new HelloWorldService();//实体化服务类
    HelloWorld hl = service.getHelloWorldPort();//获得接口类 (HelloWorld)
    
    //MyRoleArray myRoleArray = hl.getAllRoles();//这个位置拦截不到信息 因为方法执行后才拦截
    org.apache.cxf.endpoint.Client client = ClientProxy.getClient(hl);//使用代理类 获取客户端  
    client.getInInterceptors().add(new LoggingInInterceptor());//进入拦截器
    client.getOutInterceptors().add(new LoggingOutInterceptor());//检出拦截器
    
    //添加自定义拦截器 用于在头部添加用户信息
    client.getOutInterceptors().add(new AddHeaderInInterceptor("wh","12456"));//自己编写的类
    //编译array
    MyRoleArray myRoleArray = hl.getAllRoles();//这边一定要注意顺序 拦截器必须在方法前 否则 拦截不到
    List roles = myRoleArray.getItem();
    for (MyRole myRole : roles) {
        //便利里面的角色名称
        for (Role role : myRole.getValue()) {
            System.out.println("角色id:"+role.getId()+"角色名称:"+role.getRoleName());
        }
    }
}

}

上面代码需要 注意 发送前添加了一个头 而不是修改一个头

6.3 启动服务器端

启动正常

6.4 启动客户端 查看结果
头部携带的信息如下:

Payload: soap:Server用户秘钥异常~!!

结果正常:

第七章 使用Spring整合CXF开发WEB端

关于整合spring发布webservice 官方文档地址
7.1 新建一个web项目

骨架选择 web1.0的骨架

7.2 替换为jre1.8

7.3 目前项目处于报错状态 是因为jsp文件中 没有httpservlet

针对这种情况 实际上是缺少运行时servlet导致的,解决的方法是 添加tomcat后重新保存

7.4 pom.xml中添加spring支持



    org.springframework
    spring-core
    4.1.7.RELEASE


    org.springframework
    spring-beans
    4.1.7.RELEASE


     org.springframework
     spring-tx
     4.1.7.RELEASE
    

    org.springframework
    spring-context
    4.1.7.RELEASE


    org.springframework
    spring-context-support
    4.1.7.RELEASE

 

    org.springframework
    spring-web
    4.1.7.RELEASE

 

    org.springframework
    spring-webmvc
    4.1.7.RELEASE

 

    org.springframework
    spring-aop
    4.1.7.RELEASE

 
 

    org.springframework
    spring-aspects
    4.1.7.RELEASE

 

    org.springframework
    spring-jdbc
    4.1.7.RELEASE

7.5 添加cxf支持


org.apache.cxf
cxf-core
3.1.5

    
        org.apache.cxf
        cxf-rt-frontend-jaxws
        3.1.5
    
     
    
        org.apache.cxf
        cxf-rt-transports-http
        3.1.5
    

注意 这边已经没有了jetty的支持 改为tomcat

7.6 编写spring的配置文件 注意文件的位置 放在resources中

7.7 具体的配置

 
    
        
        
    
    
    
    
    
    
    
    
  

其中 xmlns:jaxws="http://cxf.apache.org/jaxws" 为新命名的空间
implementor="#helloWorld" 为暴露的接口
adress ="/HelloWorld"为url访问时 输入的url标识符

7.8 #helloWorld需要在实现类中加入注解

@Component("helloWorld")
@WebService//同样适用webservice记性注解
public class HelloWorldImpl implements HelloWorld 

7.9 web.xml中的配置如下

    

  WS_Spring_CXF
  
    index.html
    index.htm
    index.jsp
    default.html
    default.htm
    default.jsp
  
  
    
    
        contextConfigLocation
        classpath:applicationContext.xml
    
     
    
    
        org.springframework.web.context.ContextLoaderListener
    
    
      
     CXFServlet  
     org.apache.cxf.transport.servlet.CXFServlet  
      
     
      
       CXFServlet  
       /webservice/*  
    

除了基本的web配置外 需要单门配置servlet 的mapping进行处理servlet

7.10 使用客户端进行解析

解析结果正常

第八章 使用spring拦截器

8.1 对于客户端,添加拦截器需要在 applicationContext中配置如下:

 
     
     
    
    
    
     

其中 有自定义的拦截器 和 框架本身的拦截器

8.2 客户端的调用 做了些改动,因为使用spring发布的webservice 生成的类做了调整..具体的调用方法如下:

HelloWorldImplService service = new HelloWorldImplService();
    HelloWorld hl = service.getHelloWorldImplPort();//获得接口类 (HelloWorld)

8.3 拦截效果如下:

本学习总结结束

你可能感兴趣的:(WebService知识整理)