写点什么

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

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

关注

评论

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

CAT 对接观测云最佳实践

观测云

cat

提升用户体验:阿里巴巴拍立淘API返回值的个性化推荐

技术冰糖葫芦

API Explorer API boy API 接口 API 测试

用友BIP超级版商旅费控,AI+成就一站式智慧出行

用友BIP

科大讯飞智能办公本x3和x3pro区别对比

妙龙

科大讯飞 学习机

望繁信科技荣膺上海市浦东新区博士后创新实践基地称号

望繁信科技

流程挖掘 流程智能 数字北极星 上海望繁信科技 博士后基地

区块链技术的基本理论

天翼云开发者社区

区块链

知网状告AI搜索:搜到我家论文题目和摘要,你侵权了!

Openlab_cosmoplat

人工智能

Python深度解析:上下文协议设计与应用技巧

我再BUG界嘎嘎乱杀

Python 编程 后端 开发语言 上下文管理

下载量突破400万,百万开发者首选的 AI 编码工具通义灵码是如何炼成的?

阿里云云效

阿里云 云原生 通义灵码

东南亚TikTok直播:直播专线的重要性及其优势

Ogcloud

海外直播专线 tiktok运营 tiktok直播 tiktok直播专线 tiktok直播网络

小谈容灾备份

天翼云开发者社区

云计算 容灾

NFT智能合约线上藏品交易元宇宙区块链链游开发

V\TG【ch3nguang】

用友BIP3 R6发布,平台技术能力再升级

用友BIP

天翼云CDR基本概念

天翼云开发者社区

云计算 云主机 天翼云 容灾服务

浅谈ChatGPT在云计算资源调度的应用

天翼云开发者社区

云计算 大模型 ChatGPT

信创环境:鲲鹏ARM+麒麟V10离线部署K8s和Rainbond信创平台

北京好雨科技有限公司

kubernete rainbond 信创云 企业号 8 月 PK 榜 国产化平台

90 分钟带你玩转知识库应用

Baidu AICLOUD

数据库 向量数据库

ShareSDK Twitter

MobTech袤博科技

开发者

68种架构可扩展性设计:从类到服务治理(成就架构师必备,收藏金典)

肖哥弹架构

架构设计 架构要素

10分钟构建Web 应用防火墙-WAF

coxi_vv

Web waf Web防火墙 1Panel

解决海外直播卡顿的方法

Ogcloud

海外直播专线 海外直播 海外直播网络

PSD文件用什么打开?免费白板软件在线查看设计文件!

职场工具箱

效率工具 职场 ps 在线白板 办公软件

ShareSDK 企业微信

MobTech袤博科技

ShareSDK抖音

MobTech袤博科技

Redis缓存异常及解决方案

天翼云开发者社区

redis 缓存

利用Halo 20分钟构建企业门户

coxi_vv

开源软件 halo 1Panel

用友BIP超级版新品路演收官,加速中大型企业数智化进程

用友BIP

势不可挡的All in AI,用友BIP AI+行业创新实践

用友BIP

提升游戏直播平台用户参与度的关键,开发游戏社区论坛模块

软件开发-梦幻运营部

下载量突破400万,百万开发者首选的 AI 编码工具通义灵码是如何炼成的?

阿里巴巴云原生

阿里云 云原生 通义灵码

10分钟构建代码托管平台Gitea

coxi_vv

开源软件 Gitea 1Panel

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