Eternity Rain

blog


  • Startseite

  • Archiv

  • Tags

RAML-To-JAX-RS_Maven插件工作原理

Veröffentlicht am 2017-05-16

前言

这篇关于raml to jax-rs 插件的文章旨在分析该插件的工作原理和对其部分源码进行解析,
如果你还不认识它,可以从《raml-for-jax-rs插件》解它并学习它的使用方法。

工作原理

raml to jax-rs 插件完成了从 raml -> java 的转换,这其中包括raml的解析工作以及java代码的生成工作。整个工作需要依赖三个主要的部分:

  • raml-java-parser
  • raml-jaxrs-codegen-core
  • ram-jaxrs-maven-plugin

其中后面两部分都属于raml-for-jax-rs项目中的子模块,由mulesoft开发,第一部分由raml-org组织开发。

raml-java-parser

功能

解析并验证raml

运行机制

说到运行机制,先上一个raml的例子:

#%RAML 0.8
title: Contacts App
version: v1
schemas:
  - tree: !include schema/tree.json
  - apiCreatedEntity: |
      {  "$schema": "http://json-schema.org/draft-03/schema",
         "type": "object",
         "description": "Response JSON for successful POST operation",
         "properties": {
           "id":  { "type": "string", "required": true }
         }
      }
  - apiContact: !include schema/api-contact.json

/contacts:
  # note, changing the displayName will change the name of the generated class in ${base.package.name}.resource
  displayName: Contacts
  /{id}:
    get:
      description: Retrieves specific contact by id
      responses:
        200:
          body:
            application/json:
               schema: tree
        400:
          description: Bad Request
        500:
          description: Internal Server Error

raml语法是在yaml的语法规范之上又定义了一套针对REST的规范,所以解析raml就分为yaml解析和raml解析两部分。
如果解析上面的raml,会有以下几步:

  1. 验证yaml语法,并将raml转化为树形结构对象;
  2. 树形对象进入raml验证阶段,程序以广度优先遍历的方式层层获取树形的node,同时依据已有的配置对该树对象进行验证,这里的配置不止一类:
    • 定义每种不同的node下允许出现的子node类型来控制raml语言中key的范围,如:title是必须有的标签;resouce 可以有get,post等action,但是不能在其下定义 “ben” 这样的action;
    • 定义node对应的handler可以控制raml中key对应的value的范围,如:body下面的mimeType可以是”application/json”,”application/xml”,”application/x-www-form-urlencoded”,”multipart/form-data”这几种;
    • 定义node对应的rule可以控制value的规则,如:baseUri标签对应BaseUriRule,该rule中通过正则规范了value必须符合URI结构。

通过这两步可以完全验证raml文件是否符合raml规范。

raml-jaxrs-codegen-core

功能

集成raml-java-parser对raml进行解析并验证,转换raml对象为java,这里提到一点,生成java model并写成java文件是由sun的 JCodeModel 工具来实现的。

运行机制:

生成过程中主要用到的几个类有 Context , AbstractGenerator , Generator , ClientGenerator , Configuration:

  • Configuration 用来hold生成过程中的参数;
  • Context 持有整个过程中 Configuration, Raml object,JCodeModel 实例 ,SchemaMapper;
  • AbstractGenerator : AbstractGenerator是Generator和ClientGenerator的模板类,提供addResourceMethod方法供它们实现,因为生成interface method的逻辑,Generator 和 ClientGenerator是不一样的。其他的功能比如生成interface,添加参数,标记注解,配置验证等公用部分在AbstractGenerator里直接提供。

生成过程在以上几个类的配合下进行:验证raml对象、解析json schema并生成java model、依据configuration 决定解析resource并生成interface model的方式、将mode写成java文件。

ram-jaxrs-maven-plugin

功能

提供raml-to-jaxrs 的maven插件功能,对外暴露raml生成java过程中可配置的参数,将控制权交由使用者。

运行机制

这里就是比较薄的一层了,仅仅是将用户配置的参数转变为 core 中的 Configuration对象,然后先去验证raml文件头部的"#%RAML **"行是否存在,接下来根据configuration调用Generator或 ClientGenerator的run()方法。

研究源码后的一些想法

基于以上的研究结果,现在可以根据需要,自由生成特定的java代码,同时也可以自定义raml新的语法规则。

raml转换为html,js,markdown

Veröffentlicht am 2016-12-12

通过raml将接口设计完成后,我们需要提供简洁可读的直观视图和说明文档。
这里我们通过node.js来实现转换

Raml2html

官方地址

raml版本支持

raml2html 4和更高的版本仅支持raml 1.0语法转换,对于raml 0.8语法,需要使用raml2html的3.x版本

安装

npm i -g raml2html

NOTE:如果是raml 0.8,执行 npm i -g raml2html@3.0.0

使用

raml2html –help
raml2html example.raml > example.html

Raml2md

官方地址

安装

npm i -g raml2md

使用

raml2md example.raml > example.md
raml2md -i example.raml -o example.md

以上两种方式都可以实现

Raml javascript 生成器

官方地址

安装

npm install raml-client-generator -g

使用

raml-to-client api.raml -o api-client -l javascript

XMLHttpRequest(XHR)详解

Veröffentlicht am 2016-11-30

引言

本文的阅读者,在阅读这篇文章时你需要拥有html,js,http的相关知识,这样你可以更好的理解我们所谈论的话题,因为我们并不会在文中对它们进行讲解。

简介

解释定义

XMLHttpRequest是一组API函数集,存在于浏览器的JS内核中,因此它可以被JavaScript这样web浏览器内嵌的脚本语言调用,通过HTTP在浏览器和web服务器之间收发XML或其它数据。

除XML之外,XMLHttpRequest还能用于获取其它格式的数据,如JSON或者甚至纯文本。

背景知识

