1. 概述
Jersey 是一个开源框架,用于开发 RESTful Web 服务。
它不仅是 JAX-RS 规范的官方参考实现,还提供了一系列扩展功能,简化 Web 应用开发。
本文将通过一个小型示例,演示如何使用 Jersey 提供的 Model-View-Controller(MVC)扩展。
如果你更关注纯 API 开发,可以参考我们另一篇 Jersey + Spring 构建 REST API 的教程。
✅ 提示:本文重点在 MVC 渲染,不是写 REST 接口,别跑偏。
2. Jersey 中的 MVC
Jersey 提供了对 MVC 设计模式的支持扩展,允许你在服务端渲染页面,而不仅仅是返回 JSON。
在 Jersey 的上下文中,MVC 各组件对应如下:
- ✅ Controller(控制器):即带有
@Path
注解的资源类或方法 - ✅ View(视图):绑定到资源的模板文件(如
.ftl
) - ✅ Model(模型):资源方法返回的 Java 对象,用于填充模板
要启用 MVC 功能,必须注册对应的 MVC 模块。
本文选用主流模板引擎 Freemarker。Jersey 原生支持多种渲染引擎,包括:
- Freemarker
- Mustache
- JSP(Java Server Pages)
⚠️ 踩坑提醒:JSP 在嵌入式容器中支持较差,生产环境建议用 Freemarker 或 Mustache。
更多 MVC 原理可参考:Servlet + JSP 的 MVC 实现。
3. 项目搭建
本节将配置 Maven 依赖,并使用嵌入式 Grizzly 服务器快速启动应用。
3.1. Maven 依赖
首先引入 Jersey MVC 的 Freemarker 扩展:
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-mvc-freemarker</artifactId>
<version>3.1.1</version>
</dependency>
接着添加嵌入式容器 Grizzly 的支持:
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-servlet</artifactId>
<version>3.1.1</version>
</dependency>
✅ 版本一致很重要,避免兼容问题。
3.2. 服务器配置
要启用 MVC 模板功能,需注册对应的 JAX-RS 特性。
我们创建一个自定义配置类:
public class ViewApplicationConfig extends ResourceConfig {
public ViewApplicationConfig() {
packages("com.baeldung.jersey.server");
property(FreemarkerMvcFeature.TEMPLATE_BASE_PATH, "templates/freemarker");
register(FreemarkerMvcFeature.class);
}
}
关键配置说明:
配置项 | 作用 |
---|---|
packages() |
扫描指定包下所有 @Path 注解的资源类 |
TEMPLATE_BASE_PATH |
模板文件根路径,对应 src/main/resources/templates/freemarker |
register(FreemarkerMvcFeature.class) |
启用 Freemarker 渲染支持 |
✅ 约定优于配置:模板默认放在
resources
下,别放错目录。
3.3. 启动应用
使用 exec-maven-plugin
快速启动嵌入式服务器:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<mainClass>com.baeldung.jersey.server.http.EmbeddedHttpServer</mainClass>
</configuration>
</plugin>
编译并运行:
mvn clean compile exec:java
输出日志:
Jul 28, 2018 6:21:08 PM org.glassfish.grizzly.http.server.HttpServer start
INFO: [HttpServer] Started.
Application started.
Try out http://localhost:8082/fruit
Stop the application using CTRL+C
访问 http://localhost:8082/fruit,看到 “Welcome Fruit Index Page!” 即表示成功。
✅ 端口是 8082 不是 8080,别惯性思维。
4. MVC 模板使用方式
Jersey 的 MVC 核心是两个类:**Viewable
** 和 @Template
注解。
下面介绍三种绑定模板的姿势。
4.1. 使用 Viewable
类
最直接的方式:在资源方法中返回 Viewable
实例。
@Path("/fruit")
public class FruitResource {
@GET
public Viewable get() {
return new Viewable("/index.ftl", "Fruit Index Page");
}
}
解析:
- ✅
FruitResource
是 Controller - ✅
"Fruit Index Page"
是 Model(字符串) - ✅
/index.ftl
是 View 模板路径
简单粗暴,适合快速原型。
4.2. 使用 @Template
注解
不想每次都 new Viewable?用 @Template
更清爽。
@GET
@Template(name = "/all.ftl")
@Path("/all")
@Produces(MediaType.TEXT_HTML)
public Map<String, Object> getAllFruit() {
List<Fruit> fruits = new ArrayList<>();
fruits.add(new Fruit("banana", "yellow"));
fruits.add(new Fruit("apple", "red"));
fruits.add(new Fruit("kiwi", "green"));
Map<String, Object> model = new HashMap<>();
model.put("items", fruits);
return model;
}
优势:
- ✅ 方法返回值直接作为 Model
- ✅ 模板路径通过注解声明,代码更清晰
- ✅ 返回
Map<String, Object>
是常见做法,便于模板访问
对应的 all.ftl
模板会接收到 items
变量并渲染列表。
4.3. 错误处理:@ErrorTemplate
出错了怎么返回友好页面?用 @ErrorTemplate
!
@GET
@ErrorTemplate(name = "/error.ftl")
@Template(name = "/named.ftl")
@Path("{name}")
@Produces(MediaType.TEXT_HTML)
public String getFruitByName(@PathParam("name") String name) {
if (!"banana".equalsIgnoreCase(name)) {
throw new IllegalArgumentException("Fruit not found: " + name);
}
return name;
}
行为逻辑:
- ✅ 正常情况:使用
/named.ftl
渲染 - ❌ 异常情况:自动跳转到
/error.ftl
,Model 是异常对象本身
error.ftl
示例:
<body>
<h1>Error - ${model.message}!</h1>
</body>
${model.message}
直接调用异常的 getMessage()
方法。
✅ 单元测试验证错误流:
@Test
public void givenGetFruitByName_whenFruitUnknown_thenErrorTemplateInvoked() {
String response = target("/fruit/orange").request()
.get(String.class);
assertThat(response, containsString("Error - Fruit not found: orange!"));
}
测试访问 /fruit/orange
,确认返回错误页面且包含异常信息。
5. 总结
本文带你实战了 Jersey 的 MVC 扩展功能:
- ✅ 如何集成 Freemarker 模板引擎
- ✅ 三种模板绑定方式:
Viewable
、@Template
、@ErrorTemplate
- ✅ 错误页面统一处理,提升用户体验
虽然现在前后端分离是主流,但在某些内部系统、管理后台或需要服务端渲染的场景下,Jersey MVC 依然能帮你快速出活。
💡 项目源码已托管:GitHub - eugenp/tutorials