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数据
SOAP协议
含义:simple object access protocol——简单对象访问协议,它是用于交换XML编码信息的轻量级协议。
soap的组成
- Envelope:其为必须的部分,以XML的根元素出现
- Headers:可选的
- Body:必须的,在body部分包含了要执行的服务器的方法和发送给服务器的数据
理解:
- soap作为一个基于XML语言的协议用于网上传输数据
- soap是基于http的,他相当于在http的基础上+xml数据格式
- soap可以运行在任何其他传输协议上
- XML-envelope为描述信息内容和如何处理内容定义了框架,将程序编码成了XML对象的规则,执行远程调用(rpc)的约定
WSDL
含义:WSDL(网络服务描述语言,Web Services Description Language)是一门基于 XML 的语言,用于描述 Web Services 以及如何对它们进行访问。
理解:
- 通过wsdl说明书,就可以描述webservice服务端对外发布的服务
- wsdl说明书基于XML文件,其可以通过XML语言来描述整个服务
- 在wsdl中描述了:对外发布的服务名称(类)、接口的方法名称(方法)、接口参数(方法参数)、服务返回的数据类型(方法返回值)
- 一般在webservice的url后面跟上?wsdl来获取WSDL信息
UDDI
含义:UDDI是一个跨产业、跨平台的开放性架构,其可帮助web服务提供商在互联网上发布web服务的信息
理解:
- UDDI就是一种目录服务,企业可以通过UDDI来注册和搜索web服务
- UDDI通过soap进行通讯,其构建于.NET之上
WebService的优缺点
WebService优点
- 异构平台的互通性(跨平台)
- 更广泛的软件复用(远程调用实现复用)
- 成本低,可读性强,应用范围广(基于soap协议)
- 更迅捷的软件发行方式
WebService缺点
由于soap是基于xml传输的,本身使用xml传输会传输一些无关的内容进而影响效率,随着soap的完善,soap协议增加了许多内容,这样就导致了使用soap去完成简单的数据传输而携带的信息变得更多进而影响效率
注意:基于JAX-RS规范下的webservice也可以传输json格式数据,这在一定程度上弥补了传输效率问题
SOA
含义:面向服务架构,其是一种思想,它将应用程序的不通功能单元通过中立的契约联系起来,使得各种形式的功能单元相互集成,目前来说webservice是soa的一种较好的实现方式。
ApacheCXF框架
含义:其是Apache开源基金组织提供的优秀的webservice实现框架
CXF分为JAX-WS和JAX-RS两种开发方式
- JAX-WS:基于xml协议的WebService技术
- JAX-RS:基于restful风格的开发方式
ApacheCXF实现WebService(JAX-WS)
服务端发布服务
导入依赖
<!--进行jaxws开发的核心包-->
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.0.1</version>
</dependency>
<!--内置jetty web服务器-->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.0.1</version>
</dependency>
<!--日志的实现-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
<!--junit测试类-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
在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[%15.15t]%-5p %30.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
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://impl.tedu.cn/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns2="http://schemas.xmlsoap.org/soap/http" xmlns:ns1="http://tedu.cn/" name="HelloWorldImplService" targetNamespace="http://impl.tedu.cn/">
<wsdl:import location="http://localhost:8000/ws/hello?wsdl=HelloWorld.wsdl" namespace="http://tedu.cn/"> </wsdl:import>
<wsdl:binding name="HelloWorldImplServiceSoapBinding" type="ns1:HelloWorld">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="sayHello">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="sayHello">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="sayHelloResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HelloWorldImplService">
<wsdl:port binding="tns:HelloWorldImplServiceSoapBinding" name="HelloWorldImplPort">
<soap:address location="http://localhost:8000/ws/hello"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
客户端访问服务
导入依赖(和服务端使用的依赖一样)
获得服务端接口
@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);
}
}
ApacheCXF实现WebService(JAX-RS)
服务端发布服务
导入依赖
<dependencies>
<!--进行jaxrs开发的核心包-->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>3.0.1</version>
</dependency>
<!--内置jetty web服务器-->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.0.1</version>
</dependency>
<!--日志的实现-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
<!--客户端调用时需要使用的依赖-->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-client</artifactId>
<version>3.0.1</version>
</dependency>
<!--对json支持的两个依赖(就是不仅可以传递xml数据,也可以传递json数据)-->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.3.7</version>
</dependency>
<!--junit测试类-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!--maven编译插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
</plugins>
</build>
在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[%15.15t]%-5p %30.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形式
<User>
<name>张三</name>
<city>北京</city>
</User>
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);
}
}