论坛首页 Java企业应用论坛

向大家推荐一个轻量级的java rest 框架 JRest4Guice

浏览 8390 次
精华帖 (0) :: 良好帖 (10) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-03-27  
本帖已转向:http://www.iteye.com/topic/201103


[i]
大家好,今天向大家推荐一个轻量级的java rest 框架 JRest4Guice

项目地址: http://code.google.com/p/jrest4guice/

这个项目借鉴了http://www.iteye.com/topic/170289的一些思想和代码。本人在此先谢了。

特点:
     1. 基于GUICE

     2. 零配置式服务声明
          @Restful(uri = "/contacts")
          public class ContactListRestService

     3. 服务的自动扫描注册
    
     4. 非侵入式风格,用户不需要实现特定的接口来实现Restful服务
          用户只要在指定的POJO上:
          1. 声明为@Restful,并指明访问的URI格式
          2. 在指定的方法上声明为@HttpMethod

     5. 支持Rest的Post. Get. Put. Delete操作
          用户在指定的方法上通过@HttpMethod注解来声明方法的类型,如下:
          @HttpMethod(type = HttpMethodType.POST)
          public String createContact(String name, @RequestParameter("homePhone") String homePhone, @ModelBean Contact contact)

          @HttpMethod
          public String getContact(@FirstResult int first, @MaxResults int max)
          注:如果没有提供HttpMethodType类型的声明,系统会自动根据方法名称的前缀来自动识别(方法名必须以get/post/put/delete开头)

     6. 灵活的注入
          6.1. 支持HttpServletRequest. HttpServletResponse. ModelMap的注入
                 @Inject
               private ModelMap modelMap;

               @Inject
               private HttpServletRequest request;

               @Inject
               private HttpServletResponse response;

          6.2. 支持参数的自动注入
               方法中的参数可以由系统自动注入,如下:
               public String createContact(String name, @RequestParameter("homePhone") String homePhone, @ModelBean Contact contact)
               注:如果参数没有任何注解,系统默认获取上下文ID为参数名称的参数值,否则通过@RequestParameter注解指定的参数名称来获取,@ModelBean可以将上下文中的参数转换成指定参数类型的Java bean

          6.3. 支持对JndiResource的注入         


示例代码:
@Restful(uri = { "/contact", "/contact/{contactId}" })
public class ContactRestService {
	@Inject
	private ModelMap modelMap;

	@Inject
	private HttpServletRequest request;

	@Inject
	private HttpServletResponse response;

	@Inject
	@JndiResource(jndi = "test/ContactService")
	private ContactService service;

	@HttpMethod(type = HttpMethodType.POST)
	public String createContact(String name, @RequestParameter("homePhone") String homePhone, @ModelBean Contact contact) {
		if (contact == null)
			return HttpResult.createFailedHttpResult("-1","联系人信息不能为空").toJson();
		String contactId = null;
		try {
			contactId = this.service.createContact(contact);
			return HttpResult.createSuccessfulHttpResult(contactId).toJson();
		} catch (RemoteException e) {
			return HttpResult.createFailedHttpResult(e.getClass().getName(),e.getMessage()).toJson();
		}
	}

	@HttpMethod
	public String putContact(@RequestParameter("contactId")
	String contactId, @ModelBean
	Contact contact) {
		if (contactId == null)
			return HttpResult.createFailedHttpResult("-1","没有指定对应的联系人标识符").toJson();

		try {
			this.service.updateContact(contact);
			return HttpResult.createSuccessfulHttpResult("修改成功").toJson();
		} catch (RemoteException e) {
			return HttpResult.createFailedHttpResult(e.getClass().getName(),e.getMessage()).toJson();
		}
	}

	@HttpMethod
	public String getContact(@RequestParameter("contactId")
	String contactId) {
		try {
			Contact contactDto = this.service.findContactById(contactId);
			return HttpResult.createSuccessfulHttpResult(contactDto).toJson();
		} catch (Exception e) {
			return HttpResult.createFailedHttpResult(e.getClass().getName(),e.getMessage()).toJson();
		}
	}

