JAX-RS入门.doc_第1页
JAX-RS入门.doc_第2页
JAX-RS入门.doc_第3页
JAX-RS入门.doc_第4页
JAX-RS入门.doc_第5页
已阅读5页,还剩61页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

JAX-RS入门第一节、基础简介JAX-RS是一套用java实现REST服务的规范,提供了一些标注将一个资源类,一个POJOJava类,封装为Web资源。标注包括:Path,标注资源类或方法的相对路径GET,PUT,POST,DELETE,标注方法是用的HTTP请求的类型Produces,标注返回的MIME媒体类型Consumes,标注可接受请求的MIME媒体类型PathParam,QueryParam,HeaderParam,CookieParam,MatrixParam,FormParam,分别标注方法的参数来自于HTTP请求的不同位置,例如PathParam来自于URL的路径,QueryParam来自于URL的查询参数,HeaderParam来自于HTTP请求的头信息,CookieParam来自于HTTP请求的Cookie。目前JAX-RS的实现包括:Apache CXF,开源的Web服务框架。Jersey, 由Sun提供的JAX-RS的参考实现。RESTEasy,JBoss的实现。Restlet,由Jerome Louvel和Dave Pawson开发,是最早的REST框架,先于JAX-RS出现。Apache Wink,一个Apache软件基金会孵化器中的项目,其服务模块实现JAX-RS规范(以上来自:/wiki/JAX-RS) 装备本文使用的工具有:Eclipse-jee-heliosJava-1.6.0_26apache-tomcat-6.0.30SoapUI-3.6使用到的外部jar包有(必须的部分,需要加到Web容器中)neethi-3.0.2.jarjsr311-api-1.1.1.jarcxf-bundle-2.6.0.jar使用到的外部jar包有(可选的部分,当且仅当作为一个独立的application运行时)jetty-http-7.5.4.v20111024.jarjetty-io-7.5.4.v20111024.jarjetty-server-7.5.4.v20111024.jarjetty-util-7.5.4.v20111024.jarjetty-continuation-7.5.4.v20111024.jarwsdl4j-1.6.2.jar 准备 (以下例子来自: Oreilly - RESTful Java with JAX-RS (12-2009) (ATTiCA).pdf) 创建工程为了后续顺利进行,首先在eclipse上先创建一个Dynamic Web Project,完成以后,一个符合war结构的工程目录会自动生成,之后可以很简单的导出为war文件,其中需要把以下jar包放到 /WebContent/WEB-INF/lib 里:neethi-3.0.2.jarjsr311-api-1.1.1.jarcxf-bundle-2.6.0.jar 另外,在工程目录下,新建一个 lib 文件夹用来存放以下可选的jar包: jetty-http-7.5.4.v20111024.jarjetty-io-7.5.4.v20111024.jarjetty-server-7.5.4.v20111024.jarjetty-util-7.5.4.v20111024.jarjetty-continuation-7.5.4.v20111024.jarwsdl4j-1.6.2.jar最后一步就是把所有这9个jar都加到工程的build path里去,这样工程就准备好了。 定义服务这里要实现一个简单的REST服务用于对客户进行管理,包括:创建客户查看客户更新客户首先给出对应的于这些操作的服务接口:Java代码 import java.io.InputStream; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; Path(/customers) public interface CustomerResource POST Consumes(application/xml) public Response createCustomer(InputStream is); GET Path(id) Produces(application/xml) public StreamingOutput getCustomer(PathParam(id) int id); PUT Path(id) Consumes(application/xml) public void updateCustomer(PathParam(id) int id, InputStream is) ; 令人惊奇的是,这个接口已经包含了所有实现我们既定目标的关键部分:Path: 定义服务路径,接口中定义的整个服务的顶级路径为/customers ,方法对应的服务路径为接口路径加方法定义的Path值,如果未定义,则用接口路径,例如getCustomer()的服务路径为: /customers/id 。所以此REST对外服务路径都是 服务的上下文路径/customers/ 子级目录,POST,GET,PUT:标注方法所支持HTTP请求的类型 (参考上面的说明)Produces,Consumes:标注方法支持或返回的请求MIME类型。由上可以看到,每个方法被调用的条件如下:createConsumer(): 请求HTTP方法为POST;请求MIME类型为application/xml;请求路径为: 上下文路径/customers getCustomer(): 请求的HTTP方法为GET;请求的MIME类型为application/xml;请求的路径为: 上下文路径/customers/id 注: id为某个存在(或不存在)customer的编号updateCustomer(): 请求的HTTP方法为PUT;请求的MIME类型为application/xml;请求的路径: 上下文路径/customers/id注: id为某个存在(或不存在)customer的编号一个好的实现方法是将REST服务的定义和实现分开,这样代码的结构简洁、清晰,在后期也可以很方便的进行实现的替换和服务定义的修改。 下面就是添加实现部分:Java代码 public class CustomerResourceService implements CustomerResource private Map customerDB = new ConcurrentHashMap(); private AtomicInteger idCounter = new AtomicInteger(); public Response createCustomer(InputStream is) Customer customer = readCustomer(is); customer.setId(idCounter.incrementAndGet(); customerDB.put(customer.getId(), customer); System.out.println(Created customer + customer.getId(); return Response.created(URI.create(/customers/ + customer.getId() .build(); public StreamingOutput getCustomer(int id) final Customer customer = customerDB.get(id); if (customer = null) throw new WebApplicationException(Response.Status.NOT_FOUND); return new StreamingOutput() public void write(OutputStream outputStream) throws IOException, WebApplicationException outputCustomer(outputStream, customer); ; public void updateCustomer(int id, InputStream is) Customer update = readCustomer(is); Customer current = customerDB.get(id); if (current = null) throw new WebApplicationException(Response.Status.NOT_FOUND); current.setFirstName(update.getFirstName(); current.setLastName(update.getLastName(); current.setStreet(update.getStreet(); current.setState(update.getState(); current.setZip(update.getZip(); current.setCountry(update.getCountry(); protected void outputCustomer(OutputStream os, Customer cust) throws IOException PrintStream writer = new PrintStream(os); writer.println(); writer.println( + cust.getFirstName() + ); writer.println( + cust.getLastName() + ); writer.println( + cust.getStreet() + ); writer.println( + cust.getCity() + ); writer.println( + cust.getState() + ); writer.println( + cust.getZip() + ); writer.println( + cust.getCountry() + ); writer.println(); protected Customer readCustomer(InputStream is) try DocumentBuilder builder = DocumentBuilderFactory.newInstance() .newDocumentBuilder(); Document doc = builder.parse(is); Element root = doc.getDocumentElement(); Customer cust = new Customer(); if (root.getAttribute(id) != null & !root.getAttribute(id).trim().equals() cust.setId(Integer.valueOf(root.getAttribute(id); NodeList nodes = root.getChildNodes(); for (int i = 0; i nodes.getLength(); i+) Node item = nodes.item(i); if(!(item instanceof Element) continue; Element element = (Element) nodes.item(i); if (element.getTagName().equals(first-name) cust.setFirstName(element.getTextContent(); else if (element.getTagName().equals(last-name) cust.setLastName(element.getTextContent(); else if (element.getTagName().equals(street) cust.setStreet(element.getTextContent(); else if (element.getTagName().equals(city) cust.setCity(element.getTextContent(); else if (element.getTagName().equals(state) cust.setState(element.getTextContent(); else if (element.getTagName().equals(zip) cust.setZip(element.getTextContent(); else if (element.getTagName().equals(country) cust.setCountry(element.getTextContent(); return cust; catch (Exception e) throw new WebApplicationException(e, Response.Status.BAD_REQUEST); 这些方法的实现都很直接,不细说,不过有一点需要特别注意的是: 最好不要在实现中混杂有服务的定义部分,例如Path标签,PathParam标签等等,如果想修改定义,最好是在接口中修改;或者如果想覆盖某个接口方法的某个annotation,则所有该接口方法的annotation定义都需要重写,而不能仅修改变化的。第二节、运行上一节,已经成功的定义了一个REST服务,并且提供了具体的实现,不过我们还需要把它运行起来。 在上一节的装备部分,列举了必须的jar(在tomcat中运行)和可选的jar(作为一个独立的应用程序运行)。这一节将分别介绍如何做为一个独立的应用程序运行和如何在tomcat里运行。 Tomcat(或者其他Web容器)中运行要在tomcat之类的容器里运行,首先需要定义一个Application类:Java代码 import java.util.HashSet; import java.util.Set; import javax.ws.rs.core.Application; public class CustomerApplication extends Application private Set singletons = new HashSet(); private SetClass empty = new HashSetClass(); public CustomerApplication() singletons.add(new CustomerResourceService(); Override public SetClass getClasses() return empty; Override public Set getSingletons() return singletons; 简单说明:getClasses():返回所有定义的服务类的类;每次请求都生成新的服务对象getSingletons():返回所有定义的服务类的实例;每次请求都会重用已经存在对象因为我们的数据需要被重用,因为getClasses()返回空;getSingletons()返回创建的一个对象实现。 有了这个application类后,就需要一个特定的servlet类去处理具体的调用,其中application类做为这个servlet类的一个init-param参数指定。 不同的JAX-RS的实现者会有不同的对应的servlet的实现,这里我选择的Apache CXF的实现,它对应的servlet类为:org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet 所以我们需要在web.xml中如下样声明一个servlet:Java代码 rest org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet javax.ws.rs.Application com.restfully.shop.services.CustomerApplication rest /* 最后的web.xml文件内容如下:Java代码 JaxrsService index.html index.htm index.jsp default.html default.htm default.jsp Archetype Created Web Application rest org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet javax.ws.rs.Application com.restfully.shop.services.CustomerApplication rest /* 这个REST服务已经实现完成,接下来就可以运行了,在工程上点右键: Run As - Run on server如果没有配server,这里需要配一个web server,配置完成后,工程就会自动在这个server上运行。最后就可以测试了,这里推荐用SoapUI测试。 假设选定是tomcat,tomcat的上下文路径是http:/localhost:8080/,并且假设工程名是 JaxrsDemo,那么这个REST服务的根路径就是: http:/localhost:8080/JaxrsDemo/customers ;它会对应提供一个wadl文件,路径为: http:/localhost:8080/JaxrsDemo/customers/?_wadl 。 直接导入 http:/localhost:8080/JaxrsDemo/customers/?_wadl 到SoapUI中,对应的请求方法和结构就会自动生成,只需要填入测试内容即可。 独立的应用程序与web容器有几点不同之处:需要提供自己的上下文路径,即上面的http:/localhost:8080/JaxrsDemo部分需要自己控制启动和停止如果看看org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet的源码就会发现,其中的关键类是:org.apache.cxf.jaxrs.JAXRSServerFactoryBean。我们需要得到一个org.apache.cxf.jaxrs.JAXRSServerFactoryBean对象,然后通过它来得到一个org.apache.cxf.endpoint.Server对象,这个org.apache.cxf.endpoint.Server对象就可以理解成是一个tomcat。 参考org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet的实现,如下:Java代码 JAXRSServerFactoryBean bean = ResourceUtils.createApplication( new CustomerApplication(), false); String address = http:/localhost:8008/; bean.setAddress(address); Server server = bean.create(); server.start(); try Thread.sleep(100000000); catch (InterruptedException e) e.printStackTrace(); server.stop(); 其中 http:/localhost:8008/ 就相当于http:/localhost:8080/JaxrsDemo部分,所以这个REST服务的路径为:http:/localhost:8008/customers , 对应的服务定义文件为: http:/localhost:8008/customers/?_wadl . 剩下的测试过程同上。第三节、细节 一、若希望一个Java类能够处理REST请求,则这个类必须至少添加一个Path(/)的annotation;对于方法,这个annotation是可选的,如果不添加,则继承类的定义。 二、一个方法上只有添加了某个Http Method的annotation,例如GET,才有资格处理请求。 三、Path里的值可以是一个复杂的表达式,例如Path(id) ,其中 id表达式代码了一个模板参数;一个模板参数是一个定义在Path里的通配符,它以 开始,中间是一堆字母和数字的混合串(不能包含 / 字符),以 结尾。又如: Path(firstName-lastName) . 四、Path也支持正则表达式,例如: Path(id: d+) ,其中 d+ 表示一个数字。格式为: A : B 五、可以自定http method的annotation,具体参考已经有的实现,例如GET的实现:Java代码 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) HttpMethod(HttpMethod.GET) public interface GET 其中HttpMethod的实现为:Java代码 Target(ElementType.ANNOTATION_TYPE) Retention(RetentionPolicy.RUNTIME) Documented public interface HttpMethod /* * HTTP GET method */ public static final String GET=GET; /* * HTTP POST method */ public static final String POST=POST; /* * HTTP PUT method */ public static final String PUT=PUT; /* * HTTP DELETE method */ public static final String DELETE=DELETE; /* * HTTP HEAD method */ public static final String HEAD=HEAD; /* * HTTP OPTIONS method */ public static final String OPTIONS=OPTIONS; /* * Specifies the name of a HTTP method. E.g. GET. */ String value(); 因此,可以如下自定义新的http 请求方法:Java代码 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) HttpMethod(LOCK) public interface LOCK 注:请不要试图重写HttpMethod annotation。 六、Path的优先级规则首先检查匹配的字符的个数,匹配的个数越多越优先其次检查内嵌的模板表达式的个数,个数越多越优先最后是非缺省模板表达式的个数;缺省模板即未定义正则表达式的模板例如以下顺利:Java代码 1 /customers/id/name/address 2 /customers/id : .+/address 3 /customers/id/address 4 /customers/id : .+ 如果以上匹配不能解决你的问题,那多半是因为你的设计过于复杂,需要重新审视。 七、允许、保留和需要转意的字符所以有 a-z、A-Z、0-9 都被允许所有: _-!.()* 都被允许所有: ,;:$&+=?/ 都是保留字符 (需要转意)所有其他字符需要用 % 进行转意;格式为 %HH (%加两个十六进制数)注:如果Path中uri的表达式包含有需要转意的字符,JAX-RS会自动进行转意;否则会视之为已经进行了URL的encoding。 八、Matrix ParamMatrix Param是一个嵌在URI字符串中的name-value对,例如:/mercedes/e55;color=black/2006 Matrix Param用于修饰特定的片段,如上修饰e55片段;不同于Query Param,用于修饰整个URI资源。Matrix Param不出现在Path的表达式中,例如以上URI对应的Path的表达式可能是:Path(/e55/year)。但是可以用MatrixParam annotation来取得Matrix Param的值,例如:MatrixParam(color) 九、Subresource Locators Subresources Locators是指一个指定了Path annotation,但未指定HttpMethod的annotation,例如GET,的Java方法。这个方法返回一个含有JAX-RS annotation的REST Service对象,这个对象知道怎么去分发剩余的请求。 例如:Java代码 Path(/customers) public class CustomerDatabaseResource Path(database-db) public CustomerResource getDatabase(PathParam(database) String db) / find the instance based on the db parameter CustomerResource resource = locateCustomerResource(db); return resource; protected CustomerResource locateCustomerResource(String db) . 其中CustomerResource可能是: Java代码 public class CustomerResource private Map customerDB = new ConcurrentHashMap(); private AtomicInteger idCounter = new AtomicInteger(); POST Consumes(application/xml) public Response createCustomer(InputStream is) . GET Path(id) Produces(application/xml) public StreamingOutput getCustomer(PathParam(id) int id) . PUT Path(id) Consumes(application/xml) public void updateCustomer(PathParam(id) int id, InputStream is) . 因为CustomerResource 并不打算做一个Service暴露,所以在类上没有添加Path的annotation。 十、完全动态分发 所谓完全分发,就是实现类可以是任意类,例如上面的CustomerDatabaseResource的getDatabase()方法的实现可以改为:Java代码 Path(/customers) public class CustomerDatabaseResource protected CustomerResource europe = new CustomerResource(); protected FirstLastCustomerResource northamerica = new FirstLastCustomerResource(); Path(database-db) public Object getDatabase(PathParam(database) String db) if (db.equals(europe) return europe; else if (db.equals(northamerica) return northamerica; else return null; 这里返回值是一个Object,已经不再局限于CustomerResource类了。JAX-RS会检查这个实例,以决定怎么分发请求。第四节、注入 一、Annotationsjavax.ws.rs.PathParam: 从URI模板参数中提取数据javax.ws.rs.MatrixParam:从URI中提取Matrix参数javax.ws.rs.QueryParam:从URI中提取查询参数javax.ws.rs.FormParam:提取Post Form参数javax.ws.rs.HeaderParam:提取HTTP请求头信息javax.ws.rs.CookieParam:提取客户设置的cookie的信息javax.ws.rs.core.Context:通用的注入annotation,允许注入各种帮助或者信息对象通常这些注释用在服务方法上,当JAX-RS收到一个请求会,就会去查找相应的服务方法,然后把方法需要的信息注入。 如果是 “每个请求一个对象”的模式,你可以将这些annotation用在变量、set方法或者是构造方法上;如果是单态模式,则不允许将这些annotation用在变量、或者set方法上,因为对像会同时处理多个请求,如果将这些值用在变量或者set方法上,则多个请求会彼此冲突,陷入错误。 二、PathParamJava代码 public class CustomerResource . Path(id) GET Produces(application/xml) public StreamingOutput

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论