WebService
0. WebService简介
0.1 问题引入
问题一:
Ø 它们公司服务器的数据库中都保存了天气预报数据吗?
Ø 如果没有, 那数据都存在哪了呢?
Ø 这些网站是如何得到这些数据的呢?
问题二:
Ø 各个门户网站显示的股票行情信息数据又是怎么来的呢?
0.2 WebService简介
Webservice也叫XML Web Service,Web服务。可使用开放的**xml**标准来描述、发布、发现、协调和配置这些应用程序。用于开发分布式的互操作的应用程序。
是一种跨编程语言、跨操作系统、跨网络的远程服务器调用技术。
WebService,顾名思义就是基于Web的服务。它使用Web(HTTP)方式,接收和响应外
部系统的某种请求。从而实现远程调用.
我们可以调用互联网上查询天气信息Web服务,然后将它嵌入到我们的程序(C/S或B/S
程序)当中来,当用户从我们的网点看到天气信息时,他会认为我们为他提供了很多的信息服务,但其实我们什么也没有做,只是简单了调用了一下别人服务器上的一段代码而已。
学习WebSerice可以将你的服务(一段代码)发布到互联网上让别人去调用,也可以调用别人机器上发布的WebService,就像使用自己的代码一样.。
webservice可以实现跨平台远程调用,从而实现异构程序之间的通信,特定需求下可以实现远程数据共享、软件重用、分布式程序集成。
webservice 即web服务,**它是一种跨编程语言和跨操作系统平台的远程调用技术**即跨平台远程调用技术。
它采用标准**SOAP**协议(Simple Object Access Protocol),协议传输webservice数据,是w3c标准。
xml和XSD(XML Schema Datatypes)是webservice的基础,是w3c标准,**采用**wsdl作为描述语言即webservice使用说明书。
- 以各个网站显示天气预报功能为例:
气象中心的管理系统将收集的天气信息并将数据暴露出来(通过WebService Server), 而各大站点的应用就去调用它们得到天气信息并以不同的样式去展示(WebService Client).
网站提供了天气预报的服务,但其实它们什么也没有做,只是简单了调用了一下气象中心服务器上的一段代码而已。
0.3 为什么要用Web service?
web service能解决:
**跨平台调用**
**跨语言调用**
**远程调用**
1、采用xml支持跨平台远程调用。
2、基于http的soap协议,可跨越防火墙。
3、支持面向对象开发。
4、有利于软件和数据重用,实现松耦合。
0.4 什么时候使用webService?
- 同一家公司的新旧应用之间
- 不同公司的应用之间 分析业务需求:天猫网与中通物流系统如何交互?
- 一些提供数据的内容聚合应用:天气预报、股票行情
0.5 web Service原理
Webservice是使用Http发送SOAP协议的数据的一种远程调用技术
Webservice要开发服务端
Webservice要开发客户端
Webservice客户端开发需要阅读服务端的使用说明书(WSDL)
- 服务器端
public class WeatherServer {
public static void main(String[] args) throws Exception {
Map<String, String> map=new HashMap<String, String>();
map.put("北京", "天气晴");
map.put("成都", "小雨");
map.put("深圳", "阴天");
//创建Socket
ServerSocket serverSocket=new ServerSocket(8888);
System.out.println("服务器端开始启动...监听端口:8888");
while(true){
//接收客户端的请求,accept为阻塞方法
Socket socket=serverSocket.accept();
//接收客户端数据
DataInputStream inputStream=new DataInputStream(socket.getInputStream());
//响应客户端数据
DataOutputStream outputStream=new DataOutputStream(socket.getOutputStream());
String city = inputStream.readUTF();
System.out.println("正在查询城市数据:"+city);
String result=map.get(city);
outputStream.writeUTF(result);
}
}
}
-
客户端程序
public class WeatherClient {
public static void main(String[] args) throws Exception {
Scanner input=new Scanner(System.in);
System.out.print("请输入要查询的城市:");
String city = input.next();
Socket socket=new Socket("127.0.0.1",8888);
//发送数据到服务器
DataOutputStream outputStream=new DataOutputStream(socket.getOutputStream());
//接收服务器发送的数据
DataInputStream inputStream=new DataInputStream(socket.getInputStream());
outputStream.writeUTF(city);
//readUTF也是一个阻塞的方法,输出查询结果
String result = inputStream.readUTF();
System.out.println("查询结果:"+result);
}
}
总结:webservice的底层原理,就是socket
1. Web Service中的几个重要术语
1.1. WSDL:web service definition language
直译 : WebService定义语言
- 对应一种类型的文件.wsdl (告诉客户端如何调用接口)
- 定义了web service的服务器端与客户端应用交互传递请求和响应数据的格式和方式
- 一个web service对应一个唯一的wsdl文档
1.2. SOAP:simple object access protocal
直译: 简单对象访问协议
- 是一种简单的、基于HTTP和XML的协议, 用于在WEB上交换结构化的数据
. soap消息:请求消息和响应消息
. http+xml片断
SOAP协议 = HTTP协议 + XML数据格式
SOAP1.1和SOAP1.2区别
-
相同点:
-
请求发送方式相同:都是使用POST
-
协议内容相同:都有Envelope和Body标签
-
-
不同点:
-
数据格式不同:content-type不同
-
SOAP1.1:text/xml;charset=utf-8
-
SOAP1.2:application/soap+xml;charset=utf-8
-
-
命名空间不同:
-
SOAP1.1:http://schemas.xmlsoap.org/soap/envelope/
-
SOAP1.2:http://www.w3.org/2003/05/soap-envelope
-
1.3. UDDI
UUDI:目录服务
UDDI:Universal Description, Discovery and Integration.可译为"通用描述,发现与集成服务"。 服务目录检索
企业可以使用它对 Web services 进行注册和搜索.
企业将自己提供的Web Service注册在UDDI,也可以使用别的企业在UDDI注册的web service服务,从而达到资源共享。UDDI旨在将全球的webservcie资源进行共享,促进全球经济合作。
UDDI现状:
目前大部分企业使用webservice并不是必须使用UDDI,因为用户通过WSDL知道了web service的地址,可以直接通过WSDL调用webservice。
2. jdk发布webservice服务
使用JAX-WS开发webservice
JAX-WS 的全称为 **Java API for XML-Based Webservices** ,从java5开始支持JAX-WS2.0版本,Jdk1.6.0_13以后的版本支持2.1版本,jdk1.7支持2.2版本
那么如何才可以发布一个WebService呢?
1:用Jdk1.6.0_13以后的版本发布一个WebService服务,并通过地址栏查看它的wsdl文档.
2:通过wsimport生成客户端代码,调用并查看运行的结果.(学会如何调用是我们的重点).
3:从互联网上获取一个WebService网络公开的服务,并使用工具生成客户端,调用并查看结果。
测试代码包括服务端和客户端的开发
2.1 服务端开发
@WebService
public class JdkWeatherbServer {
public String queryWeather(String cityName){
System.out.println("客户端查询参数:"+cityName);
String result="晴转多云";
return result;
}
public static void main(String[] args) {
Endpoint.publish("http://127.0.0.1:8888/weather", new JdkWeatherbServer());
System.out.println("服务器端启动....");
}
}
其中:
1、编写SEI(Service Endpoint Interface),为了开发方便我们用java类代替接口加实现类的方式。
2、java类中至少要有一个非静态的公开方法需要作为webservice服务方法。
3、public class WeatherServer上边要加上@WebService,表示webservice的发布类。
4、使用Endpoint.publish()方法发布服务。
2.2查看使用说明书(wsdl)
在地址栏输入(注意后面的参数?wsdl)
http://127.0.0.1:8888/weather?wsdl
2、 这里注意目前不是访问webService,只是获取一个用于描述WebService的说明文件,即:wsdl文件,我们需要看懂wsdl的文件,这样就知道服务端究竟发布什么样的服务,提供什么样的方法,需要传递什么类型的参数,方法的返回值类型是什么等。.
3、 wsdl- 即WebServiceDescriptionLanguage,是以XML文件形式来描述WebService的”说明书”,有了说明书,我们才可以知道如何使用或是调用这个服务。
这里从下向上看:
其中:
1:JdkWeatherServerService为视图服务
2:WeatherServerPort为调用的端口,即getJdkWeatherServerPort()方法
3:JdkWeatherServer为接口,接口中提供一个queryWeather()的方法
2.3 客户端开发
wsimport是jdk自带的,可以根据wsdl文档生成客户端调用代码的工具.当然,无论服务器端的WebService是用什么语言写的,都将在客户端生成Java代码.服务器端用什么语言编写的并不重要.
wsimport.exe位于JAVA_HOME\bin目录下.
例如:在cmd的窗口下执行wsimport可以看到
Wsimport 程序可以根据wsdl webservice说明书 生成客户端调用代码!
常用参数为:
• -d<目录> - 将生成.class文件。默认参数。
• -s<目录> - 将生成.java文件。
• -p<生成的新包名> -将生成的类,放于指定的包下。
• (wsdlurl) - http://server:port/service?wsdl,必须的参数。
wsimport其他参数说明,我们经常使用的参数为-d,-s,-p
-d<目录>将会生成.class文件.
示例:wsimport –d . http://127.0.0.1:8888/weather?wsdl
-s<目录>将会生成.java文件.
示例:wsimport –s . http://127.0.0.1:8888/weather?wsdl
-p<包名>将生成的文件(.java或是.class修改成指定的包名)
示例:wsimport -s . -p com.bruceliu.client1 http://127.0.0.1:8888/weather?wsdl
示例:
C:/> wsimport -s . -p com.bruceliu.client1 http://127.0.0.1:8888/weather?wsdl
注意:-s不能分开,-s后面有个小点
在cmd的窗口执行:
在C盘看到客户端生成的java文件:
将.java的文件整个包拷贝到客户端的工程下,然后使用WeatherClient的客户端测试,测试代码如下:
public class JdkWeaterClient {
public static void main(String[] args) {
//定义服务视图
JdkWeatherbServerService jdkWeatherbServerService=new JdkWeatherbServerService();
//通过服务视图找到对应的端口类型,即(portType)
JdkWeatherbServer jdkWeatherbServer=jdkWeatherbServerService.getJdkWeatherbServerPort();
//通过端口类型调用webService方法
String result = jdkWeatherbServer.queryWeather("北京");
System.out.println("天气:"+result);
}
}
其中JdkWeatherServiceService为服务器类
其中JdkWeatherServer为接口类
其中getWeatherServerPort为端口类型
其中queryWeather为调用的方法
这四个参数在wsdl的使用说明书中都会看到。
l 通常用于描述服务的名称,即new WeatherServerService ();-通常称为服务名。
l 在元素内的即new JkdWeatherServerService ().getJdkWeatherServerPort (); - 通常称为服务端口。
l 是服务接口名。即:
JdkWeatherServer weatherServer = new JdkWeatherServerService ().getJdkWeatherServerPort ();
3. 调用第三方提供的webService服务
互联网上面有很多的免费webService服务,我们可以调用这些免费的WebService服务,将一些其他网站的内容信息集成到我们的Web应用中显示,下面就以获取天气预报数据和查询国内手机号码归属地为例进行说明。
气象中心的管理系统将收集的天气信息并将数据暴露出来(通过WebService Server), 而各大站点的应用就去调用它们得到天气信息并以不同的样式去展示(WebService Client).
Webservice的网络网址:www.webxml.com.cn
4 JAX-WS注解 webservice的注解开发
如何才可以修改wsdl文件的内容?
使用WebService的注解。WebService的注解都位于javax.jws包下:
• **@WebService-**定义服务,在public class上边
l name属性:接口名称
l portName属性:端口名称
l serviceName属性:定义服务器端的类
l targetNamespace属性:目标包文件
• **@WebMethod-**定义方法,在公开方法上边
l @WebMethod对所有非静态的公共方法对外暴露为服务.
l 对于静态方法或非public方法是不可以使用@WebMethod注解的.
l 对public方法可以使用@WebMethod(exclude=**true)**定义为非对外暴露的服务。
• **@WebResult-定义返回值,在方法返回值前边
l 对方法的返回值进行配置
• **@WebParam-定义参数,在方法参数前边
l 对方法的参数进行配置
1:通过WebService的注解,可以更加形像的描述Web服务。从而生成WSDL文档。
2:当修改了WebService注解之后,同时会影响客户端生成的代码。
3:调用的方法名和参数名也发生了变化。
4:服务类加上注解以后再次通过wsimport生成源代码然后调用。
服务端代码
@WebService(name="myName",portName="myPortName",serviceName="myServiceName",targetNamespace="com.webservice")
public class WeatherServer01 {
@WebMethod(operationName="myMethod")
public @WebResult(name="myResult") String queryWeather(@WebParam(name="myParam") String cityName){
if("北京".equals(cityName)){
return "晴天白云";
}else if("成都".equals(cityName)){
return "阴雨绵绵";
}else{
return "风平浪静";
}
}
public static void main(String[] args) {
Endpoint.publish("http://127.0.0.1:8888/weather", new WeatherServer01());
System.out.println("天气服务已经发布.....");
}
}
生成的WSDL
- 客户端
public class MyClient {
public static void main(String[] args) {
MyName myName = new MyServiceName().getMyPortName();
String result = myName.myMethod("成都");
System.out.println("当前天气:"+result);
}
}
5. CXF安装和配置
5.1CXF简介
Apache CXF = Celtix + XFire
1)CXF是一个开源的webservice框架,提供很多完善功能,可以实现快速开发
2)CXF支持的协议:SOAP1.1/1.2,REST
3)CXF支持数据格式:XML,JSON(仅在REST方式下支持)
4)并可以与Spring进行快速无缝的整合
5)灵活的部署:可以运行有Tomcat,Jboss,Jetty(内置),IBMWS,BeaWS上面。
5.2 CXF的环境变量的配置
环境变量:
• JAVA_HOME,
• CXF_HOME
• Path = %JAVA_HOME%\bin;%CXF_HOME%\bin;
• CLASSPATH=.;%CXF_HOME%\lib\cxf-manifest.jar
5.3 CXF的安装和配置
下载:http://cxf.apache.org/download.html
直接解压到某个盘下就可以使用:
6.cxf发布WebService
6.1服务端程序
(1)创建一个java工程,添加所需要依赖的Jar包(目录下全部的jar包)
(2)创建SEI接口,要加入@WebService接口
备注:**@BindingType(SOAPBinding.SOAP12HTTP_BINDING)**表示发布SOAP1.2的服务端
@WebService
@BindingType(SOAPBinding.SOAP12HTTP_BINDING)
public interface WeatherInterface {
public String queryWeather(String cityName);
}
(3)创建SEI接口实现类
public class WeatherInterfaceImpl implements WeatherInterface {
@Override
public String queryWeather(String cityName) {
if ("成都".equals(cityName)) {
return "冷且霾";
} else {
return "暖且晴";
}
}
}
(4)发布服务(使用JaxWsServerFactoryBean发布)
public class WeatherServer {
// 用JaxWsServerFactoryBean发布服务,设置3个参数,1.服务接口;2.服务实现类;3.服务地址;
public static void main(String[] args) {
JaxWsServerFactoryBean factoryBean = new JaxWsServerFactoryBean();
// 设置服务接口
factoryBean.setServiceClass(WeatherInterface.class);
// 设置服务实现类
factoryBean.setServiceBean(new WeatherInterfaceImpl());
// 设置服务地址
factoryBean.setAddress("http://127.0.0.1:8888/weather");
// 发布
factoryBean.create();
System.out.println("服务发布成功...");
}
}
附录:
服务器端发布的类
l ServerFactoryBean
l JaxWsServerFactoryBean(建议使用)
2个对象,都可以发布服务端,但是我们建议使用JaxWsServerFactoryBean,因为它生成的wsdl文件更加规范。
服务器端的拦截器:
// 输入拦截器
jaxWsServerFactoryBean.getInInterceptors().add(new LoggingInInterceptor());
// 输出拦截器
jaxWsServerFactoryBean.getOutInterceptors().add(new LoggingOutInterceptor());
目的是用来输出响应的头部信息,可以看到soap协议的格式(http请求+Xml数据):
例如:
Soap1.2:
----------------------------
ID: 1
Address: http://127.0.0.1:8888/weather
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml; charset=UTF-8
Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], Content-Length=[202], content-type=[text/xml; charset=UTF-8], Host=[127.0.0.1:8888], Pragma=[no-cache], SOAPAction=[""], User-Agent=[Apache-CXF/3.2.6]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:queryWeather xmlns:ns2="http://demo.weather.com/"><arg0>成都</arg0></ns2:queryWeather></soap:Body></soap:Envelope>
--------------------------------------
十月 06, 2018 11:07:22 上午 org.apache.cxf.services.WeatherInterfaceService.WeatherInterfacePort.WeatherInterface
信息: Outbound Message
---------------------------
ID: 1
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml
Headers: {}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:queryWeatherResponse xmlns:ns2="http://demo.weather.com/"><return>冷且霾</return></ns2:queryWeatherResponse></soap:Body></soap:Envelope>
--------------------------------------
(5)测试服务是否发布成功,阅读使用说明书.直接访问即可。
6.2 客户端程序
(1)生成客户端代码
1.1 **wsdl2java命令是CXF提供的生成客户端的工具,他和wsimport类似,可以根据WSDL生成客户端代码**
1.2 wsdl2java常用参数:
-d,指定输出目录
-p,指定包名,如果不指定该参数,默认包名是WSDL的命名空间的倒序
1.3 **wsdl2java支持SOAP1.1和SOAP1.2**
完整示例:
wsdl2java -p com.weather.client -d . http://127.0.0.1:8888/weather?wsdl
//根据指定说明书地址生成客户端代码 -p指定生成的包名 -d .表示输出到当前目录
(2)使用说明书,使用生成代码调用服务端
//使用JaxWsProxyFactoryBean调用服务端,设置2个参数,1.设置服务接口;
public class WeatherClient {
public static void main(String[] args) {
JaxWsProxyFactoryBean factoryBean = new JaxWsProxyFactoryBean();
// 设置服务接口
factoryBean.setServiceClass(WeatherInterface.class);
// 设置服务地址
factoryBean.setAddress("http://127.0.0.1:8888/weather");
// 获取服务接口实例
WeatherInterface weatherInterface = factoryBean
.create(WeatherInterface.class);
// 调用查询方法
String string = weatherInterface.queryWeather("成都");
System.out.println(string);
}
}
7. CXF+Spring整合
7.1服务器程序
(1)创建一个web工程
(2)导入jar包
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.webservice.demo</groupId>
<artifactId>Server1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<!-- 集中定义依赖版本号 -->
<properties>
<junit.version>4.12</junit.version>
<spring.version>4.3.8.RELEASE</spring.version>
<cxf.version>3.2.6</cxf.version>
</properties>
<dependencies>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-core</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.2.6</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.2.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<version>3.1</version>
</configuration>
</plugin>
<!-- java编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
(3)拷贝cxf开发的java工程的代码到web工程中,如图:
@WebService
@BindingType(SOAPBinding.SOAP12HTTP_BINDING)
public interface WeatherInterface {
public String queryWeather(String cityName);
}
public class WeatherInterfaceImpl implements WeatherInterface {
@Override
public String queryWeather(String cityName) {
if ("成都".equals(cityName)) {
return "冷且霾";
} else {
return "暖且晴";
}
}
}
(4)配置Spring配置文件 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:cxf="http://cxf.apache.org/core"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd">
<!--jaxws:server发布SOAP协议的服务 ,对JaxWsServerFactoryBean类封装 -->
<!--
发布一个服务
id="aa":表示惟一标识
address:表示发布服务的项目地址,weatherServer此时就表示访问url的时候,在?wsdl之前要存在weatherServer,也就是weatherServer?wsdl
serviceClass:表示指定发布服务的接口的路径
-->
<jaxws:server serviceClass="com.webservice.demo.WeatherInterface" address="/weather">
<jaxws:serviceBean>
<ref bean="weatherInterface" />
</jaxws:serviceBean>
<!--配置拦截器 -->
<jaxws:inInterceptors>
<ref bean="inInterceptor" />
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<ref bean="outInterceptor" />
</jaxws:outInterceptors>
</jaxws:server>
<!-- 配置服务实现类 -->
<bean name="weatherInterface" class="com.webservice.demo.WeatherInterfaceImpl"></bean>
<!--配置拦截器bean -->
<bean name="inInterceptor" class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
<bean name="outInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>
</beans>
第五步:配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>D</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 添加Spring的上下文环境监听 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置CXF的Servlet -->
<servlet>
<servlet-name>CXF</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CXF</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
</web-app>
第六步:启动tomcat,部署web工程到tomcat
第七步:测试服务是否发布成功
WSDL地址规则:http://localhost:端口号/项目名称/servlet拦截路径/服务名称?wsdl
7.2客户端程序
第一步:创建web工程作为客户端,引入cxf的jar包
第二步:生成客户端代码
执行命令:wsdl2java -p 包名 -d . 服务说明书地址
示例:
wsdl2java -p com.webservice.client -d . <http://localhost:8080/Server1/ws/weather?wsdl>
第三步:配置spring的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:cxf="http://cxf.apache.org/core"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd">
<!--jaxws:client实现客户端配置,对JaxWsProxyFactoryBean类封装 -->
<jaxws:client id="weatherClient" address="http://127.0.0.1:8080/Server1/ws/weather"
serviceClass="com.webservice.client.WeatherInterface"></jaxws:client>
</beans>
第四步:初始化spring上下文,获取接口实现类,调用查询方法
public class WeatherClient {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
WeatherInterface weatherInterface = (WeatherInterface) context.getBean("weatherClient");
System.out.println(weatherInterface.queryWeather("北京"));
}
}
8.CXF发布REST服务
8.1什么是REST
- 定义:REST就是一种编程风格,它可以精确定位网上资源(服务接口、方法、参数)
- REST支持数据格式:XML、JSON
- REST支持发送方式:GET,POST
需求
- 第一个:查询单个学生
- 第二个:查询多个学生
8.2 实现
8.2.1 服务端
开发步骤:
-
导入jar
-
创建学生类,需要加入@ XmlRootElement
@XmlRootElement(name="student")//@XmlRootElement可以实现对象和XML数据之间的转换
public class Student {
private long id;
private String name;
private Date birthday;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
- 创建SEI接口
@WebService
@Path("/student")//@Path("/student")就是将请求路径中的“/student”映射到接口上
public interface StudentInterface {
/**
* 根据id查询单个学生
* @param id
* @return
*/
@GET //指定请求方式,如果服务端发布的时候指定的是GET(POST),那么客户端访问时必须使用GET(POST)
@Produces(MediaType.APPLICATION_XML_VALUE)//指定服务数据类型
@Path("/query/{id}")//@Path("/query/{id}")就是将"/query"映射到方法上,"{id}"映射到参数上,多个参数,以“/”隔开,放到“{}”中
public Student queryStuById(@PathParam("id")long id);
/**
* 根据查询多个学生
* @param name
* @return
*/
@GET
@Produces({MediaType.APPLICATION_XML_VALUE,"application/json;charset=utf-8"})
@Path("/querylist/{name}")
public List<Student> queryStudentList(@PathParam("name") String name);
}
-
创建SEI接口实现类
public class StudentInterfaceImpl implements StudentInterface { @Override public Student queryStuById(long id) { Student st = new Student(); st.setId(id); st.setName("张三"); st.setBirthday(new Date()); return st; } @Override public List<Student> queryStudentList(String name) { Student st = new Student(); st.setId(110); st.setName("张三"); st.setBirthday(new Date()); Student st2 = new Student(); st2.setId(120); st2.setName("李四"); st2.setBirthday(new Date()); List<Student> list = new ArrayList<Student>(); list.add(st); list.add(st2); return list; } }
- 发布服务
public class StudentServer { public static void main(String[] args) { // JAXRSServerFactoryBean发布REST的服务 JAXRSServerFactoryBean factoryBean = new JAXRSServerFactoryBean(); // 设置服务实现类 factoryBean.setServiceBean(new StudentInterfaceImpl()); // 设置资源类,如果有多个资源类,可以以“,”隔开。 factoryBean.setResourceClasses(StudentInterfaceImpl.class); // 设置服务地址 factoryBean.setAddress("http://localhost:8888/user"); // 发布服务 factoryBean.create(); System.out.println("服务已经发布....."); } }
-
测试服务
测试查询单个学生:http://localhost:8888/user/student/query/1002
结果如下:
<student> <birthday>2017-11-19T21:39:18.449+08:00</birthday> <id>1002</id> <name>张三</name> </student>
测试查询多个学生:http://localhost:8888/user/student/querylist/1002
GET请求默认是xml类型:
结果如下:
<students> <student> <birthday>2017-11-19T21:40:16.609+08:00</birthday> <id>110</id> <name>张三</name> </student> <student> <birthday>2017-11-19T21:40:16.609+08:00</birthday> <id>120</id> <name>李四</name> </student> </students>
多个类型选择:http://localhost:8888/user/student/querylist/1002?_type=json
结果如下:
{"student":[ {"birthday":"2017-11-19T21:41:28.204+08:00","id":110,"name":"张三"}, {"birthday":"2017-11-19T21:41:28.204+08:00","id":120,"name":"李四"} ] } http://localhost:8888/user/student/querylist/1002?_type=xml
备注:
如果服务端发布时指定请求方式是GET(POST),客户端必须使用GET(POST)访问服务端,否则会报异常 如果在同一方法上同时指定XML和JSON媒体类型,默认返回XML
8.2.2 客户端
public class HttpClient { public static void main(String[] args) throws Exception { System.out.println("haha"); // 第一步:创建服务地址,不是WSDL地址 URL url = new URL("http://localhost:8888/user/student/querylist/1002?_type=json"); // 第二步:打开一个通向服务地址的连接 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); // 第三步:设置参数 // 3.1发送方式设置:POST必须大写 connection.setRequestMethod("GET"); // Post 请求不能使用缓存 connection.setUseCaches(false); connection.setInstanceFollowRedirects(true); // 3.2设置数据格式:content-type // 3.3设置输入输出,因为默认新创建的connection没有读写权限, connection.setDoInput(true); connection.setDoOutput(true); // 第五步:接收服务端响应,打印 int responseCode = connection.getResponseCode(); if (200 == responseCode) {// 表示服务端响应成功 InputStream is = connection.getInputStream(); InputStreamReader isr = new InputStreamReader(is,"UTF-8"); BufferedReader br = new BufferedReader(isr); StringBuilder sb = new StringBuilder(); String temp = null; while (null != (temp = br.readLine())) { sb.append(temp); } System.out.println(sb.toString()); // dom4j解析返回数据,课下作业 is.close(); isr.close(); br.close(); } } }
9 CXF+Spring整合REST服务
9.1 服务端
开发步骤:
第一步:创建web项目(引入jar包)引入REST所依赖的包
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxrs</artifactId> <version>3.2.6</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.jaxrs</groupId> <artifactId>jackson-jaxrs-json-provider</artifactId> <version>2.4.1</version> </dependency> <dependency> <groupId>javax.ws.rs</groupId> <artifactId>jsr311-api</artifactId> <version>1.1.1</version> </dependency>
第二步:创建POJO类(同上)
@XmlRootElement(name="student")//@XmlRootElement可以实现对象和XML数据之间的转换 public class Student { private long id; private String name; private Date birthday; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
第三步:创建SEI接口(同上)
@WebService @Path("/student")//@Path("/student")就是将请求路径中的“/student”映射到接口上 public interface StudentInterface { /** * 根据id查询单个学生 * @param id * @return */ @GET //指定请求方式,如果服务端发布的时候指定的是GET(POST),那么客户端访问时必须使用GET(POST) @Produces(MediaType.APPLICATION_XML_VALUE)//指定服务数据类型 @Path("/query/{id}")//@Path("/query/{id}")就是将"/query"映射到方法上,"{id}"映射到参数上,多个参数,以“/”隔开,放到“{}”中 public Student queryStuById(@PathParam("id")long id); /** * 根据查询多个学生 * @param name * @return */ @GET @Produces({MediaType.APPLICATION_XML_VALUE,"application/json;charset=utf-8"}) @Path("/querylist/{name}") public List<Student> queryStudentList(@PathParam("name") String name); }
第四步:创建SEI实现类(同上)
public class StudentInterfaceImpl implements StudentInterface { @Override public Student queryStuById(long id) { Student st = new Student(); st.setId(id); st.setName("张三"); st.setBirthday(new Date()); return st; } @Override public List<Student> queryStudentList(String name) { Student st = new Student(); st.setId(110); st.setName("张三"); st.setBirthday(new Date()); Student st2 = new Student(); st2.setId(120); st2.setName("李四"); st2.setBirthday(new Date()); List<Student> list = new ArrayList<Student>(); list.add(st); list.add(st2); return list; } }
第五步:配置Spring配置文件,applicationContext.xml
使用 jaxrs:server发布服务:设置1.服务地址;2.服务实现类
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:cxf="http://cxf.apache.org/core" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd"> <!-- 发布REST的服务,对JAXRSServerFactoryBean类封装 --> <jaxrs:server address="/user"> <jaxrs:providers> <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider" /> </jaxrs:providers> <jaxrs:serviceBeans> <ref bean="studentInterface" /> </jaxrs:serviceBeans> </jaxrs:server> <!-- 配置服务实现类 --> <bean name="studentInterface" class="com.webservice.service.impl.StudentInterfaceImpl"></bean> </beans>
第六步:配置web.xml
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- 添加Spring的上下文环境监听 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 配置CXF的Servlet --> <servlet> <servlet-name>CXF</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CXF</servlet-name> <url-pattern>/ws/*</url-pattern> </servlet-mapping>
第七步:部署到tomcat下,启动tomcat
第八步:测试服务
REST服务的使用说明书地址:
http://localhost:8080/Server1/ws/user/student/querylist/2342342?_type=json
10.2 客户端
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>测试页面</title> <script type="text/javascript"> window.onload = function() { document.getElementsByTagName("input")[0].onclick = function() { var xhr; if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else { xhr = new ActiveXObject("Microsoft.XMLHTTP"); } xhr.open("GET","http://localhost:8080/Server1/ws/user/student/querylist/2342342?_type=json"); xhr.send(); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { if (xhr.status == 200) { var result = xhr.responseText; var stu = JSON.parse(result); for(var i=0;i<stu.length;i++){ alert(stu[i].id+"--"+stu[i].name+"--"+stu[i].birthday); } } } } }; } </script> </head> <body> <input type="button" value="点击查询" /> </body> </html>
10 面试题
- 什么是WebService,WebService有什么作用
- 什么是SOAP、WSDL
- WebService一般在什么时候使用,你在项目中什么地方应用到了
- 常见的WebService框架比较
- Spring如何整合CXF框架
- 如何理解RESTful
- 如何在项目中使用RESTful