写点什么

简单易用的 MVC 框架:VRaptor

  • 2014-08-04
  • 本文字数:4412 字

    阅读完需:约 14 分钟

使用 Java 进行 Web 开发时,有很多基于 MVC 的框架可供选择。 VRaptor 就是其中之一。最新的 VRaptor 第四版基于 CDI1.1 。本文将带你逐步了解这一框架的原理及新版本的新增特性。

在 VRaptor 框架中创建一个控制器,只需要在 Java 类中添加 @Controller 注解即可,框架将根据其约定的 URL 和 JSP 规范完成剩余的工作,这样可以尽量减少配置文件的使用。例如:

复制代码
@Controller
public class UserController {
public void list() { //... }
}

规范简介

VRaptor 的 URL 格式规范为 controllerName/methodName,因此可以通过 user/list 访问到 list 方法。需要注意的是,后缀 Controller 并没有包含在路径中。

JSP 调度器遵循另外一个有用的规范。与 URL 的规范类似,在控制器方法执行完毕后,VRaptor 会在 WEB-INF/jsp/controllerName/methodName.jsp 这一路径下查找 JSP 文件。

在我们的例子中就是 WEB-INF/jsp/user/list.jsp

根据这些规范,Controller 中所有的公有方法会被逐一映射用于应答 HTTP 请求,不限制请求动作的类型。

根据所选择的请求动作,也可以通过使用 @Get,@Post,@Put 或 @Delete 注解限制对控制器方法的访问。

如果不想使用默认的 URL 规范,也可以在注解中添加一个 String 类型的参数,修改控制器方法的访问路径。如果不希望限制 HTTP 请求动作的类型,可以使用 @Path 注解。

复制代码
@Get("any/other/url")
public void list() { //... }

获取参数

控制器方法可以接收参数,VRaptor 会尝试为这些参数填入值。这对于简单的值类型通常是可行的,例如下面 search 方法的 long 类型值。

复制代码
@Get
public void search(long id) { //... }

如果请求中带有名为 id 的参数,也就是与方法的参数名具有相同的名字,VRaptor 会尝试将这个参数转化为控制器方法所期望的类型。通过 URL:user/search?id=1 可以访问到这个方法,或者也可以使用参数化的 URL:

复制代码
@Get("user/search/{id}")
public void search(long id) { //... }

在这种情况下,URL 路径就是 _user/search/1__。_

不仅如此,我们还可以从视图中得到更加复杂的对象,如下所示,这个 add 方法能够得到一个 User 类型的对象:

复制代码
@Post
public void add(User user) { //... }

调用 add 方法的 form.jsp 的代码如下:

复制代码
<form action="user/add" method="post">
<input type="text" name="user.name"/>
<input type="text" name="user.email"/>
<input type="submit" value="Create user">
</form>

表单的请求参数样例如下:

复制代码
user.name = Rodrigo Turini
user.email = rodrigo.turini@caelum.com.br
...

自定义结果集

如果想在 JSP 页面中获取对象列表,我们只需让控制器方法将对象列表作为参数返回。

复制代码
@Get
public List<User> list(){
return userDao.list();
}

因为返回值是一个 List类型的对象,视图中的变量名就应该是 ${userList}。如果返回类型是一个简单的 User 对象,视图变量名就是 ${user}。另外一种将对象传送给视图的方式是使用专门的 Result 接口。我们可以将这个 Bean 注入到我们的控制器中,然后调用它的 include 方法。

复制代码
@Controller
public class UserController {
@Inject private Result result;
@Inject private UserDao userDao;
@Get
public void list(){
List<User> list = userDao.list();
result.include(list);
result.include("users", list);
}
}

上述代码将同一个 list 变量作为参数传给 include 方法两次,主要是为了说明这两种方式的用法和它们之间的区别。第一个 include 方法将会生成一个在 JSP 页面中可用的 ${userList}的变量,与方法的返回值一样。第二个 include 方法显式地提供了对象的名字,因此可以通过 ${users}访问到它。

Result 类中还有很多其他的方法可以帮助我们与视图进行交互。从下面的例子可以看到,很容易就返回用 JSON 格式序列化后的对象列表。

复制代码
@Get
public void jsonList(){
List<User> users = userDao.list();
result.use(json()).from(users).serialize();
}

Result 类为我们提供了包括 json 方法在内的多个方法,用于处理最为通用的一些结果类型,例如 xml,html,jsonp 等。Result 类中还有一个 representation 方法,可以根据请求所能接受的格式序列化对象。

简单的配置

目前为止,我们所看见的配置都非常简单。因为 VRaptor 中所有的类都是由 CDI 管理的 Bean,我们可以特化(specialize)任何一个 VRaptor 组件——而且这些定制能够被完美地封装。例如,如果想更改默认的渲染视图或 VRaptor 用于查找视图的文件夹,只需要特化 DefaultPathResolver 类即可。

复制代码
@Specializes
public class CustomPathResolver extends DefaultPathResolver {
@Override
protected String getPrefix() {
return "/root/folder/";
}
}

我们也可以通过重写 PathAnnotationRoutesParser 类来修改 URL 的默认规范。

VRaptor 与 JPA 的集成

CDI 集成框架比较有趣的另一个方面就是它能够让你很方便地管理项目中的外部类。例如,VRaptor 与 JPA 的整合就相当简单。首先创建一个 EntityManager 的生产类:

复制代码
public class EntityManagerCreator {
@Inject private EntityManagerFactory factory;
@Produces @RequestScoped
public EntityManager getEntityManager() {
return factory.createEntityManager();
}
public void destroy(@Disposes EntityManager em) {
if (em.isOpen()) {
em.close();
}
}
}

然后就可以在应用程序中的任何一个 Bean 中注入这个对象的一个实例。

复制代码
public class UserDao {
@Inject private EntityManager em;
public void add(User user) {
em.getTransaction().begin();
em.persist(user);
em.getTransaction().commit()
}
// ...
}

我们也可以创建一个简单的拦截器,将视图的渲染包含在事务中。示例如下:

复制代码
@Intercepts
public class JPATransactionInterceptor {
@Inject private EntityManager manager;
@AroundCall
public void intercept(SimpleInterceptorStack stack) {
EntityTransaction transaction = manager.getTransaction();
transaction.begin();
stack.next();
transaction.commit();
}
}

从上面的代码中可以看到,使用 @AroundCall 注解的拦截器方法接收一个 SimpleInterceptorStack 对象作为参数。调用 next() 方法就会将请求分派给控制器,因此我们需要在调用 next() 方法之前打开事务,然后在调用之后关闭事务。

另外一种将视图的渲染包含在事务中的方法就是支持 @Transactional 注解,然后仅在包含这个注解的方法上应用拦截器。这种方法和之前的方法一样很简单,只需要在拦截器中添加一个 accepts 方法即可。

复制代码
@Accepts
public boolean accepts() {
return method.containsAnnotation(Transactional.class);
}

在 UserController 类的 add 方法上添加 @Transactional 注解:

复制代码
@Controller
public class UserController {
@Inject private Result result;
@Inject private UserDao userDao;
@Get
public void list() {
List<User> list = userDao.list();
result.include(list);
result.include("users", list);
}
@Post @Transactional
public void add(User user) {
userDao.add(user);
}
}

然后 UserDao 类的方法只需要将持久化的工作代理给 EntityManager.persist 即可:

复制代码
public void add(User user) {
em.persist(user);
}

添加插件

与许多其他的功能一样,上述功能已经以 VRaptor4 插件的形式实现并发布。上文所提及的拦截器和生产者分别位于 vraptor-jpa vraptor-hibernate 插件中,只需要将相关的 jar 包添加到项目中(或通过配置自己熟悉的依赖管理工具)就可以正常使用它们,而无需更多的配置。

只要插件中包含 beans.xml 文件,CDI 框架就可以管理插件中的类并且能够让这些类在 VRaptor 上被注入。由于创建扩展非常简单,插件开发的社区参与非常活跃并且已经创建了很多插件。在 VRaptor 的框架文档中罗列了其中一部分插件。

深入挖掘 VRaptor 在 JavaEE 中的使用

在应用服务器中集成 VRaptor 与 JPA 和事务控制更容易。可以使用注解 @PersistenceContext 替代 CDI 的 @Inject 注入 EntityManager。这样就可以使用注解 javax.transaction.Transactional 来控制方法中的事务。

复制代码
@Controller
public class UserController {
@Inject private Result result;
@Inject private UserDao userDao;
@Get
public void list(){
List<User> list = userDao.list();
result.include("users", list);
}
@Post @Transactional
public void add(User user){
userDao.add(user);
}
@Post @Transactional
public void remove(User user){
userDao.delete(user);
}
}

或者也可以像下面这段代码一样将 @Transactional 注解直接添加到 VRaptor 控制器上,而不是对方法做注解:

复制代码
@Controller @Transactional
public class UserController {
@Inject private Result result;
@Inject private UserDao userDao;
// remaining code
}

之所以可以这样,是因为 VRaptor 控制器和其他的类一样,都是由 CDI 管理的 Bean。

VRaptor 示例

通过基于 VRaptor 框架的开源项目,可以在实践中了解更多关于 VRaptor 框架的功能。 Mamute 问答引擎就是一个很好地利用 Vraptor4 新特性的例子,具体内容可以参见项目代码库。也可以通过VRaptor 框架的示例应用 vraptor-music-jungle 和已经配置好的基础项目 blank-project 快速展开学习。

关于 VRaptor4 的更多信息

VRaptor 官方文档中可以了解到更多关于 VRaptor4 框架的更多信息:从一分钟指南十分钟指南开始,然后学习从老版本向新版本迁移的教程,再之后可以浏览由社区编写的详细说明文档。如果想更加深入地学习,可以阅读与VRaptor 内核无缝集成的 CDI1.1 的规格说明书

关于作者

Rodrigo Turini毕业于计算机科学专业目前在巴西的 Caelum 公司工作,担任开发者和讲师的角色。他是 Vraptor4 的领导者之一并在许多其他开源项目中有所贡献。

查看英文原文: VRaptor MVC Framework; Powerful Simplicity


感谢崔康对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2014-08-04 10:555566
用户头像

发布了 75 篇内容, 共 66.3 次阅读, 收获喜欢 6 次。

关注

评论

发布
暂无评论
发现更多内容

以生产力为中心:超宽带的跨时代之舞

脑极体

超宽带

OpenResty 入门以及 WAF 防御实战

越长大越悲伤

nginx openresty waf

安装Linux系统对硬件的要求

芯动大师

可观测 AIOps 的智能监控和诊断实践丨QCon 全球软件开发大会总结

阿里巴巴云原生

阿里云 云原生 AIOPS 可观测

不抖机灵!让工程师来告诉你做芯片是如何烧钱的!

IC男奋斗史

芯片 半导体 芯片测试 ATE测试

工赋开发者社区 | 工业机器人产业链及主要配套厂商

工赋开发者社区

Python - 字典2

小万哥

Python 程序员 软件 后端 开发

文心一言 VS 讯飞星火 VS chatgpt (113)-- 算法导论10.2 5题

福大大架构师每日一题

福大大架构师每日一题

如何转产品工程师?

IC男奋斗史

职业规划 芯片 半导体 芯片测试 ATE测试

ARTS 打卡第6周

AI帅辉

ARTS 打卡计划 学习分享

工赋开发者社区 | 精益与MES融合下的数字化转型

工赋开发者社区

CSS小技巧之单标签loader

南城FE

CSS css3 前端 Loader

Easysearch压缩模式深度比较:ZSTD+source_reuse的优势分析

极限实验室

easysearch

【论文阅读】【三维场景点云分割】Superpoint Transformer for 3D Scene Instance Segmentation

AI帅辉

深度学习 论文阅读 Transformer 分割 3D点云

公平,而非平等

俞凡

管理 组织架构

阿尔比特(ARBT)DAPP质押挖矿系统开发丨源码技术搭建

l8l259l3365

活在无限中

少油少糖八分饱

读后感 阅读 动漫 葬送的芙莉莲 有限与无限游戏

充换电企业开迈斯低成本提升线上应用稳定性的最佳实践

阿里巴巴云原生

阿里云 云原生

简单易用的MVC框架:VRaptor_Java_Rodrigo Turini_InfoQ精选文章