AI实践哪家强?来 AICon, 解锁技术前沿,探寻产业新机! 了解详情
写点什么

简单易用的 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:555421
用户头像

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

关注

评论

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

【学习课程送福利!】InfoQ最新Java开发课程喊你来领奖品!100%中奖!

飞算JavaAI开发助手

Apache 官方限定社区周边,Community Over Code 亚洲大会参会礼包抢鲜看!

Apache IoTDB

揭穿DevOps的5个谣言!

互联网工科生

DevOps 运维 自动化运维

腾讯云ES:一键配置,LDAP身份验证服务来了!

腾讯云大数据

ES

Python 运行 shell 命令的一些方法

互联网工科生

Python Shell Shell命令

“这对我个人能力的认可意义重大”!数据库“小白”到 Apache IoTDB committer 的心路历程!

Apache IoTDB

专家老师带教!现场答疑!阿里云实时计算 Flink 版线下训练营北京站来啦!

Apache Flink

大数据 flink 实时计算

上央视啦!扫描全能王科技助力社会跑步进入无纸化办公时代

合合技术团队

人工智能 环保 无纸化办公

基于eBPF技术构建一种应用层网络管控解决方案

统信软件

盘古开天、AI落地,走进华为看AI如何重塑千行百业

彭飞

如何唤醒潜在用户?选择智能化推送系统的重要性

MobTech袤博科技

前端 前端开发 APP开发 前端开发工具

火热的低代码和无代码赛道

互联网工科生

软件开发 低代码 无代码 应用开发

ARTS薪火重启之第一周

渣渣辉

算法 职业规划 心得分享

生成式AI产业趋势:技术升级与广泛应用

百度开发者中心

百度文心一言 文心大模型

生成式AI:引领我们进入零信任世界

百度开发者中心

文心一言

LeetCode题解:617. 合并二叉树,JavaScript,详细注释

Lee Chen

JavaScript LeetCode

Apache Dubbo 云原生可观测性的探索与实践

阿里巴巴云原生

Apache 阿里云 云原生 dubbo

用低代码实现企业敏捷运营

力软低代码开发平台

华为开发者大会2023:云空间筑牢鸿蒙生态“云基因”

HarmonyOS SDK

HarmonyOS

文档比对技术难点与使用场景

合合技术团队

人工智能 算法 文字识别 文档对比

生成式AI崛起,元宇宙发展遭遇挑战

百度开发者中心

元宇宙 文心一言

首起针对国内金融企业的开源组件投毒攻击事件

墨菲安全

网络安全 安全 投毒分析

生成式AI:数字医疗前瞻的新引擎

百度开发者中心

医疗 百度文心一言

生成式AI在金融行业的创新应用

百度开发者中心

金融 文心大模型

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