	@HttpMethod
	public String deleteContact(@RequestParameter("contactId")
	String contactId) {
		try {
			this.service.deleteContact(contactId);
			return HttpResult.createSuccessfulHttpResult("删除成功").toJson();
		} catch (Exception e) {
			return HttpResult.createFailedHttpResult(e.getClass().getName(),e.getMessage()).toJson();
		}
	}
}
[/i]
   发表时间:2008-03-27  
不想用SVN的朋友可以直接下载这个压缩包,里面包含了例子
0 请登录后投票
   发表时间:2008-03-27  
学习中 ,永远支持你 ,谢谢发表好的知识。
0 请登录后投票
   发表时间:2008-03-27  
还是不喜欢@HttpMethod。

为什么需要它呢?难道简单的这样不好么?

Contact contact = resourceLocator.getResource(path, Contact.class);
contactRepository.addContact(contact);


为什么非要把rest method绑在pojo里?
0 请登录后投票
   发表时间:2008-03-28  
ajoo 写道
还是不喜欢@HttpMethod。

为什么需要它呢?难道简单的这样不好么?

Contact contact = resourceLocator.getResource(path, Contact.class);
contactRepository.addContact(contact);


为什么非要把rest method绑在pojo里?


注解的方式是把资源看成是URI的映射,如果把资源看成是具体的类,虽然说可以通过超类继承很多基础方法,但带来的是对类的扩展无法更方便的支持。

如果不使用@HttpMethod,也许我们会这样:
1、让每个Rest 服务必须要实现一个特定的接口,如:
public interface RestAble{
    public void get();
    public void post();
    public void put();
    public void delete();
}


2、通过一种方法名的约束,让具体的Rest方法前必须以类似get/post/put/delete的前缀

而引入@HttpMethod,可以:
1、从平台特定的API中解放出来,不要实现任何带限制的接口,就像EJB2.0那样要求每个EJB实现home/remote接口一样。
2、不会限制用户的业务方法的命名,如下:
public void registUser(....);
public void postUser(....);


以下两种方式,哪一种各能让用户接受呢?
0 请登录后投票
   发表时间:2008-03-28  
不绑@HttpMethod,那么一般通过url映射的就是Contact这种domain model,也就是rest的资源的概念。

而绑了@HttpMethod,那么写的就是你例子中的ContactService的这种服务模型。

那么前者怎么处理put/create/get呢?

很灵活,可以用你提出的Restable的接口,但是这个接口不是框架定义的,而是客户自己定义的,然后客户就这么调用:
Restable restable = resourceLocator.getResource(path, Restable.class);
restable.get();


也可以,干脆不搞什么接口。比如我的应用还是传统的servlet或者struts action。只想在某个servlet里面嵌入rest的url映射,那么在某个FooServlet的doGet()里面,我就这么做:
Foo foo = resourceLocator.getResource(request.getPathInfo(), Foo.class);
response.getWriter().write(foo.getName());


而如果绑了@HttpMethod,框架做的事情更多,更大,更复杂,但是客户程序的灵活性就低了,你只能针对服务模型设计,必须在FooService里面处理get/put,而不能交给系统别的部分去做。

所以我觉得,除非@HttpMethod有特别突出的优点是domain model提供不了的,还是不要强迫客户走这个服务模型。

0 请登录后投票
   发表时间:2008-03-28  
ajoo 写道
不绑@HttpMethod,那么一般通过url映射的就是Contact这种domain model,也就是rest的资源的概念。

而绑了@HttpMethod,那么写的就是你例子中的ContactService的这种服务模型。

那么前者怎么处理put/create/get呢?

很灵活,可以用你提出的Restable的接口,但是这个接口不是框架定义的,而是客户自己定义的,然后客户就这么调用:
Restable restable = resourceLocator.getResource(path, Restable.class);
restable.get();


也可以,干脆不搞什么接口。比如我的应用还是传统的servlet或者struts action。只想在某个servlet里面嵌入rest的url映射,那么在某个FooServlet的doGet()里面,我就这么做:
Foo foo = resourceLocator.getResource(request.getPathInfo(), Foo.class);
response.getWriter().write(foo.getName());


而如果绑了@HttpMethod,框架做的事情更多,更大,更复杂,但是客户程序的灵活性就低了,你只能针对服务模型设计,必须在FooService里面处理get/put,而不能交给系统别的部分去做。

