Christopher Franklin 2024-01-08
1. 简介
Javalin 是一个为 Java 和 Kotlin 编写的轻量级 Web 框架。它构建在 Jetty Web 服务器之上,因此性能非常高。Javalin 的设计灵感来源于 koa.js,从底层开始就注重简洁性和易用性。
在本教程中,我们将逐步构建一个基于该轻量框架的基本 REST 微服务。
2. 添加依赖项
要创建一个基本应用,我们只需要一个依赖项 —— Javalin 本身:
<dependency>
<groupId>io.javalin</groupId>
<artifactId>javalin</artifactId>
<version>1.6.1</version>
</dependency>
最新版本可在此处查看:Javalin 官网
3. 配置 Javalin
Javalin 让搭建基本应用变得非常简单。我们首先定义主类,并设置一个简单的 “Hello World” 应用。
在项目的根包下创建一个名为 JavalinApp.java 的新文件。
在该文件中,创建一个 main 方法,并添加以下代码以启动一个基础应用:
Javalin app = Javalin.create()
.port(7000)
.start();
app.get("/hello", ctx -> ctx.html("Hello, Javalin!"));
这里我们创建了一个 Javalin 实例,让它监听 7000 端口并启动应用。
同时,我们还设置了第一个端点,用于响应 /hello 路径的 GET 请求。
运行此应用后,访问 http://localhost:7000/hello 即可看到效果。
4. 创建 UserController
“Hello World” 示例非常适合入门,但在实际应用中作用有限。现在我们来看一个更贴近现实的使用场景。
首先,我们需要定义所操作对象的数据模型。在项目根目录下创建一个名为 user 的包。
然后,在该包中添加一个新的 User 类:
public class User {
public final int id;
public final String name;
// 构造函数
}
接下来,我们需要设置数据访问对象(DAO)。在本示例中,我们将使用内存中的对象来存储用户数据。
在 user 包中创建一个名为 UserDao.java 的新类:
class UserDao {
private List<User> users = Arrays.asList(
new User(0, "Steve Rogers"),
new User(1, "Tony Stark"),
new User(2, "Carol Danvers")
);
private static UserDao userDao = null;
private UserDao() {
}
static UserDao instance() {
if (userDao == null) {
userDao = new UserDao();
}
return userDao;
}
Optional<User> getUserById(int id) {
return users.stream()
.filter(u -> u.id == id)
.findAny();
}
Iterable<String> getAllUsernames() {
return users.stream()
.map(user -> user.name)
.collect(Collectors.toList());
}
}
将 DAO 实现为单例模式,便于在本示例中使用。当然,我们也可以将其声明为主类的静态成员,或使用如 Guice 这样的依赖注入库。
最后,我们创建控制器类。Javalin 在定义路由处理器方面非常灵活,以下只是其中一种方式。
在 user 包中创建一个名为 UserController.java 的新类:
public class UserController {
public static Handler fetchAllUsernames = ctx -> {
UserDao dao = UserDao.instance();
Iterable<String> allUsers = dao.getAllUsernames();
ctx.json(allUsers);
};
public static Handler fetchById = ctx -> {
int id = Integer.parseInt(Objects.requireNonNull(ctx.param("id")));
UserDao dao = UserDao.instance();
Optional<User> user = dao.getUserById(id);
if (user.isPresent()) {
ctx.json(user);
} else {
ctx.html("Not Found");
}
};
}
通过将处理器声明为 static,我们确保控制器本身不保存任何状态。但在更复杂的应用中,如果需要在请求之间共享状态,则应移除 static 修饰符。
另外请注意,静态方法更难进行单元测试。如果需要良好的可测试性,应使用非静态方法。
5. 添加路由
我们现在已有多种方式从模型中获取数据。最后一步是通过 REST 端点暴露这些数据。我们需要在主应用中注册两个新路由。
将以下代码添加到主应用类中:
app.get("/users", UserController.fetchAllUsernames);
app.get("/users/:id", UserController.fetchById);
编译并运行应用后,即可调用这些新端点:
- 访问 http://localhost:7000/users 将列出所有用户名;
- 访问 http://localhost:7000/users/0 将返回 ID 为 0 的用户 JSON 对象。
至此,我们已拥有一个可以检索用户数据的微服务。
6. 扩展路由
大多数微服务的核心任务之一是数据检索。
但我们也需要能够向数据存储中写入数据。Javalin 提供了构建服务所需的所有 HTTP 方法处理器。
前面我们看到了 GET 的示例,此外还支持 PATCH、POST、DELETE 和 PUT。
另外,如果我们引入 Jackson 作为依赖项,就可以自动将 JSON 请求体解析为模型类。例如:
app.post("/") { ctx ->
User user = ctx.bodyAsClass(User.class);
}
这允许我们直接从请求体中提取 JSON 格式的用户对象,并将其转换为 User 模型实例。
7. 结论
我们可以结合上述所有技术来构建完整的微服务。
本文介绍了如何配置 Javalin 并构建一个简单应用,还讨论了如何使用不同的 HTTP 方法,使客户端能够与我们的服务进行交互。