XMLHttp最初是由微软公司发明的,在Internet Explorer 5.0中用作ActiveX对象,可通过JavaScript、VBScript或其它浏览器支持的脚本语言访问。Mozilla的开发人员后来在Mozilla 1.0中实现了一个兼容的版本。之后苹果电脑公司在Safari 1.2中开始支持XMLHttpRequest,而Opera从8.0版开始也宣布支持XMLHttpRequest。
大多数使用了XMLHttpRequest的设计良好的网页,会使用简单的JavaScript函数(ajax)将不同浏览器之间调用XMLHttpRequest的差异性屏蔽,该函数会自动检测浏览器版本并隐藏不同环境的差异。

特点

  1. XMLHttpRequest最大的好处在于可以动态地更新网页,它无需重新从服务器读取整个网页,也不需要安装额外的插件。该技术被许多网站使用,以实现快速响应的动态网页应用。例如:Google的Gmail服务、Google Suggest动态查找界面以及Google Map地理信息服务。
  2. 上一个特点我称之为异步更新,还有一个特点,我把它叫做异步提交(当然也支持同步提交了)。这个特性的异步针对的是服务端,也就是说可以异步请求服务端,这样不会因为服务端响应时间过长而让页面出现假死情况。

运行机制

这里有一篇文章介绍了JavaScript的运行机制。

通过其中如下片段可以发现XMLHttpRequest的实现原理:

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。
于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
具体来说,异步执行的运行机制如下:(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。
(3)一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于>是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。

下图就是主线程和任务队列的示意图。

enter image description here

只要主线程空了,就会去读取”任务队列”,这就是JavaScript的运行机制。这个过程会不断重复。

enter image description here

上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在”任务队列”中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取”任务队列”,依次执行那些事件所对应的回调函数。

代码

执行栈中的代码(同步任务),总是在读取”任务队列”(异步任务)之前执行。请看下面这个例子。

var req = new XMLHttpRequest();
req.open('GET', url);    
req.onload = function (){};    
req.onerror = function (){};    
req.send();

上面代码中的req.send方法是Ajax操作向服务器发送数据,它是一个异步任务,意味着只有当前脚本的所有代码执行完,系统才会去读取”任务队列”。所以,它与下面的写法等价。

var req = new XMLHttpRequest();
req.open('GET', url);
req.send();
req.onload = function (){};    
req.onerror = function (){};  

也就是说,指定回调函数的部分(onload和onerror),在send()方法的前面或后面无关紧要,因为它们属于执行栈的一部分,系统总是执行完它们,才会去读取”任务队列”。

Ajax

Ajax是基于XMLHttpRequest实现的,前面提到过,不同浏览器之间调用XMLHttpRequest会有差异性,如下:

Internet Explorer

xmlhttp_request = new ActiveXObject(“Msxml2.XMLHTTP.3.0″); //3.0或4.0, 5.0
xmlhttp_request = new ActiveXObject(“Msxml2.XMLHTTP”);
xmlhttp_request = new ActiveXObject(“Microsoft.XMLHTTP”);

Mozilla﹑Netscape﹑Safari

xmlhttp_request = new XMLHttpRequest();

下面是Ajax源码:

var xmlHttp

function showHint(str)
{

  if (str.length==0)
    { 
    document.getElementById("txtHint").innerHTML="";
    return;
    }

  xmlHttp=GetXmlHttpObject()

  if (xmlHttp==null)
    {
    alert ("您的浏览器不支持AJAX!");
    return;
    }

  var url="gethint.asp";
  url=url+"?q="+str;
  url=url+"&sid="+Math.random();
  xmlHttp.onreadystatechange=stateChanged;
  xmlHttp.open("GET",url,true);
  xmlHttp.send(null);
} 

function stateChanged() 
{ 
if (xmlHttp.readyState==4)
{ 
document.getElementById("txtHint").innerHTML=xmlHttp.responseText;
}
}

function GetXmlHttpObject()
{
  var xmlHttp=null;
  try
    {
    // Firefox, Opera 8.0+, Safari
    xmlHttp=new XMLHttpRequest();
    }
  catch (e)
    {
    // Internet Explorer
    try
      {
      xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");
      }
    catch (e)
      {
      xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
      }
    }
return xmlHttp;
}

可以看到,Ajax只是对XMLHttpRequest进行了封装,将不同浏览器之间调用XMLHttpRequest的差异性对外屏蔽。

JavaScript运行机制详解

Veröffentlicht am 2016-11-30

单线程

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

任务队列

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。
于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
具体来说,异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。
(3)一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。

下图就是主线程和任务队列的示意图。

enter image description here

只要主线程空了,就会去读取”任务队列”,这就是JavaScript的运行机制。这个过程会不断重复。

事件和回调函数

“任务队列”是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在”任务队列”中添加一个事件,表示相关的异步任务可以进入”执行栈”了。主线程读取”任务队列”,就是读取里面有哪些事件。
“任务队列”中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入”任务队列”,等待主线程读取。
所谓”回调函数”(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。
“任务队列”是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,”任务队列”上第一位的事件就自动进入主线程。但是,由于存在后文提到的”定时器”功能,主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程。

Event Loop

主线程从”任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。
为了更好地理解Event Loop,请看下图(转引自Philip Roberts的演讲《Help, I’m stuck in an event-loop》)。

enter image description here

上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在”任务队列”中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取”任务队列”,依次执行那些事件所对应的回调函数。
执行栈中的代码(同步任务),总是在读取”任务队列”(异步任务)之前执行。请看下面这个例子。

var req = new XMLHttpRequest();
req.open('GET', url);    
req.onload = function (){};    
req.onerror = function (){};    
req.send();

上面代码中的req.send方法是Ajax操作向服务器发送数据,它是一个异步任务,意味着只有当前脚本的所有代码执行完,系统才会去读取”任务队列”。所以,它与下面的写法等价。

var req = new XMLHttpRequest();
req.open('GET', url);
req.send();
req.onload = function (){};    
req.onerror = function (){};   

也就是说,指定回调函数的部分(onload和onerror),在send()方法的前面或后面无关紧要,因为它们属于执行栈的一部分,系统总是执行完它们,才会去读取”任务队列”。

定时器

除了放置异步任务的事件,”任务队列”还可以放置定时事件,即指定某些代码在多少时间之后执行。这叫做”定时器”(timer)功能,也就是定时执行的代码。
定时器功能主要由setTimeout()和setInterval()这两个函数来完成,它们的内部运行机制完全一样,区别在于前者指定的代码是一次性执行,后者则为反复执行。以下主要讨论setTimeout()。
setTimeout()接受两个参数,第一个是回调函数,第二个是推迟执行的毫秒数。

console.log(1);
setTimeout(function(){console.log(2);},1000);
console.log(3);

上面代码的执行结果是1,3,2,因为setTimeout()将第二行推迟到1000毫秒之后执行。
如果将setTimeout()的第二个参数设为0,就表示当前代码执行完(执行栈清空)以后,立即执行(0毫秒间隔)指定的回调函数。

setTimeout(function(){console.log(1);}, 0);
console.log(2);

上面代码的执行结果总是2,1,因为只有在执行完第二行以后,系统才会去执行”任务队列”中的回调函数。
总之,setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在”任务队列”的尾部添加一个事件,因此要等到同步任务和”任务队列”现有的事件都处理完,才会得到执行。
HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常不会立即执行,而是每16毫秒执行一次。这时使用requestAnimationFrame()的效果要好于setTimeout()。
需要注意的是,setTimeout()只是将事件插入了”任务队列”,必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。

以上文章引用于:http://www.ruanyifeng.com/blog/2014/10/event-loop.html

Hexo介绍及常用安装使用

Veröffentlicht am 2016-11-27

hexo简介

hexo是使用nodejs开发的一个快速、简洁、高效、开源的静态博客生成器。

hexo使用Markdown解析文章,因此,文章需采用Markdown语法编写,在几秒内,即可利用靓丽的主题生成静态网页。类似于jekyll、Octopress、Wordpress,我们可以用hexo创建自己的博客,托管到github,绑定自己的域名。

选择hexo的理由

市场上也有不少其他类似hexo的博客生成器,为什么选择hexo呢?

  • 非常小巧,使用nodejs开发,下载安装时只需要几个简答的npm命令即可

  • 纯静态博客,不需要额外的数据库、php环境…仅仅需要一个nodejs环境即可

  • 由于是静态博客,所以,可以非常方便部署到github

  • 支持markdown语法、兼容windows linux mac,高可扩展性和自定义,而且有非常多的hexo主题

安装hexo

这里我们针对windows环境进行演示说明,其他环境大体上与此一致,详细请参照官方文档。

在安装hexo之前,你需要保证环境中有Node.js和Git环境,这里我们就不赘述他们俩的安装。

在Node.js和Git安装无误情况下,执行:

$ npm install -g hexo-cli

通过npm进行hexo的全局安装,这么简单,一条命令hexo即安装完成。

hexo使用

建站

安装hexo完成后,就可以进行使用了。

首先,对于不是从github更新的hexo库,我们需要自己初始化本地hexo库:

$ hexo init <folder>
$ cd <folder>
$ npm install

初始化完成后目录下有如下文件:

结构说明

_config.yml

网站的配置信息,可以在此配置大部分的参数。

package.json

应用程序的信息。EJS, Stylus 和 Markdown renderer 已默认安装,你可以自由移除。

package.json
{
  "name": "hexo-site",
  "version": "0.0.0",
  "private": true,
  "hexo": {
    "version": ""
  },
  "dependencies": {
    "hexo": "^3.0.0",
    "hexo-generator-archive": "^0.1.0",
    "hexo-generator-category": "^0.1.0",
    "hexo-generator-index": "^0.1.0",
    "hexo-generator-tag": "^0.1.0",
    "hexo-renderer-ejs": "^0.1.0",
    "hexo-renderer-stylus": "^0.2.0",
    "hexo-renderer-marked": "^0.2.4",
    "hexo-server": "^0.1.2"
  }
}
scaffolds

模版 文件夹。当你新建文章时,Hexo 会根据 scaffold 来建立文件。

source

资源文件夹是存放用户资源(blog)的地方。

public

Markdown 和 HTML 文件会被解析并放到 public 文件夹,而其他文件会被拷贝过去。

themes

主题文件夹。Hexo会根据主题来生成静态页面。

这样即建站成功。

写作

完成建站,接下来当然是开始写作了。

$ hexo new [layout] <title>

这条命令用来创建一篇新文章,用hexo new创建的文章格式是md,和普通markdown文档不一样,它包含了当前layout提供的头部模板,如post layout布局的文章,顶部会出现:

---
title: Hexo介绍及常用安装使用
date: 2016-11-27 21:59:22
tags: hexo
---

在_config.yml中会有default_layout这个参数,默认是post,也就是说不加layout参数时,new出来的文章使用post布局。当然,default_layout可以根据自己的需要进行更改。

layout

Hexo 有三种默认布局:post、page 和 draft,它们分别对应不同的路径,而你自定义的其他布局和 post 相同,都将储存到 source/_posts 文件夹。

布局 路径
post source/_posts
page source
draft source/_drafts

上面说过post布局的样子,对于page和draft,你可以尝试用它们 new 一篇新文章来看看效果。

文件名称

Hexo 默认以标题做为文件名称,但你可以编辑_config.yml中的 new_post_name 参数来改变默认的文件名称,举例来说,设为 :year-:month-:day-:title.md 可让您更方便的通过日期来管理文章。

变量 描述
:title 标题(小写,空格将会被替换为短杠)
:year 建立的年份,比如, 2016
:month 建立的月份(有前导零),比如, 04
:i_month 建立的月份(无前导零),比如, 4
:day 建立的日期(有前导零),比如, 07
:i_day 建立的日期(无前导零),比如, 7

文件创建成功,剩下的就是用markdown语法进行愉快的创作了!

生成及部署

生成

文档编写完成后,想要对外发布就得先进行文章的解析和资源的生成:

$ hexo generate

该命令生成静态网页文件。

本地启动

生成完可以在本地进行启动测试:

$ hexo server

启动后,可以通过http://localhost:4000/访问本地blog。

选项 描述
-p,–port 重设端口
-s,–static 只使用静态文件
-l,–log 启动日记记录,使用覆盖记录格式

部署

$ hexo deploy

该命令用来对generate后的资源进行部署,但是注意,使用该命令之前,需要安装hexo-deployer-git插件:

$ npm install hexo-deployer-git

安装完成,我们可以进行部署,这里我们还得明确一点,部署到哪?

在_config.yml中可以指定部署目的地:

deploy:
- type: git
  repo:
- type: heroku
  repo:

对于git方式来说,repo建议使用ssh协议而非https协议,还有,你需要提前在github上配置好当前机器的公钥。

这个指定的git repo是一个github pages,他是github为方便程序员们发布项目的说明文档而开放的服务,而 gh Pgaes 的美好之处在于,它有足以胜任成为一个独立博客的能耐。这里不详细介绍,你可以去官网学习。

在deploy之后,访问github pages地址你会发现文档已经全部发布。

如果嫌麻烦,可以执行:

$ hexo generate -d

or

$ hexo g -d

将编译和部署合并成一步操作

清理

$ hexo clean

清理掉以生成的静态网页相关文件

针对github上已有的blog仓库

实际工作中,往往是已经有一个blog仓库,我们需要更新它到本地并用hexo去管理。

由于hexo管理下的node_modules文件夹过大,一般我们不会将其放在github中,所以clone下来的文件夹中没有node_modules,需要执行如下命令进行node_modules的安装:

$ npm install

这种情况下不需要执行初始化hexo init了,之后的其他操作都还是一样的。

注:以上对于hexo的常用知识进行了介绍,如果你还需要了解更详细的内容,从这里到官网进行学习。

raml-for-jax-rs插件

Veröffentlicht am 2016-11-24

简介

raml-for-jax-rs插件是mulesoft开发的,是实现被JAX-RS注解的java代码与Raml Api描述之间相互转换的工具。
其中包含jaxrs-to-raml和raml-to-jaxrs两个主要工程,分别实现一种转换过程。

设计原则

RAML生成JAX-RS

所有发行版本(Eclipse Plugin,Maven Plugin和Jar)的工作方式如下:

  • 生成接口,并在RAML定义改变的情况下重新生成接口
  • 一个接口被生成一个顶级resource,该接口下面的子接口称为sub-resouce被作为不同的方法定义在同一个接口下
  • 为每个资源操作创建响应对象包装器,以便指导实现者仅生成与RAML定义兼容的结果
  • 为不属于JAX-RS核心规范的http方法生成自定义注解(PATCH)
  • 对象基于代表 请求/响应 实体的json schemas来生成
  • 英语是接口和方法中常用的语言

当前支持

  • JAX-RS 1.1 和 2.0
  • 除@Pattern外,JSR-303注解都支持,因为Raml用的是ECMA 262/Perl 5模式,而javax.validation用的是Java模式,@Min/@Max支持对raml中定义的非小数最大最小值的限制
  • 模型对象基于JSON schemas生成,带有Jackson1,2或者Gson注解
  • 基于XML Schemas生成带有JAXB注解的结果

JAX-RS注解

  • Path
  • Consumes, Produces.
  • QueryParam, FormParam, PathParam, HeaderParam.
  • DELETE, GET, HEAD, OPTIONS, POST, PUT.
  • DefaultValue.

扩展注解

验证注解:

  • NotNull
  • Min
  • DecimalMin
  • Max
  • DecimalMax

支持方式

该插件支持以下几种使用方式

  • Maven 插件
  • eclipse插件
  • 命令行

Maven插件

这里我们着重对Raml to JAX-RS方式中Maven插件的使用进行介绍

RAML to JAX-RS

这个Maven插件用来基于raml Api生成JAX-RS注释的java接口,支持基于单一或多个RAML文件

Maven 组件

Maven 组件仓库地址

  • https://repository-master.mulesoft.org/releases/ - release repository
  • https://repository-master.mulesoft.org/snapshots/ - snaphots repository
使用方式

需要在Project中的Pom文件中增加以下plug-in内容, 例如:

<plugin>
<groupId>org.raml.plugins</groupId>
<artifactId>raml-jaxrs-maven-plugin</artifactId>
<version>1.3.4</version>
<configuration>
    <!-- Use sourcePaths if you want to provide a single RAML file or a list of RAML files -->
    <sourceDirectory>${basedir}/raml</sourceDirectory>
    <!-- Optionally configure outputDirectory if you don't like the default value: ${project.build.directory}/generated-sources/raml-JAX-RS -->
    <!-- Replace with your package name -->
    <basePackageName>com.acme.api</basePackageName>
    <!-- Valid values: 1.1 2.0 -->
    <jaxrsVersion>2.0</jaxrsVersion>
    <useJsr303Annotations>false</useJsr303Annotations>
    <!-- Valid values: jackson1 jackson2 gson none -->
    <jsonMapper>jackson2</jsonMapper>
    <removeOldOutput>true</removeOldOutput>
    <!-- Optionally set extensions to a list of fully qualified names of classes
    that implement org.raml.jaxrs.codegen.core.ext.GeneratorExtension -->
    <!-- for example:
    <extensions>
        <param>com.abc.AuthorizationAnnotationExtension</param>
        <param>com.abc.ParameterFilterExtension</param>
    </extensions>
    Custom annotator for json schema to pojo convertor
    <customAnnotator>com.abc.MyCustomAnnotator</customAnnotator>
    -->
</configuration>
<executions>
    <execution>
        <goals>
            <goal>generate</goal>
        </goals>
        <phase>generate-sources</phase>
    </execution>
</executions>
</plugin>
配置说明
Schema Mean Optional
sourceDirectory raml文件目录 custom
sourcePaths raml文件路径集合 custom
outputDirectory 输出目录 custom
basePackageName 生成的基础包路径 custom
removeOldOutput 是否删除上一次的输出 boolean
jaxrsVersion JAX-RS规范版本 1.1 or 2.0
useJsr303Annotations 是否使用JSR303规范 boolean
jsonMapper json与java映射规范 jackson1 jackson2 gson none
extensions 扩展对Resource的生成控制 custom
customAnnotator json schema到pojo的定制化注解器 custom
jsonMapperConfiguration 对于json映射的配置 “generateBuilders”,”includeHashcodeAndEquals”,”includeToString”,”useLongIntegers”
skip 跳过插件的执行 boolean
modelPackageName 设置model对象所在的目录名称(basepackage+modelpackage,如果schema中已经指定了javapackage,则此配置不生效) custom
generateClientProxy 是否生成客户端代理(不生成warpper和用在返回的schema) boolean
mapToVoid true:如果resource定义的方法的response没有body,生成的java method返回值将会是void,不是wrapper response boolean
useTitlePropertyForSchemaNames 将title属性作为schema的名称(没试出来) boolean
asyncResourceTrait 【待定】
ignoredParameters 定义忽略参数列表,忽略指定的resource query参数 custom

运行mvn raml:generate时,RAML定义的内容就会被处理生成相应的Java代码,同样在运行mvn compile 或者 mvn package命令时这个插件也会执行。

RAML语法简单介绍

Veröffentlicht am 2016-11-24

人类可读的raml语法

开始设计时,首先创建一个包含以下内容的RAML文件:

#%RAML 0.8
title: This is My API
baseUri: http://api.domain.com
version: 1

在以上代码中,我们首先声明这是一个RAML规范,它对应RAML 0.8,并声明API的标题、基本URI、以及这个API的版本号。
在RAML中声明资源非常简单,只需使用/resourceName格式。而添加方法也同样便捷,只需引用相应的HTTP谓词即可:

#%RAML 0.8
title: This is My API
baseUri: http://api.domain.com
version: 1

/resource1:
  get:
    description: This gets the collection of resource1
  post:
    description: This adds a new item to the collection

RAML让你能够定义多种相应,返回不同的状态码、头信息以及响应体。例如:

#%RAML 0.8
title: This is My API
baseUri: http://api.domain.com
version: 1

/resource1:
  get:
    responses:
      200:
        headers:
          cache-control:
            example: |
              public, no-cache, no-store

        body:
          application/json:
            example: |
              {"name":"Michael Stowe"}
          application/xml:
            example: |
              <name>Michael Stowe</name>
      400:
        #...
      401:
        #...

RAML本身还有大量的其它特性,让你通过schema与参数定义完整的API。它还允许你使用资源嵌套、文件引用(可以引用多个文件,以保持规范的易读性和易组织性),甚至是变量或属性的设置,从而在整个规范中保持一致性。

举例来说,你可以在规范中按以下方式利用这些特性:

#%RAML 0.8
title: This is My API
baseUri: http://api.domain.com
version: 1

/resource1:
  get:
    responses:
      200:
        body:
          application/json:
            schema: |
              {
                  "type": "object",
                  "$schema": "http://json-schema.org/draft-03/schema",
                  "id": "http://jsonschema.net",
                  "required": true,
                  "properties": {
                    "firstName": {
                      "type": "string",
                      "required": true
                    },
                    "lastName": {
                      "type": "string",
                      "required": true,
                      "minLength": 3,
                      "maxLength": 36
                    }
                  }
              }

  /sub-resource:
    get:
      queryParameters:
        firstName:
          description: "the user’s first name"
          example: John
          required: true
          type: string

对开发者十分友好

RAML的优点不仅在于简单的格式与丰富的工具,它还能够让开发者应用编码的最佳实践,例如模式与重用代码。这不仅能够极大地减少开发者的工作,还能够促使API在不同的资源与方法中保持统一性。虽然你总是能够抽象出一套schema,以保持规范的良好组织,或是通过“!include”命令引入schema、示例与其它RAML代码片段,但RAML还提供了两个额外的实用与独特的模板特性:即traits与resourceTypes

通过traits定义通用属性

RAML的traits特性允许你为方法(GET、PUT、POST、PATCH、DELETE等等)定义通用的属性(或traits),例如它们是否可过滤、可搜索或是可分页。

在创建trait时,你实际上是创建了一份模板,它能够通过接受参数为方法提供属性,只需几行代码就能够完成。同时为你所需的trait提供了最大程度的灵活性与自定义能力:

traits:
-searchable:
  queryParameters:
  query:
  description: |
    JSON array [{"field1","value1","operator1"},…] <<description>
  example: |
    <<example>>

/people:
  get:
    is: [searchable: {description: "search by name", example: "[\"firstName\"\,\"Michael\",\"like\"]"}]  

通过ResourceTypes为资源定义模板

此外,与traits相似,resourceTypes也允许你为资源本身创建模板,以此调用通用的方法与响应。打个比方,对于Collection这种资源类型来说,你可能会大量用到POST与GET方法,并且通常会返回200、201或400等状态码。只要将它定义为一种resourceType,你就能够通过两行代码就完成调用,而无需为你所创建的每种资源都加入相同的方法与状态码。

resourceTypes:
- collection:
    description: Collection of available <<resourcePathName>>
    get:
      description: Get a list of <<resourcePathName>>.
      responses:
        200:
          body:
            application/json:
              example: |
                <<responseVariable>>
        400:
          #...
        401:
          #...

/people:
  type:
    collection:
      responseVariable: |
        {
         "name" : "Michael Stowe",
         "company" : "MuleSoft",
        }

你甚至可以在resourceTypes中定义可选的方法,只需为该方法加上一个问号(?)即可。这种可选方法只有在定义了该方法的资源中才会被引入,这就为你赋予了更大的灵活性。

注:这里只介绍了一些基本用法,详细用法请参考官方语法介绍

RAML介绍

Veröffentlicht am 2016-11-24

RAML是什么

Write once. Use many. Creative laziness encouraged
RESTful API Modeling Language (RAML) makes it easy to manage the whole API lifecycle from design to sharing. It’s concise - you only write what you need to define - and reusable. It is machine readable API design that is actually human friendly.

RAML的全称是RESTful API Modeling Language,这是一种用来描述基于Restful架构的API(设计API)的语言。它的语法规范是基于YAML的新规范,因此机器与人类都能够轻易地理解其中的内容。

但是,仅仅简单地创建一个原型是不够的。MuleSoft创建了一种API契约设计周期图,这一设计的前提在于API不仅仅是机器之间的一种契约,同样也是提供商与用户之间的一种契约。

在API设计后,可以对外提供模拟的API调用,这样就能从API使用者那得到具体反馈。这些有价值的反馈能够让公司找到设计中的缺陷,例如数据或结构中的不一致性,以及API中的一些令人困惑之处。在这一阶段及时发现设计中的问题非常关键,因为一旦你的API发布之后需要修改,那么在大多数情况下都会破坏向后兼容性,而这将影响API的使用。

这里就不对RAML的具体语法做说明了,在我的另一篇文章《raml语法简单介绍》中对语法有相关介绍。

为什么用RAML

RAML的目的不仅仅在于创建更易于理解的规范(你可以将这一工作指派给文档团队,他们会做得更好)而已。RAML的设计者Uri Sarid希望使用者能够打破固有的思维,在开始编写代码之前以一种全新的方式对API进行建模。
Roy Fielding博士在他的博客Untangled中指出,现在的人们总有一种倾向,他们只考虑短期的设计,目光只能看到我们所知的、我们目前所想的内容。

REST风格的发明者Roy Fielding表示:“很不幸,人们很善于处理短期的设计,但对于长期的设计通常就束手无策了。”

设计API的一大挑战在于,API通常都会存在较长的一段时间,有可能会存在数年之久。毕竟,API的设计需要开发者投入大量的精力,但同样对于客户来说也是一种极大的投入,因为客户也依赖于这些API,需要基于它们进行开发。从2009年起,Uri开始思考如何解决API的长期设计的挑战,他希望能够找到一种工具,让设计者只需几行代码就能够对API进行建模(或设计),然后快速地生成一个原型,让全球的开发者都能够尝试你的原型并提供反馈。在2013年,随着RAML 0.8的发布,他的梦想终于变成了现实。

自那时起,人们对于RAML的兴趣就在不断地升温。无论是大型企业还是小公司,他们都开始意识到了RAML所带来的益处:包括以一种人类可读的格式设计API、在设计时就看到这些API将怎样工作、并且能够为API创建一个活动的、全功能的示例,让开发者能够简单地通过点击按键,对这个API发起实际的调用。

周边生态

  • DESIGN
    对于基于RAML的API设计,通常使用API Workbench或API Designer作为设计平台。它们都是由mulesoft开发的,前者作为Atom的插件使用,后者是单独的一个设计平台。

  • BUILD
    设计好的API可以由多种方式进行build: NodeJS,JAVA,NET,Python,Mule,IOT
    这里我只简单介我们常用的两种。对于JAVA来说,通常我们使用RAML for JAX-RS进行build,简单来说就是把设计好的RAML转换成JAX-RS规范的java代码,用于后续的deploy。还有一种是mule的APIkit,它基于anypoint studio进行raml翻译及build。

  • TEST
    我们可以用多数的第三方进行测试,如API Fortress, API Science, Parasoft, Postman, SmartBear。

  • Document
    形成API文档,我们不再需要花大量的时间。运用API Console,RAML to HTML我们即可以根据RAML自动化动态的生成文档。

  • SHARE
    我们可以简单的根据RESTful API为Java,.NET,PHP,Ruby等主流语言生成SDK,也可以使用API Notebook体验全新的API交互方式。API Notebook完全改变了开发人员与API交互的方式,让他们可以使用基本的JavaScript轻松地尝试调用,操作数据,甚至连接到多个API。

Raml提供了全新的RESTful API设计方式,围绕其周边衍生出若干方便快捷的解决方案。无论是从思维、交互性还是效率上来说,它都是api设计上的一次成功的进步

Jsonschema2pojo中json schema规范写法

Veröffentlicht am 2016-09-27

简介

日常开发中,json数据格式经常会被用到,其简单易懂的写法(对人与计算机皆如此)以及其轻量级特性非常适用于网络间的数据传递。json数据格式与java对象会经常进行相互转换,本文探讨的是json to java的转换。
Jsonschema2pojo即是一种json转换java的工具

主流JSON库

在解决json2java的问题时,一般使用四种主流的JSON库来进行json解析:

  • JSON.simple
  • GSON
  • Jackson
  • JSONP

Jsonschema2pojo

Jsonschema2pojo支持jackson1,jackson2,gson三种规范,基于这些规范对json数据进行解析,相应的,json数据也就需要有它本身一定的写法规范,Jsonschema2pojo定义了json schema的规范和书写方式

Json Schema feature support

这里,我就常用的以及一些扩展的书写规范向大家一一说明,也提供了一些例子供参考。

下表为json schema书写过程中固定的一些rule,请先简单阅读:

JSON Schema rule Supported Since Note
type (Simple) Yes 0.1.0
type (Union) No
properties Yes 0.1.0
patternProperties No
additionalProperties Yes 0.1.3
items Yes 0.1.0
additionalItems No
required Yes 0.1.6
optional Yes 0.1.0 Deprecated
dependencies No
minimum, maximum Yes 0.3.2 Via optional JSR-303 annotations
exclusiveMinimum, exclusiveMaximum No
minItems, maxItems Yes 0.3.2 Via optional JSR-303 annotations
uniqueItems Yes 0.1.0
pattern Yes 0.3.2 Via optional JSR-303 annotations
minLength, maxLength Yes 0.3.4 Via optional JSR-303 annotations
enum Yes 0.1.0
default Yes 0.1.7
title Yes 0.1.6
description Yes 0.1.0
format Yes 0.1.0
divisibleBy No
disallow No
extends Yes 0.1.8
id No
$ref Yes 0.1.6 Supports absolute, relative, slash & dot delimited fragment paths, self-ref
$schema No

properties

schema

1
2
3
4
5
6
7
8
{
"type" : "object",
"properties" : {
"foo" : {
"type" : "string"
}
}
}

java

1
2
3
4
5
6
7
8
9
public class MyObject {
private String foo;
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
}

properties用于指定schema的参数,转换为pojo后,properties中的属性foo会转换为java对象的属性foo,”type”用来指定对象或属性的类型

type

指定所定义的有类型概念的数据类型,下表为json schema中type声明类型与pojo中声明类型间的对应关系:

Schema Type Java Type
string java.lang.String
number java.lang.Double
integer java.lang.Integer
boolean java.lang.Boolean
object generated Java type
array java.util.List
array (with “uniqueItems”:true) java.util.Set
null java.lang.Object
any java.lang.Object

additionalProperties

schema1

1
2
3
4
{
"type" : "object",
"additionalProperties" : {}
}

java1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyObject {
private java.util.Map<String, Object> additionalProperties = new java.util.HashMap<String, Object>();
@org.codehaus.jackson.annotate.JsonAnyGetter
public java.util.Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@org.codehaus.jackson.annotate.JsonAnySetter
public void setAdditionalProperties(String name, Object value) {
this.additionalProperties.put(name, value);
}
}

additionalProperties为true,会生成Map作为传入参数中附加参数的接受器,false则不生成,写成“{}”等同于true。 你也可以通过指定additionalProperties的数据类型来约束生成的结果。

schema2

1
2
3
4
5
6
{
"type" : "object",
"additionalProperties" : {
"type" : "number"
}
}

java2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyObject {
private java.util.Map<String, Double> additionalProperties = new java.util.HashMap<String, Double>();
@org.codehaus.jackson.annotate.JsonAnyGetter
public java.util.Map<String, Double> getAdditionalProperties() {
return this.additionalProperties;
}
@org.codehaus.jackson.annotate.JsonAnySetter
public void setAdditionalProperties(String name, Double value) {
this.additionalProperties.put(name, value);
}
}

上面将additionalProperties的类型改为Double,pojo中属性的泛型就会变为<String, Double> 如果将type指定为object(我的json文件名为source.json)

schema3

1
2
3
4
5
6
{
"type" : "object",
"additionalProperties" : {
"type" : "object"
}
}

java3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Source{
private java.util.Map<String, SourceProperty> additionalProperties = new java.util.HashMap<String, SourceProperty>();
@org.codehaus.jackson.annotate.JsonAnyGetter
public java.util.Map<String, SourceProperty> getAdditionalProperties() {
return this.additionalProperties;
}
@org.codehaus.jackson.annotate.JsonAnySetter
public void setAdditionalProperties(String name, SourceProperty value) {
this.additionalProperties.put(name, value);
}
}

如上,会生成一个以主文件名为前缀,Property为后缀的类(SourceProperty)作为additionalProperties的value类型,该类中则有:

java4

1
private Map<String, Object> additionalProperties = new HashMap<String, Object>();

array

schema

1
2
3
4
5
6
7
8
9
10
11
{
"type" : "object",
"properties" : {
"myArrayProperty" : {
"type" : "array",
"items" : {
"type" : "string"
}
}
}
}

array写法如上即会生成Array<string>类型的参数myArrayProperty,如果在myArrayProperty指定“uniqueItems”:true,生成的集合则为Set<>. 对于items,可以$ref(后面介绍)引用其他jsonschema文件,也可以直接在其下编写jsonschema,会生成一个以集合参数为名称(MyArrayProperty)的对象作为该集合的泛型

enum

schema

1
2
3
4
5
6
7
8
9
{
"type" : "object",
"properties" : {
"myEnum" : {
"type" : "string",
"enum" : ["one", "secondOne", "3rd one"]
}
}
}

java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Generated("com.googlecode.jsonschema2pojo")
public static enum MyEnum {
ONE("one"),
SECOND_ONE("secondOne"),
_3_RD_ONE("3rd one");
private final String value;
private MyEnum(String value) {
this.value = value;
}
@JsonValue
@Override
public String toString() {
return this.value;
}
@JsonCreator
public static MyObject.MyEnum fromValue(String value) {
for (MyObject.MyEnum c: MyObject.MyEnum.values()) {
if (c.value.equals(value)) {
return c;
}
}
throw new IllegalArgumentException(value);
}
}

enum作为枚举写法经常被使用,比较简单,不详细介绍

format

可以给属性指定format规则,它会影响你的参数类型,和type对比,format的优先级更高,format的规则列表如下:

Format value Java type
“date-time” java.util.Date
“date” String
“time” String
“utc-millisec” long
“regex” java.util.regex.Pattern
“color” String
“style” String
“phone” String
“uri” java.net.URI
“email” String
“ip-address” String
“ipv6” String
“host-name” String
“uuid” java.util.UUID
anything else (unrecognised format) type is unchanged

extends

extends属性声明在schema层表明继承

flower.json:

1
2
3
{
"type" : "object"
}

rose.json

1
2
3
4
5
6
{
"type" : "object",
"extends" : {
"$ref" : "flower.json"
}
}

Rose.java:

1
2
3
public class Rose extends Flower {
....
}

$ref

Supported protocols

  • http://, https://
  • file://
  • classpath:, resource:, java: (all synonyms used to resolve schemas from the classpath)

$ref同样支持内部类型的引用

1
2
3
4
5
6
7
8
9
10
11
{
"type" : "object",
"properties" : {
"child1" : {
"type" : "string"
},
"child2" : {
"$ref" : "#/properties/child1"
}
}
}

上面这种child2引用了child1的属性

1
2
3
4
5
6
7
8
9
10
11
12
{
"description" : "Tree node",
"type" : "object",
"properties" : {
"children" : {
"type" : "array",
"items" : {
"$ref" : "#"
}
}
}
}

这种写法将shema类型作为集合类型泛型的引用,类似于tree结构 也可以直接定位到集合的items属性作为类型(#/properties/children/items)

javaType

1
2
3
4
{
"javaType" : "com.other.package.CustomTypeName",
"type" : "object"
}

javaType允许指定属性类型或类名称为java类(已存在或不存在的),如此生成会创建一个CustomTypeName类,并将属性类型指定为该类 如果不加”type” : “object”,则不会生成对应类,但是属性类型依然为该类名,报错而已 当然,你也可以直接指定已有的封装类

javaEnumNames

schema

1
2
3
4
5
6
7
8
9
10
{
"type" : "object",
"properties" : {
"foo" : {
"type" : "string",
"enum" : ["H","L"],
"javaEnumNames" : ["HIGH","LOW"]
}
}
}

java

1
2
3
4
5
public enum Foo {
HIGH("H"),
LOW("L")
...
}

javaEnumNames是对于enum的扩展,可以指定java中的enum列表具体名称

javaInterfaces

schema

1
2
3
4
{
"javaInterfaces" : ["java.io.Serializable", "Cloneable"],
"type" : "object"
}

java

1
2
3
4
public class FooBar implements Serializable, Cloneable
{
...
}

这个好理解,声明接口

javaName

该属性用于指定类或属性的生成名称

schema

1
2
3
4
5
6
7
8
9
{
"type": "object",
"properties": {
"a": {
"javaName": "b",
"type": "string"
}
}
}

java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyClass {
@JsonProperty("a")
private String b;
@JsonProperty("a")
public String getB() {
return b;
}
@JsonProperty("a")
public void setB(String b) {
this.b = b;
}
}

如果使用在主schema级别,可以直接指定对应的类名

JAX-RS规范的版本.实现.jar对应关系

Veröffentlicht am 2016-09-27   |   in JAX-RS

简介


JAX-RS: Java API for RESTful Web Services是一个Java编程语言的应用程序接口,支持按照 表象化状态转变 (REST)架构风格创建Web服务. JAX-RS使用了Java SE 5引入的Java 标注来简化Web服务客户端和服务端的开发和部署。

版本说明


名词解释

JCP:Java Community Process

一个开放的国际组织,主要由Java开发者以及被授权者组成,职能是发展和更新。
Java技术和JCP两者的原创者都是SUN计算机公司。然而,JCP已经由SUN于1995年创造Java的非正式过程,演进到如今有数百名来自世界各地Java代表成员一同监督Java发展的正式程序。
JCP维护的规范包括J2ME、J2SE、J2EE,XML,OSS,JAIN等。组织成员可以提交JSR(Java Specification Requests),通过特定程序以后,进入到下一版本的规范里面。

JSR:Java Specification Requests

Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。
即为一个规范代号:JSR311,JSR303等
完整JSR列表请查看JCP官网

版本

JSR311: 即代号为311的规范请求,对应JAX-RS规范,版本为 1.0
JSR399: 即代号为399的规范请求,对应JAX-RS规范,版本为 2.0

JAX-RS对应实现


何为实现

对于规范的实现,可以理解为面向对象语言中实现接口的概念。接口定义的即是一种规范、一种约束,由具体的模块来去完整的诠释这种规范,此为实现。

下面是不同组织用来诠释JAX-RS规范的不同产品:

  • Apache CXF,开源的Web服务框架。
  • Jersey, 由Sun提供的JAX-RS的参考实现。
  • RESTEasy,JBoss的实现。
  • Restlet,由Jerome Louvel和Dave Pawson开发,是最早的REST框架,先于JAX-RS出现。
  • Apache Wink,一个Apache软件基金会孵化器中的项目,其服务模块实现JAX-RS规范

这里讨论Jersey和RESTEasy:
Jersey1.x:JAX-RS 1.0
Jersey2.x:JAX-RS 2.0

RESTEasy1.x/2.x:JAX-RS 1.0
RESTEasy3.x:JAX-RS 2.0

Jar包区别


在JAX-RS规范的具体应用中,经常会遇见jsr311-api.jar,jsr311-api.jar,jaxrs-api.jar,jersey-core这几种包,下面对这些包的区别做了描述:

  • jsr311-api:JAX-RS 1.0 的官方规范包
  • javax.ws.rs-api:JAX-RS 2.0 的官方规范包

(以上为官方标准规范包,其中包含对应的注解和一些辅助类)

  • jaxrs-api:RESTEasy规范包,其中包含对应版本JAX-RS规范的内容以及自定义的异常和接口
  • jersey-core:Jersey规范包,包含对应版本JAX-RS规范的内容以及部分实现,余下实现在jersey的其他包中
12
lashou

lashou

11 Artikel
1 Kategorien
6 Tags
© 2017 lashou
Erstellt mit Hexo
Theme - NexT.Muse