热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JAX-RS:开发一个简单的服务

JAX-RS:开发一个简单的服务2011-09-2116:48:50我来说两句收藏我要投稿JAX-RS使用注解进行配置,所以用它开发REST风格的服务
JAX-RS:开发一个简单的服务
2011-09-21 16:48:50      我来说两句      
收藏     我要投稿

JAX-RS 使用注解进行配置,所以用它开发REST 风格的服务非常简单。楼主在本文用一个小例子来说明JAX-RS 的基本用法。

 

假设楼主要开发一个小电影服务,客户端可以通过请求URI 对电影进行CRUD 操作。为简明起见,这儿不使用数据库,只在内存中模拟。先用一个非常简单的Movie类,在后续的文章中根据情况逐步扩充:

 

 

1

2

3

4

5

publicclassMovie {

    privateintid;

    privateString title;

    // 此处省略若干行

}

嗯,就是一个很普通的JavaBean,实际项目中可以根据需要加上@Entity等注解。接下来看看如何编写JAX-RS 服务。

 

一个JAX-RS 服务就是一个使用了JAX-RS 注解来将HTTP 请求绑定到方法的Java 类,一共支持两种类型:单请求对象或单例对象。单请求对象意味着每来一个请求,就创建一个服务对象,在请求结束时销毁。单例对象则意味着只有一个服务对象处理所有的请求,从而可以在多个请求间维持服务状态。JAX-RS 服务可通过继承javax.ws.rs.core.Application来定义,其中的getClasses方法返回单请求对象的类型,getSingletons方法返回单例对象的类型。这两个方法是可选的。在Java EE 6 环境中,如果这两个方法都返回null或者空集合,那么应用程序中的所有JAX-RS 都将被部署。这时可以用CDI 的@javax.inject.Singleton或者EJB 的@javax.ejb.Singleton注解来指定单例对象。

如果电影服务的上下文根路径为http://localhost/ms,而楼主希望将服务部署到http://localhost/ms/rest 下面,只需要写一个类:

 

 

1

2

3

@ApplicationPath("rest")

publicclassRestApplication extendsApplication {

}

@ApplicationPath注解指定所有服务的相对基址,如果为空字符串,则直接使用上下文根路径。另一种配置方式是在web.xml 文件中进行声明,那是为了使JAX-RS 能在Servlet 容器(例如Tomcat)中运行,此处略过。这项配置必不可少,否则无法部署服务。

 

很好很强大,现在开始编写电影服务类MovieService,先看看声明和初始化:

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

@Singleton

@Path("movie")

publicclassMovieService {

    privateAtomicInteger ai;

    privateConcurrentMap movieMap;

 

    @PostConstruct

    privatevoidinit() {

        ai = newAtomicInteger();

        movieMap = newConcurrentHashMap<>();

        intid = ai.getAndIncrement();

        movieMap.put(id, newMovie().setId(id).setTitle("Avatar"));

    }

因为楼主只需要一个“内存数据库”,所以用单例对象即可,此处使用CDI 的@javax.inject.Singleton来声明单例。@Path声明了一个服务,它指示MovieService负责处理发送到http://localhost/ms/rest/movie 的请求。路径的拼接方式非常直观。init方法带有@PostConstruct注解,因此将在MovieService构造完成后立即调用,它向movieMap中存入了一个ID 为0 的Movie对象。为简化代码,Movie的设置方法都返回this,有点伪造构建者模式的味道。

 

接下来看看如何处理HTTP 请求。

GET

GET 请求用于获取一个资源。在本例中用来获取一部电影的信息:

 

 

1

2

3

4

5

6

7

8

9

10

11

@GET

@Path("{id}")

@Produces(MediaType.APPLICATION_JSON)

publicMovie find(@PathParam("id") intid) {

    Movie movie = movieMap.get(id);

    if(movie != null) {

        returnmovie;

    } else{

        thrownewWebApplicationException(Response.Status.NOT_FOUND);

    }

}

该方法标注了@GET,表示用来处理向http://localhost/ms/rest/movie/{id} 发送的GET 请求。@Path再次用来绑定路径,注意其参数{id},它带有花括号,对应URI 的最后一段,也正好和方法参数id的@PathParam的值相对应。这种参数还有很多高级用法,以后再介绍。@Produces注解指定输出格式为JSON。JAX-RS 内置了很多格式,详见MediaType的文档。如果找到了相应ID 的对象,则直接返回,JAX-RS 会自动加上响应码200 OK;否则抛出异常,错误码为404 Not Found。

例如,通过浏览器访问http://localhost/ms/rest/movie/0,得到的结果为{"@id":"0","@title":"Avatar"}。

POST

POST 请求用于创建一个资源。在本例中用来创建一部电影:

 

 

1

2

3

4

5

6

7

@POST

@Consumes(MediaType.APPLICATION_JSON)

publicResponse create(Movie movie) {

    intid = ai.getAndIncrement();

    movieMap.put(id, movie.setId(id));

    returnResponse.created(URI.create(String.valueOf(id))).build();

}

由于没有@Path注解,所以POST 请求的目标就直接是http://localhost/ms/rest/movie。Consumes和@Produces相反,表示接受的数据类型,此处JAX-RS 会自动把JSON 数据转换为Movie对象。返回的响应码为201 Created,并且带有所创建资源的URI。

例如,向http://localhost/ms/rest/movie 发送POST 请求,正文为{"@title": "007"},则可以从FireBug 的网络监控中看到返回的响应码,以及头部中Location 的值为http://localhost:8080/rest/service/movie/1。多次发送该POST 请求,将会创建多个资源,以保证POST 不是幂等的。

PUT

PUT 请求用于创建或更新一个资源。与POST 不同,PUT 请求要指定某个特定资源的地址。在本例中用来更新一部电影的信息:

 

 

1

2

3

4

5

6

7

8

9

10

11

@PUT

@Path("{id}")

@Consumes(MediaType.APPLICATION_JSON)

publicResponse update(@PathParam("id") intid, Movie movie) {

    movie.setId(id);

    if(movieMap.replace(id, movie) != null) {

        returnResponse.ok().build();

    } else{

        thrownewWebApplicationException(Response.Status.NOT_FOUND);

    }

}

更新成功就返回200 OK,否则返回404 Not Found。这儿先把movie对象的ID 强制改为URI 所指定的,以免出现不一致。也可以根据需求,将不一致作为异常处理,给客户端返回一个错误码。

顺便啰嗦一句,反正代码在自己手中,楼主也可以把PUT 搞成非幂等的,例如将PUT 当成POST 来处理,就像以前把GET 和POST 一视同仁那样。不过咱既然在搞JAX-RS,就还是要沾染一点REST 风格,严格遵守HTTP 才是。

DELETE

DELETE 请求用于删除一个资源。在本例中用来删除一部电影:

 

 

1

2

3

4

5

6

7

8

9

@DELETE

@Path("{id}")

publicResponse delete(@PathParam("id") intid) {

    if(movieMap.remove(id) != null) {

        returnResponse.ok().build();

    } else{

        thrownewWebApplicationException(Response.Status.NOT_FOUND);

    }

}

 

没什么特别的,该说的前面都说了。

HEAD 和OPTIONS 请求就忽略吧,用得不太多,也同样挺简单的。

 

JAX-RS 服务的部署和部署常规Web 程序一样,打包成war 文件就可以了。最后赞一下NetBeans 可以为REST 风格的服务自动生成测试页面,很好用,虽然在Firefox 下页面显示不正常,但IE 是可以的。


推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
author-avatar
PFwX代佳佳ZYYTHFQN
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有