所以我觉得,除非@HttpMethod有特别突出的优点是domain model提供不了的,还是不要强迫客户走这个服务模型。



嗯,我大概明白你的想法了,理由确实也是很充分的。不过有些地方,我的看法与你有些不同。

1、对REST上资源的理解,我不认为它必须与Domain Model对应,我只会把它们看为有固定结构的数据体,当然它们也可以和Domain Model对应,但更多时候它们可能只是某种数据视图。所以我只会把它们看成是某种有含义受约束的URI,而不会将它们和Domain Model画等号。

2、由于有第一点的观点,所以我认为REST内资源的(GET, POST, PUT, DELETE)的含义是可由应用定义的,也不一定需要全部实现。

3、至于和其它MVC整合,我觉得不需要强迫要在其它MVC内完成REST的工作,两者完全可以在URI级就很好地分离开。对于某些需要公用的东西,则可以通过应用的上下文共享。
0 请登录后投票
   发表时间:2008-03-29  
不错。我也不确定我对资源的理解是不是准确。只不过我觉得不绑HttpMethod的话,你可以有灵活性,想domain就domain,想service就service。

另外,不是强迫整合,而是说,嵌入能力是一个比较好的特性。不能嵌入,而非得从头到尾用你的框架就有些局限罢了。
0 请登录后投票
   发表时间:2008-03-29  
最新动态


经过几天的奋战,我们团队又发布了一个基于Guice的JPA实现--Jpa4Guice 0.1 预览版,示例如下:

1、业务接口
@ImplementedBy(ContactServiceBean.class)
public interface ContactService {
	public String createContact(Contact contact) throws RemoteException;
	public List<Contact> listContacts(int first,int max) throws RemoteException;
	public Contact findContactById(String contactId) throws RemoteException;
	public void updateContact(Contact contact) throws RemoteException;
	public void deleteContact(String contactId) throws RemoteException;
}


2、业务实现
public class ContactServiceBean implements ContactService {
	@Inject
	private EntityManager entityManager;//JPA实体管理器的注入

	@Transactional//事务声明
	public String createContact(Contact contact) throws RemoteException {
		if (contact == null)
			throw new RemoteException("联系人的内容不能为空");
		
		if(this.entityManager.createNamedQuery("byName").setParameter("name", contact.getName()).getResultList().size()>0){
			throw new RemoteException("联系人的姓名相同,请重新输入");
		}

		this.entityManager.persist(contact);
		return contact.getId();
	}

	@Transactional//事务声明
	public void deleteContact(String contactId) throws RemoteException {
		Contact contact = this.findContactById(contactId);
		if (contact == null)
			throw new RemoteException("联系人不存在");

		this.entityManager.remove(contact);
	}

	@Transactional//事务声明
	public Contact findContactById(String contactId) throws RemoteException {
		return this.entityManager.find(Contact.class, contactId);
	}

	@Transactional//事务声明
	public List<Contact> listContacts(int first, int max)
			throws RemoteException {
		return this.entityManager.createNamedQuery("list").setFirstResult(first)
				.setMaxResults(max).getResultList();
	}

	@Transactional//事务声明
	public void updateContact(Contact contact) throws RemoteException {
		if (contact == null)
			throw new RemoteException("联系人的内容不能为空");

		this.entityManager.merge(contact);
	}
}


3、Rest的控制类
@Restful(uri = { "/contact", "/contact/{contactId}" })
public class ContactController {
@Inject
private ContactService service;//注入的业务接口

@HttpMethod(type = HttpMethodType.POST)
public String createContact(String name, @RequestParameter("homePhone") String homePhone, @ModelBean Contact contact) {}

...
...
}

===========================================
我们会在下一个版本中增加动态DAO的支持
===========================================

注:
  1、由于最近的更新比较频繁,所以没有打包,有需要的朋友请从SVN上直接下载原代码,源代码中有详细的例子。
  2、示例已经可以运行jboss/tomcat/jetty服务器上,已经完全脱离了EJB环境。

谢谢大家的参与和批评。

-- cnoss小组
0 请登录后投票
   发表时间:2008-04-15  
小建议一下, 能否像webwork一样,使用maven进行管理依赖

这样checkout就不需要下载额外的jar
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics