WebService最早是微软提出了一种以XML为载体网络信息传输的规范,现在几乎所有的语言与平台都支持,带有状态机制,不依赖于容器,可以发送一个xml作为其请求内容.
WebService通常是基于http的远程方法调用(RMI),号称是可以返回远程对象,一般来说客户端可以象调用本地方法一样调用WebService的方法。
在各种各样的对 WebService的解释版本中, 足够官方, 却并没有太大帮助, 在我理解, WebService 是一种应用程序组件, 提供一个接口, client端 提供参数, 通过多种方式发起请求,获取到相应的返回值, 可以通过WebService进行网络通信, 同时可以实现不同平台间的数据共享, 仅此而已.
参考链接:WebService的两种方式SOAP和REST比较 (转)
不过需要注意的一点是,WebService有两种方式:
SOAP(Simple Object Access Protocol), 简单对象访问协议.
简单对象访问协议是交换数据的一种协议规范,是一种轻量的、简单的、基于XML(标准通用标记语言下的一个子集)的协议,它被设计成在WEB上交换结构化的和固化的信息。
SOAP的概念理解:
SOAP消息基本上是从发送端到接收端的单向传输,但它们常常结合起来执行类似于请求 / 应答的模式。所有的 SOAP消息都使用 XML 编码。一条 SOAP消息就是一个包含有一个必需的 SOAP 的封装包,一个可选的 SOAP 标头和一个必需的 SOAP 体块的 XML 文档。
SOAP 通讯协议使用 HTTP 来发送XML 格式的信息,SOAP 把 XML 的使用代码化为请求和响应参数编码模式, 并用HTTP 作传输。
具体地讲, 一个SOAP 方法可以简单地看作遵循SOAP编码规则的HTTP请求和响应, 一个 SOAP终端则可以看作一个基于HTTP 的URL, 它用来识别方法调用的目标。
在我目前的理解中, SOAP可以概括为以下要点:
SOAP消息用XML进行编码, 同时拥有相匹配的编码规则, 便于通信, 解析, 交互;
同时在HTTP上 发送SOAP的语义会被映射到HTTP语义, 且不仅限于HTTP协议, SOAP也可以绑定至TCP/UDP协议上;
通过HTTP发送 XML格式信息, 服务器端 转换解析相应的 信息, 处理并响应.
个人思考:
如果想要深入了解 SOAP, 需要了解 相应的 XML封装, 同时对 XML扩展文本标记语言有一定程度的了解. 粗浅的使用, 目前足以;
WebService的另一大核心:
WSDL(Web Services Description Language) 网络服务描述语言
如同字面意思所描述的那样,WSDL 是这样一个基于 XML 的语言,用于描述 Web Service 及其函数、参数和返回值。因为是基于 XML 的,所以 WSDL 既是机器可阅读的,又是人可阅读的.(XML的自描述性)
以下是几个元素定义,有助于更直观的理解 WSDL:
<message> web service 使用的消息
<types> web service 使用的数据类型
<binding> web service 使用的通信协议
需要了解更多的话, 官方文档更为详细: WSDL 文档
UDDI(Universal Description Discovery and Integration), UDDI 是一种目录服务,企业可以使用它对 Web services 进行注册和搜索。
总结来说:
SOAP定义协议层交互, 映射, WSDL用来描述 WebService, UDDI 用来进行WebService
在开始, 仍然是参考链接, 看过后,相信会对 Rest有一个比较全面的了解:
REST(Representational State Transfer) 表现层状态转移;
REST通常基于使用HTTP,URI,和XML 以及HTML(标准通用标记语言下的一个应用)这些现有的广泛流行的协议和标准。
在阅读上述博文前,可以先了解这样几个概念, 同时也是对Rest的一种描述:
参考:REST无状态的理解
首先必须清楚的是 Rest是一种软件架构风格而并非是标准;
Rest中并非是向以往的java项目设计那样, 以业务逻辑/功能为考虑出发点, 而是以 资源 为出发点考虑的, 因此很重要的一个特性为 面向资源的接口设计。
资源: 操作实体都作为资源来看待, 如在一个商城项目中, 商品类别, 具体的商品, 物流, 用户资料 等都会被看做资源,你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。 而 Client端的操作都是对资源本身的操作。
表现层: 指的是资源的表现层,它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。
当用户登录后, 一般需要保存对应的 用户信息, 用户信息本身就是一种资源, 特定的用户 对应着用户资源的一种状态, 而在服务器端取出存储在Session中的用户资源时, 实际上也就意味着 服务器保存着用户资源的 状态信息, 但在 Rest架构设计中, 实际上是期望请求方提供的,所以传递Session ID被认为是unRESTful的做法。
深感自己水平有限, 知识储备不足, 因此仅仅是将REST简介-Amber-Garden博文的要点提出来:
除了上述关于资源状态的概念理解之外,还有以下信息:
Rest架构设计有以下几条约束:
使用客户/服务器模型。客户和服务器之间通过一个统一的接口来互相通讯。
层次化的系统。在一个REST系统中,客户端并不会固定地与一个服务器打交道。
无状态。在一个REST系统中,服务端并不会保存有关客户的任何状态。也就是说,客户端自身负责用户状态的维持,并在每次发送请求时都需要提供足够的信息。
可缓存。REST系统需要能够恰当地缓存请求,以尽量减少服务端和客户端之间的信息传输,以提高性能。
统一的接口。一个REST系统需要使用一个统一的接口来完成子系统之间以及服务与用户之间的交互。这使得REST系统中的各个子系统可以独自完成演化。
其中无状态 和 统一接口 又是 较为特别的约束条件:
无状态: 理解了状态, 也就明白了什么叫无状态, 在我的理解中, 通俗的来讲就是, 对于一个请求, 请求中已经包含了足够的信息, 并不需要服务器端自身保存任何资源, 用以进行对请求的处理, 资源的返回等。
统一接口:
每个资源都拥有一个资源标识。每个资源的资源标识可以用来唯一地标明该资源。
消息的自描述性。在REST系统中所传递的消息需要能够提供自身如何被处理的足够信息。例如该消息所使用的MIME类型,是否可以被缓存等。
资源的自描述性。一个REST系统所返回的资源需要能够描述自身,并提供足够的用于操作该资源的信息,如如何对资源进行添加,删除以及修改等操作。也就是说,一个典型的REST服务不需要额外的文档对如何操作资源进行说明。
HATEOAS。即客户只可以通过服务端所返回各结果中所包含的信息来得到下一步操作所需要的信息,如到底是向哪个URL发送请求等。也就是说,一个典型的REST服务不需要额外的文档标示通过哪些URL访问特定类型的资源,而是通过服务端返回的响应来标示到底能在该资源上执行什么样的操作。一个REST服务的客户端也不需要知道任何有关哪里有什么样的资源这种信息。
除了资源的状态, 定位以外, 则是对资源的操作: 基于HTTP, 则有:
GET 读取某个资源。
DELETE 删除某个资源
POST POST动词会在目标URI之下创建一个新的子资源。
PUT则是根据请求创建或修改特定位置的资源。此时向服务端发送的请求的目标URI需要包含所处理资源的ID.(对于put请求仍然不是很理解)
Rest优点:
优点是基于特性而来的, 最大的特性便是 无状态。因为无状态原则的特性,让RESTful在分布式系统中得到了广泛的应用,它改善了分布式系统的可见性、可靠性以及可伸缩性,同时有效的降低了Client与Server之间的交互延迟。
其次,无状态请求有较强的容错性和可伸缩性。如果一台服务器宕机,无状态请求可以透明地交由另一台可用Server来处理,而有状态的请求则会因为存储请求状态信息的Server宕机而承担状态丢失的风险。
至于 Rest架构设计等其他, 在博文中有说明, 且对WebService 影响不大, 在此就不再说明。
在对WebService 及 SOAP 和 Rest有了基本的了解后, 接下来就是 WebService在Java中的简单实现.
参考链接: https://www.cnblogs.com/xdp-gacl/p/4259481.html;
重点在这样几个地方:
在 接口类和实现类 均需要 @WebService注解, 同时在接口类的方法上, 采用@WebMethod接口
在 main方法中, 用 EndPoint.publish(address, 实现类); 发布即可
访问的时候通过 address.wsdl 即可访问发布的 WebService;
打开命令行窗口,切换到src目录,执行"wsimport -keep address?wsdl生成客户端代码.
使用生成的java类创建实现类,调用对应的方法 接口.
类级别:
@javax.jws.WebService(targetNamespace = "", name = "",serviceName = "")
targetNamespace :生成的 WSDL 中使用的名称空间.
name:Web Service 的名称,映射到 WSDL 文件中的
serviceName: Web Service 的服务名,映射到 WSDL 文件
@javax.jws.soap.SOAPBinding(parameterStyle = javax.jws.soap.SOAPBinding.ParameterStyle.BARE)
方法级别
@javax.jws.WebResult(name = "", targetNamespace = "", partName = "")
name:指定生成的 WSDL 中的操作结果的名称, 默认名称“return”。
@javax.jws.WebMethod(operationName="")
operationName: 指定方法公开的公共操作名,映射到 WSDL 文件中的
@javax.jws.WebParam(name="",targetNamespace="")
name: 指定输入参数名,而不是该参数的Java;
代码如下:
@WebService
public interface IHelloWorld {
@WebMethod
String hello();
}
@WebService
public class HelloWorldImpl implements IHelloWorld {
@override
public String hello() {
return "helloWorld";
}
}
public class Publish {
public static void main(String[] args) {
String address = "http://localhost:80/Server/Test";
Endpoint.publish(address, new HelloWorldImpl());
System.out.println("发布成功");
}
}
//访问地址为:
//http://localhost:80/Server/Test?wsdl
//在命令行中,切换到 Src文件夹下 , wsimport -keep http://localhost:80/Server/Test?wsdl 即可.
public class WebServiceDemo {
public static void main(String[] args) {
//创建一个用于产生WebServiceImpl实例的工厂,WebServiceImplService类是wsimport工具生成的
HelloWorldImplService factory = new HelloWorldImplService();
//通过工厂生成一个WebServiceImpl实例,WebServiceImpl是wsimport工具生成的
HelloWorldImpl wsImpl = factory.getHelloWorldImplPort();
//调用WebService的sayHello方法
String resResult = wsImpl.hello();
}
}
通过以上代码, 便实现了一个 WebService的发布, 而后访问对应的网址, 生成对应的Java, 调用并使用相应的方法及接口.
在天气, 电话号码,等等的已经开放的WebService服务在:
http://www.webxml.com.cn/zh_cn/index.aspx
如: 访问2400多个城市天气预报Web服务 即可使用:
http://ws.webxml.com.cn/WebServices/WeatherWS.asmx?wsdl 通过 wsimport -keep url 即可;
当然在执行的时候可能会报错:
无法将名称 ‘s:schema‘ 解析为 ‘element declaration‘ 组件。
这个时候需要在浏览器中 打开相应的 链接, 右键另存到本地,将文件中所有出现
而后使用 wsimport -keep 文件路径即可.
在这之前首先要提到 Java 开发WebService的几种框架:
参考链接: JAVA开发Web Service几种框架介绍
Axis,axis2,Xfire,CXF以及JWS, 其中 JWS 为 JavaWebService, 上面所说的 使用 JDK开发 WebService即为 JWS方式, 在 Java 1.6以后支持.
如果你需要把你的实现侧重JAVA并希望和Spring集成,CXF就是更好的选择,特别是把你的Web Service嵌入其他的程序中。
下面开始谈谈 cxf 开发WebService, 个人也是才开始了解 WebService 因此仅仅做出入门级的 使用开发案例.
官方 jar 包下载:
http://cxf.apache.org/download.html
简单流程如下:
创建 JavaWeb项目
在创建 接口和实现类的过程 与JWS并没有区别, 不过不需要创建对应的 main方法, 毕竟是与 Spring相结合, 需要在 Web.xml 和 applicationContext.xml中配置即可.
下面是对应的代码:
@WebService
public interface WebServiceDemo {
//如果没有 WebMethod注解, 且配置属性, 则方法名为 print;
void print();
}
@WebService
public class WebServiceDemoImpl implements WebServiceDemo {
@Override
public void print() {
// TODO Auto-generated method stub
System.out.println("print");
}
}
//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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
<jaxws:endpoint id="webServiceDemo" implementor="zyx.webservice.demo.WebServiceDemoImpl" address="/webServiceDemo"/>
</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" version="3.0">
<display-name>WebServiceDemo</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>CXFService</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CXFService</servlet-name>
<url-pattern>/webservice/*</url-pattern>
</servlet-mapping>
</web-app>
项目启动后访问路径为:
http://localhost/WebServiceDemo/webservice/webServiceDemo?wsdl
至此:使用 SOAP方式, 发布一个 WebService项目已经介绍完了.
至于Rest的优点, 我就不再赘述, 在 1.4 节的介绍已经足够进行简单的开发了.
Java中 Restful 框架实在不少, 在这里我使用的是: Jersey
已经讲到这里了, 就将 Rest相关注解也讲一下, 有个大概的了解认识:
参考链接: REST 在 Java 中的使用
资源定位:正如之前提到的, 在统一接口 约束中的 子约束的第一条就是: 每个资源都要有 唯一标识 即 URI.
@ApplicationPath 标识应用路径,用于由@Path提供的所有资源uri的基本uri。当发布在一个servlet容器中,它的值可以使用web.xml中的servlet-mapping进行重写。
@path 标识要请求的资源类或类方法的uri路径。
@PathParam 将uri中指定的路径参数绑定到资源方法参数
@QueryParam 将http请求的Query参数绑定到资源方法参数,资源类的字段,或资源类的bean属性。
@FormParam 与上述用法类似,不过是将http请求的Form表单中的参数绑定到资源方法参数
@CookieParam 同上
@HeaderParam 指的是 http header的值
@MatrixParam(不太了解)将uri矩阵参数的值绑定到资源方法参数,资源类的字段
@DefaultValue 配置以上参数的默认值
//UserResource下的根路径
@Path("/user")
public class UserResource {
@GET
//传递参数 /user/user/zyx 则参数为 zyx;
@Path("/user/{userName}")
//访问方法的路径
@Produces(MediaType.APPLICATION_JSON)
///user/user/zyx?password=123 则password参数为123
public User getUser(@DefaultValue("zyx") @PathParam("userName") String userName, @QueryParam("password") String password) {
...
}
}
@BeanParam 将请求中的bean绑定到资源方法参数,资源类的字段,或资源类的bean属性
//bean的配置
@javax.xml.bind.annotation.XmlRootElement
public class UserBean{
@FormParam("userName")
private String userName;
@FormParam("age")
private int age;
...
}
//资源方法配置
@Path("/user")
public class UserRecource {
@javax.ws.rs.POST
@Path("/insertUserBean")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String insertUserBean(@BeanParam UserBean userBean){
...
}
}
数据格式:
@Consumes 定义一个资源类的方法或MessageBodyReader能够接受的媒体类型。方法级别的@Consumes会覆盖类级别的@Consumes。(指定接受的 Client端媒体类型)
@Produces 方法级别的@Produces会覆盖类级别的@Produces(用于定义方法的响应实体的数据类型)
@Produces("application/json");
指定多个MIME类型 @Produces({"application/json","application/xml"});
也可以用常量值得方式指定:
@Produces(value = {MediaType.APPLICATION_JSON})
媒体类型的常量值在javax.ws.rs.core.MediaType中
同时可以为每种类型定义质量因素: 质量因素是取值范围从0到1的小数值。如果不定义质量因素,那么该类型的质量因素默认为1, 如果同一请求路径对应不同的 处理 方法, 则根据 质量因素优先选取
参考链接: ttp://book.51cto.com/art/201701/529132.htm
@Encoded 不需要自动解码,直接使用编码后的请求值
代码如下:
在这里我使用的是 maven: pom.xml:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.com</groupId>
<artifactId>demo02</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>demo02 Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.ws.rs/jsr311-api -->
<!-- 加了会报错 -->
<!-- <dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<version>1.1.1</version>
</dependency> -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<!--jersey -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
<version>2.13</version>
</dependency>
<!--JAXB API -->
<dependency>
<groupId>javax.xml.ws</groupId>
<artifactId>jaxws-api</artifactId>
<version>2.1</version>
</dependency>
<!-- Json支持 -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.12</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.12</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.tomcat.maven/tomcat8-maven-plugin -->
<!-- <dependency>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat8-maven-plugin</artifactId>
<version>3.0-r1756463</version>
</dependency> -->
</dependencies>
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"
version="3.0">
<display-name>demo02</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<filter>
<filter-name>characterEncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>JerseyServlet</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<!-- 配置自己的资源加载类去加载资源 -->
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>cn.com.easy.resource.ApplicationAPI</param-value>
</init-param>
<!-- 配置默认的资源包路径,使用默认的配置类去加载资源 -->
<!-- <init-param> -->
<!-- <param-name>jersey.config.server.provider.packages</param-name> -->
<!-- <param-value>com.cisdi.jersey.api</param-value> -->
<!-- </init-param> -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JerseyServlet</servlet-name>
<url-pattern>/restful/*</url-pattern>
</servlet-mapping>
</web-app>
定义自己的资源加载器
/**
* 这个类就是将各种资源加载进来,暴露给client, 可以自由选择需要加载的资源, 日志等;
* @author Administrator
*
*/
public class ApplicationAPI extends ResourceConfig {
{
System.out.println("启动资源加载器");
}
public ApplicationAPI() {
register(Demo.class);
// 注册数据转换器
register(JacksonJsonProvider.class);
// 注册日志
register(LoggingFilter.class);
}
}
定义对应的java类, 也即接口的访问程序
@Path("/demo")
public class Demo {
@GET
@Path("/demo01/{username }")
@Produces(value = {MediaType.APPLICATION_JSON})
public String demo01(@PathParam("username") String username) {
return username;
}
}
再者就是 使用 Spring时的自定义配置, applicationContext.xml
<!-- 自动注册 -->
<context:annotation-config />
<!-- 扫描对应的包 -->
<context:component-scan base-package="cn.com.easy.restful" />
以上就是整个项目的搭建.
我觉得项目的创建并不难, 也并不是在以后应该关注的店, 而在开发中更应该注意的是, Restful的设计风格, 很容易一不小心就走偏了. 需要对 Restful 的 资源, 状态, 等概念理解到位, 这也是我最大的收获.
后感:
个人感觉, SpringMvc 的项目搭建方式与 Restful 方式挺相近, 不过关注点不同, 在 SpringMvc 的核心点 是功能与业务逻辑, 而在Rest中, 更重要的是 资源本身, 甚至于 Get Post 等方式的选取也更重要, 以便于理清楚Rest的 资源逻辑 问题.
原文:http://www.cnblogs.com/zyzdisciple/p/7868831.html