其他RESTEasy介绍见官网: http://resteasy.jboss.org/。本节简单介绍RESTEasy2+spring4的集成及客户端访问。
1.spring web基础环境
见之前章节创建的web工程testRest。
2.REST地址
与上一节类似,设计RESTEasy模块/resteasy/*。
3.RESTEasy库
在项目pom.xml中加入RESTEasy依赖:
3.0.0.Final ... org.jboss.resteasy resteasy-jaxrs ${resteasy.version} org.jboss.resteasy resteasy-client ${resteasy.version} org.jboss.resteasy resteasy-spring ${resteasy.version} ... org.jboss.resteasy resteasy-jackson-provider ${resteasy.version}
版本使用3.0.0.Final,因为更新的版本要求jdk1.8才能运行。
4.Servlet
配置RESTEasy访问入口。
resteasy.resources com.sunbin.test.resteasy.TeachersResource,com.sunbin.test.resteasy.TeacherResource resteasy.servlet.mapping.prefix /resteasy org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap resteasy org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher resteasy /resteasy/*
resteasy Servlet将拦截/resteasy/*下的所有访问。
RESTEasy与web、spring集成后,不需要再自己实现Application、Component等组件,也不需要配置文件进行路径配置,仅需要使用@Path注解。
5.实现api
新建com.sunbin.test.resteasy包,新增TeachersResource类,以实现/teachers路径的接口:
package com.sunbin.test.resteasy; import java.util.HashMap; import java.util.Map; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import org.springframework.beans.factory.annotation.Autowired; import com.sunbin.test.teacher.pojo.Teacher; import com.sunbin.test.teacher.service.TeacherService; @Path("/teachers") @Produces(MediaType.APPLICATION_JSON) public class TeachersResource { @Autowired private TeacherService teacherService; @GET public Map get() { System.out.println("Resteasy TeachersResource.get"); Map map = new HashMap(); map.put("teachers", teacherService.list()); return map; } @POST public Map post(@FormParam("age") int age, @FormParam("name") String name) { Map map = new HashMap(); try { Teacher teacher = new Teacher(); teacher.setName(name); teacher.setAge(age); System.out.println("Resteasy TeachersResource.post:" + teacher); teacherService.save(teacher); map.put("status", "y"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return map; } }
路径注解@Path、返回类型注解@Produces、访问方法@GET、参数读取@FormParam,都和Jersey的JAX-RS相同。
重新部署项目后,访问 http://localhost:8080/testRest/resteasy/teachers,发现报错:
java.lang.NullPointerException
com.sunbin.test.resteasy.TeachersResource.get(TeachersResource.java:29)
检查后发现是teacherService.list()语句中teacherService为null,@Autowired注解没有生效。
尝试使用xml配置方式实现teacherService注入,或使用jsr规范自有的@Inject、@Resource注解也不生效。
为什么会这样?分析是因为RESTEasy和spring的beanfactory之间依赖注入的问题。
RESTEasy官网手册推荐以下两种解决方法:
1.通过WebApplicationContextUtils工具类获取ApplicationContext对象
2.自定义工具类
具体使用方法见我之前的文章: spring4学习(三)在非Spring类中获取Bean
为了方便注入,这里采用第二种方法:定义Context工具类SpringContextHolder并配置至spring。之后需要对Resource类进行改造:
package com.sunbin.test.resteasy; import java.util.HashMap; import java.util.Map; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import com.sunbin.common.spring.SpringContextHolder; import com.sunbin.test.teacher.pojo.Teacher; import com.sunbin.test.teacher.service.TeacherService; @Path("/teachers") @Produces(MediaType.APPLICATION_JSON) public class TeachersResource { private TeacherService teacherService; public TeachersResource() { super(); // 不能自动注入,需要手动getBean赋值 teacherService = (TeacherService) SpringContextHolder .getBean("teacherService"); // TODO Auto-generated constructor stub } @GET public Map get() { System.out.println("Resteasy TeachersResource.get"); Map map = new HashMap(); map.put("teachers", teacherService.list()); return map; } @POST public Map post(@FormParam("age") int age, @FormParam("name") String name) { Map map = new HashMap(); try { Teacher teacher = new Teacher(); teacher.setName(name); teacher.setAge(age); System.out.println("Resteasy TeachersResource.post:" + teacher); teacherService.save(teacher); map.put("status", "y"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return map; } }
在TeachersResource类的构造方法中手动通过SpringContextHolder获取Bean,再赋值给resource。
同样的,新增TeacherResource类,以实现/teacher/{id}路径的接口:
package com.sunbin.test.resteasy; import java.util.HashMap; import java.util.Map; import javax.ws.rs.DELETE; import javax.ws.rs.FormParam; import javax.ws.rs.GET; 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.MediaType; import com.sunbin.common.spring.SpringContextHolder; import com.sunbin.test.teacher.pojo.Teacher; import com.sunbin.test.teacher.service.TeacherService; @Path("teacher/{id}") @Produces(MediaType.APPLICATION_JSON) public class TeacherResource { private TeacherService teacherService; public TeacherResource() { super(); teacherService = (TeacherService) SpringContextHolder .getBean("teacherService"); // TODO Auto-generated constructor stub } @GET public Map get(@PathParam("id") int id) { System.out.println("Resteasy TeacherResource.get:" + id); Teacher teacher = new Teacher(); teacher.setId(id); Map map = new HashMap(); map.put("teacher", teacherService.get(teacher)); return map; } @PUT public Map put(@PathParam("id") int id, @FormParam("age") int age, @FormParam("name") String name) { Map map = new HashMap(); try { Teacher teacher = new Teacher(); teacher.setId(id); teacher.setName(name); teacher.setAge(age); System.out.println("Resteasy TeacherResource.put:" + id + ":" + teacher); teacherService.update(teacher); map.put("status", "y"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return map; } @DELETE public Map delete(@PathParam("id") int id) { Map map = new HashMap(); try { System.out.println("Resteasy TeacherResource.delete:" + id); Teacher teacher = new Teacher(); teacher.setId(id); teacherService.remove(teacher); map.put("status", "y"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return map; } }
6.js测试
使用上一节的测试页面src\main\webapp\rest\index.jsp进行测试:
...