webservice简介
含义:webservice即web服务,他是一种跨编程语言和跨操作系统平台的远程调用技术。
webservice架构图
理解:
- 跨语言:右侧为服务端,左侧为客户端;若右边的服务使用php语言实现的,那么调用右边服务端的客户端不管是用什么语言实现的,都可以去远程访问右边的客户端的接口。
- 跨平台:无论右边的服务端部署在那个系统平台,只要其提供了一个接口给外界去调用,那么左边的客户端部署在哪里,使用什么语言,一样可以远程调用服务端。
- webservice主要适用于多个系统之间的交互以及数据传递
注意:客户端与服务端可能使用不同语言开发的,但是通过webservice提供的服务接口,客户端与服务端之间可以传递对象。
webservice的开发规范
jax-ws:java api for xml-webservice,jdk1.6版本自带jax-ws2.1,其底层支持jaxb;jax-wx规范的api位于javax.xml.ws.*包内,其中大部分都是注解,提供api操作web服务
jaxm&saaj:
- jaxm:java api for xml message,其主要定义了包含发送和接收消息的api,相当于web服务的服务器端,其api位于javax.messaging.*包,他是javaee的可选包,因此需要单独下载
- saaj:soap with attachment api for java,其为与jaxm搭配使用的api,为构建soap包和解析soap包提供了重要的支持;其支持附件传输,他在客户端和服务端都需要使用,其api位于javax.xml.soap.*包
jax-rs:java api for restful web services,其是java针对rest风格定制的一套web服务规范,该api位于javax.ws.rs.*包内。
注意:
- jax-ws和jaxm&saaj是基于soap协议,而jax-rs基于http协议。
- 三者规范中,只有jax-rs规范支持传递json数据,其他的规范都仅支持传递xml数据
含义:simple object access protocol——简单对象访问协议,它是用于交换xml编码信息的轻量级协议。
soap的组成
- envelope:其为必须的部分,以xml的根元素出现
- headers:可选的
- body:必须的,在body部分包含了要执行的服务器的方法和发送给服务器的数据
理解:
- soap作为一个基于xml语言的协议用于网上传输数据
- soap是基于http的,他相当于在http的基础上 xml数据格式
- soap可以运行在任何其他传输协议上
- xml-envelope为描述信息内容和如何处理内容定义了框架,将程序编码成了xml对象的规则,执行远程调用(rpc)的约定
含义:wsdl(网络服务描述语言,web services description language)是一门基于 xml 的语言,用于描述 web services 以及如何对它们进行访问。
理解:
- 通过wsdl说明书,就可以描述webservice服务端对外发布的服务
- wsdl说明书基于xml文件,其可以通过xml语言来描述整个服务
- 在wsdl中描述了:对外发布的服务名称(类)、接口的方法名称(方法)、接口参数(方法参数)、服务返回的数据类型(方法返回值)
- 一般在webservice的url后面跟上?wsdl来获取wsdl信息
含义:uddi是一个跨产业、跨平台的开放性架构,其可帮助web服务提供商在互联网上发布web服务的信息
理解:
- uddi就是一种目录服务,企业可以通过uddi来注册和搜索web服务
- uddi通过soap进行通讯,其构建于.net之上
webservice优点
- 异构平台的互通性(跨平台)
- 更广泛的软件复用(远程调用实现复用)
- 成本低,可读性强,应用范围广(基于soap协议)
- 更迅捷的软件发行方式
webservice缺点
由于soap是基于xml传输的,本身使用xml传输会传输一些无关的内容进而影响效率,随着soap的完善,soap协议增加了许多内容,这样就导致了使用soap去完成简单的数据传输而携带的信息变得更多进而影响效率
注意:基于jax-rs规范下的webservice也可以传输json格式数据,这在一定程度上弥补了传输效率问题
含义:面向服务架构,其是一种思想,它将应用程序的不通功能单元通过中立的契约联系起来,使得各种形式的功能单元相互集成,目前来说webservice是soa的一种较好的实现方式。
含义:其是apache开源基金组织提供的优秀的webservice实现框架
cxf分为jax-ws和jax-rs两种开发方式
- jax-ws:基于xml协议的webservice技术
- jax-rs:基于restful风格的开发方式
服务端发布服务
导入依赖
org.apache.cxf
cxf-rt-frontend-jaxws
3.0.1
org.apache.cxf
cxf-rt-transports-http-jetty
3.0.1
org.slf4j
slf4j-log4j12
1.7.12
junit
junit
4.11
test
在resources文件内添加日志(log4j.properties)
#info等级的日志输出到console和logfile这两个目的地(logfile表示将日志写到文件中,console则将日志写到控制台)
log4j.rootcategory=info,console,logfile
#设置日志优先控制台输出
log4j.logger.org.apache.axis.enterprise=fatal,console
#定义控制台日志输出器
log4j.appender.console=org.apache.log4j.consoleappender
#控制台日志布局
log4j.appender.console.layout=org.apache.log4j.patternlayout
#控制台日志布局的设置
log4j.appender.console.layout.conversionpattern=%d{iso8601}%-6r[.15t]%-5p 0.30c %x-%m\n
#定义文件日志输出器
log4j.appender.logfile=org.apache.log4j.fileappender
#日志的存放位置
log4j.appender.logfile.file=c:\\all\\jax.log
#启用文件日志追加模式
log4j.appender.logfile.append=true
#文件日志布局
log4j.appender.logfile.layout=org.apache.log4j.patternlayout
创建服务接口
@webservice
public interface helloworld {
//对外发布服务的接口的方法
public string sayhello(string name);
}
注意:对外发布服务的接口,需要用@webservice注解来标识这是一个webservice接口
创建接口实现类
public class helloworldimpl implements helloworld {
public string sayhello(string name) {
return name "hello webservice!";
}
}
测试类内发布服务
public class wstest {
public static void main(string[] args) {
//创建发布服务的工厂
jaxwsserverfactorybean factory = new jaxwsserverfactorybean();
//设置服务地址
factory.setaddress("http://localhost:8000/ws/hello");
//设置发布的服务类
factory.setservicebean(new helloworldimpl());
//添加日志输入、输出拦截器,观察soap请求以及soap响应内容
factory.getininterceptors().add(new loggingininterceptor());
factory.getoutinterceptors().add(new loggingoutinterceptor());
//发布服务
factory.create();
system.out.println("发布服务成功,端口8000放行");
}
}
访问wsdl说明书
访问:http://localhost:8000/ws/hello?wsdl
注意:在之前设置服务的地址后面加?wsdl
客户端访问服务
导入依赖(和服务端使用的依赖一样)
获得服务端接口
@webservice
public interface helloworld {
public string sayhello(string name);
}
注意:客户端获得的服务端接口的包名.接口名必须与服务端的包名.接口名都相同才可以进行远程调用(也必须有@webservice注解)
远程访问服务端
public class clienttest {
public static void main(string[] args) {
//服务接口的访问地址:http://localhost:8000/ws/hello
//创建cxf代理工厂
jaxwsproxyfactorybean factory = new jaxwsproxyfactorybean();
//设置远程访问服务端的地址
factory.setaddress("http://localhost:8000/ws/hello");
//设置接口的类型
factory.setserviceclass(helloworld.class);
//对该接口生成代理对象
helloworld helloworld = factory.create(helloworld.class);
//打印代理对象类型
system.out.println(helloworld.getclass());
//远程访问服务端方法
string msg = helloworld.sayhello("lili");
system.out.println(msg);
}
}
服务端发布服务
导入依赖
org.apache.cxf
cxf-rt-frontend-jaxrs
3.0.1
org.apache.cxf
cxf-rt-transports-http-jetty
3.0.1
org.slf4j
slf4j-log4j12
1.7.12
org.apache.cxf
cxf-rt-rs-client
3.0.1
org.apache.cxf
cxf-rt-rs-extension-providers
3.0.1
org.codehaus.jettison
jettison
1.3.7
junit
junit
4.11
test
org.apache.maven.plugins
maven-compiler-plugin
3.1
在resources文件内添加日志(log4j.properties)
#info等级的日志输出到console和logfile这两个目的地(logfile表示将日志写到文件中,console则将日志写到控制台)
log4j.rootcategory=info,console,logfile
#设置日志优先控制台输出
log4j.logger.org.apache.axis.enterprise=fatal,console
#定义控制台日志输出器
log4j.appender.console=org.apache.log4j.consoleappender
#控制台日志布局
log4j.appender.console.layout=org.apache.log4j.patternlayout
#控制台日志布局的设置
log4j.appender.console.layout.conversionpattern=%d{iso8601}%-6r[.15t]%-5p 0.30c %x-%m\n
#定义文件日志输出器
log4j.appender.logfile=org.apache.log4j.fileappender
#日志的存放位置
log4j.appender.logfile.file=c:\\all\\jax.log
#启用文件日志追加模式
log4j.appender.logfile.append=true
#文件日志布局
log4j.appender.logfile.layout=org.apache.log4j.patternlayout
创建实体类(user)
@xmlrootelement(name="user")
public class user {
private string name;
private string city;
public string getname() {
return name;
}
public void setname(string name) {
this.name = name;
}
public string getcity() {
return city;
}
public void setcity(string city) {
this.city = city;
}
@override
public string tostring() {
return "user{"
"name='" name '\''
", city='" city '\''
'}';
}
}
@xmlrootelement(name="user")
作用:基于restful风格的webservice,客户端与服务端之间的通讯可以传递xml数据、json数据;而@xmlrootelement用于指定对象序列化为xml或json数据时根节点的名称。
xml形式
张三
北京
json形式
{"user":{"name":"张三","city":"北京"}}
创建服务端接口(iuserservice)
@path("/userservice")
@produces("*/*")
public interface iuserservice {
@post
@path("/save")
@consumes({"application/xml","application/json"})
string saveuser(user user);
@get
@path("/get/{name}")
@consumes("application/xml")
@produces({"application/xml","application/json"})
user finduserbyname(@pathparam("name") string name);
}
@path("/userservice")
理解:该注解可以用在类上以及方法上,表示当前服务接口或接口方法对应的路径(若要访问接口方法则必须先访问接口,在接口的基础上进行path路径拼接)
@post或@get
含义:处理接口对应方法的请求类型
@produces({"application/xml","application/json"})
含义:服务器所支持的返回的数据格式(xml格式或json格式)
@consumes("application/xml")
含义:服务器所支持的请求数据的格式类型
@pathparam
作用:注解用于路径中的参数与接口方法中的参数进行绑定
创建接口实现类(userserviceimpl)
public class userserviceimpl implements iuserservice {
@override
public string saveuser(user user) {
system.out.println("保存了" user.tostring());
return "user保存成功";
}
@override
public user finduserbyname(string name) {
user user = new user();
if ("lili".equals(name)){
user.setname("lili");
user.setcity("北京");
}else {
user.setname("随机");
user.setcity("随机");
}
return user;
}
}
发布服务
public class jaxrstest {
public static void main(string[] args) {
//创建发布服务的工厂
jaxrsserverfactorybean factorybean = new jaxrsserverfactorybean();
//设置服务地址
factorybean.setaddress("http://localhost:8001/");
//设置服务类
factorybean.setservicebean(new userserviceimpl());
//添加日志输入输出拦截器
factorybean.getininterceptors().add(new loggingininterceptor());
factorybean.getoutinterceptors().add(new loggingoutinterceptor());
//发布服务
factorybean.create();
system.out.println("发布服务成功。端口:8001");
}
}
访问:http://localhost:8001/userservice/get/lili
客户端访问服务
导入依赖(和服务端使用的依赖一样)
制作需要使用的实体类
@xmlrootelement(name="user")
public class user {
private string name;
private string city;
public string getname() {
return name;
}
public void setname(string name) {
this.name = name;
}
public string getcity() {
return city;
}
public void setcity(string city) {
this.city = city;
}
@override
public string tostring() {
return "user{"
"name='" name '\''
", city='" city '\''
'}';
}
}
注意:这里实体类所用的包名可以和服务端的不一样,但是@xmlrootelement(name="user")注解必须存在
远程调用服务端
前言:这里远程调用服务端只需要使用webclient工具类即可完成,他调用请求方法后所返回的返回值为response对象,该对象可以通过readentity()方法来获取特定类型的返回值供我们使用。
webclient中的静态方法
- create(string url):表示请求的服务端url地址
- type(string type):指定请求的数据格式(xml、json)
- accept(string type):指定接收响应的数据格式(xml、json)
- post(请求参数):表示要带着该参数来发起post请求
注意:以上方法除了post()和get()等请求的方法返回值为response类型,其他方法的返回值均为webclient类型,所以可以实现链式调用
public class clienttest {
public static void main(string[] args) {
user user = new user();
user.setname("nana");
user.setcity("广东");
//通过webclient对象远程调用服务端(post请求)
response response = webclient.create("http://localhost:8001/userservice/save").type("xml").accept("json").post(user);
//读取response中请求体的内容,并获取特定类型的返回值
string s = response.readentity(string.class);
system.out.println(s);
//通过webclient对象远程调用服务端(get请求)
response response1 = webclient.create("http://localhost:8001/userservice/get/lili").accept("xml").type("json").get();
//读取response中请求体的内容,并获取特定类型的返回值
user user1 = response1.readentity(user.class);
system.out.println(user1);
}
}