在本文中,我们将快速介绍Spark 框架。Spark 框架是一个快速开发的 Web 框架,其灵感来自 Ruby 的 Sinatra 框架,并围绕 Java 8 Lambda 表达式理念构建,使其比使用其他 Java 框架编写的大多数应用程序更简洁。
如果您想在使用 Java 开发 Web API 或微服务时获得类似Node.js的体验,这是一个不错的选择。使用 Spark,您只需不到 10 行代码就可以准备好提供 JSON 的 REST API。
我们将从一个“Hello World”示例快速开始,然后是一个简单的 REST API。
在pom.xml中包含以下 Maven 依赖项:
- <dependency>
- <groupId>com.sparkjavagroupId>
- <artifactId>spark-coreartifactId>
- <version>2.5.4version>
- dependency>
您可以在Maven Central上找到最新版本的 Spark 。
在示例中的各个地方,我们将使用 Gson 库进行 JSON 操作。要在您的项目中包含 Gson,请在您的pom.xml中包含此依赖项:
- <dependency>
- <groupId>com.google.code.gsongroupId>
- <artifactId>gsonartifactId>
- <version>2.8.0version>
- dependency>
您可以在Maven Central上找到最新版本的 Gson 。
让我们看一下 Spark 应用程序的基本构建块,并演示一个快速 Web 服务。
Spark Java 中的 Web 服务建立在路由及其处理程序之上。路由是 Spark 中必不可少的元素。根据文档,每条路由都由三个简单的部分组成——动词、路径和回调。
在这里,我们展示了使用get动词的路由的基本结构:
- get("/your-route-path/", (request, response) -> {
- // your callback code
- });
让我们创建一个简单的 Web 服务,它有两条 GET 请求路由并返回“Hello”消息作为响应。这些路由使用get方法,这是从类spark.Spark的静态导入:
- import static spark.Spark.*;
-
- public class HelloWorldService {
- public static void main(String[] args) {
-
- get("/hello", (req, res)->"Hello, world");
-
- get("/hello/:name", (req,res)->{
- return "Hello, "+ req.params(":name");
- });
- }
- }
get方法的第一个参数是路由的路径。第一个路由包含一个静态路径,仅代表一个 URI(“/hello”)。
第二条路由的路径(“/hello/:name” )包含“name”参数的占位符,通过在参数前面加上冒号(“:”)来表示。将调用此路由以响应对 URI 的 GET 请求,例如“/hello/Joe”和“/hello/Mary”。
get方法的第二个参数是一个lambda 表达式,为该框架提供了函数式编程风格。
lambda 表达式将请求和响应作为参数并帮助返回响应。我们将把控制器逻辑放在 REST API 路由的 lambda 表达式中,我们将在本教程后面看到。
将HelloWorldService类作为普通 Java 类运行后,您将能够使用上述get方法定义的路由在其默认端口4567上访问该服务。
让我们看看第一个路由的请求和响应:
要求:
GET http://localhost:4567/hello
回复:
Hello, world
让我们测试第二条路由,在其路径中传递name参数:
要求:
GET http://localhost:4567/hello/baeldung
回复:
Hello, baeldung
了解如何使用 URI 中文本“baeldung”的位置来匹配路由模式“/hello/:name” ——导致调用第二个路由的回调处理函数。
在本节中,我们将为以下用户实体设计一个简单的 REST Web 服务:
- public class User {
- private String id;
- private String firstName;
- private String lastName;
- private String email;
-
- // constructors, getters and setters
- }
让我们列出构成我们 API 的路由:
下面是为User实体声明 CRUD 操作的UserService接口:
- public interface UserService {
-
- public void addUser (User user);
-
- public Collection
getUsers (); - public User getUser (String id);
-
- public User editUser (User user)
- throws UserException;
-
- public void deleteUser (String id);
-
- public boolean userExist (String id);
- }
出于演示目的,我们在 GitHub 代码中提供了这个UserService接口的Map实现来模拟持久性。您可以使用您选择的数据库和持久层来提供您自己的实现。
下面是我们的 REST 服务中使用的响应的 JSON 结构:
- {
- status: <STATUS>
- message: <TEXT-MESSAGE>
- data: <JSON-OBJECT>
- }
状态字段值可以是SUCCESS或ERROR。data字段将包含返回数据的 JSON 表示,例如User或Users集合。
当没有返回数据,或者状态为ERROR时,我们将填充消息字段以传达错误或缺少返回数据的原因。
让我们用一个 Java 类来表示上面的 JSON 结构:
- public class StandardResponse {
-
- private StatusResponse status;
- private String message;
- private JsonElement data;
-
- public StandardResponse(StatusResponse status) {
- // ...
- }
- public StandardResponse(StatusResponse status, String message) {
- // ...
- }
- public StandardResponse(StatusResponse status, JsonElement data) {
- // ...
- }
-
- // getters and setters
- }
其中StatusResponse是一个枚举,定义如下:
- public enum StatusResponse {
- SUCCESS ("Success"),
- ERROR ("Error");
-
- private String status;
- // constructors, getters
- }
现在让我们为我们的 REST API 实现路由和处理程序。
以下 Java 类包含我们 API 的路由,包括动词和路径以及每个路由的处理程序大纲:
- public class SparkRestExample {
- public static void main(String[] args) {
- post("/users", (request, response) -> {
- //...
- });
- get("/users", (request, response) -> {
- //...
- });
- get("/users/:id", (request, response) -> {
- //...
- });
- put("/users/:id", (request, response) -> {
- //...
- });
- delete("/users/:id", (request, response) -> {
- //...
- });
- options("/users/:id", (request, response) -> {
- //...
- });
- }
- }
我们将在以下小节中展示每个路由处理程序的完整实现。
下面是post方法响应处理程序,它将添加一个User:
- post("/users", (request, response) -> {
- response.type("application/json");
- User user = new Gson().fromJson(request.body(), User.class);
- userService.addUser(user);
-
- return new Gson()
- .toJson(new StandardResponse(StatusResponse.SUCCESS));
- });
注意:在此示例中,用户对象的 JSON 表示作为 POST 请求的原始正文传递。
让我们测试一下路线:
要求:
- POST http://localhost:4567/users
- {
- "id": "1012",
- "email": "your-email@your-domain.com",
- "firstName": "Mac",
- "lastName": "Mason1"
- }
回复:
- {
- "status":"SUCCESS"
- }
下面是从UserService返回所有用户的get方法响应处理程序:
- get("/users", (request, response) -> {
- response.type("application/json");
- return new Gson().toJson(
- new StandardResponse(StatusResponse.SUCCESS,new Gson()
- .toJsonTree(userService.getUsers())));
- });
现在让我们测试一下路线:
要求:
GET http://localhost:4567/users
回复:
- {
- "status":"SUCCESS",
- "data":[
- {
- "id":"1014",
- "firstName":"John",
- "lastName":"Miller",
- "email":"your-email@your-domain.com"
- },
- {
- "id":"1012",
- "firstName":"Mac",
- "lastName":"Mason1",
- "email":"your-email@your-domain.com"
- }
- ]
- }
下面是get方法响应处理程序,它返回具有给定id的User:
- get("/users/:id", (request, response) -> {
- response.type("application/json");
- return new Gson().toJson(
- new StandardResponse(StatusResponse.SUCCESS,new Gson()
- .toJsonTree(userService.getUser(request.params(":id")))));
- });
现在让我们测试一下路线:
要求:
GET http://localhost:4567/users/1012
回复:
- {
- "status":"SUCCESS",
- "data":{
- "id":"1012",
- "firstName":"Mac",
- "lastName":"Mason1",
- "email":"your-email@your-domain.com"
- }
- }
下面是put方法响应处理程序,它编辑具有路由模式中提供的id的用户:
- put("/users/:id", (request, response) -> {
- response.type("application/json");
- User toEdit = new Gson().fromJson(request.body(), User.class);
- User editedUser = userService.editUser(toEdit);
-
- if (editedUser != null) {
- return new Gson().toJson(
- new StandardResponse(StatusResponse.SUCCESS,new Gson()
- .toJsonTree(editedUser)));
- } else {
- return new Gson().toJson(
- new StandardResponse(StatusResponse.ERROR,new Gson()
- .toJson("User not found or error in edit")));
- }
- });
注意:在此示例中,数据作为 JSON 对象在 POST 请求的原始正文中传递,其属性名称与要编辑的用户对象的字段匹配。
让我们测试一下路线:
要求:
- PUT http://localhost:4567/users/1012
- {
- "lastName": "Mason"
- }
回复:
- {
- "status":"SUCCESS",
- "data":{
- "id":"1012",
- "firstName":"Mac",
- "lastName":"Mason",
- "email":"your-email@your-domain.com"
- }
- }
下面是删除方法响应处理程序,它将删除具有给定id的用户:
- delete("/users/:id", (request, response) -> {
- response.type("application/json");
- userService.deleteUser(request.params(":id"));
- return new Gson().toJson(
- new StandardResponse(StatusResponse.SUCCESS, "user deleted"));
- });
现在,让我们测试一下路线:
要求:
DELETE http://localhost:4567/users/1012
回复:
- {
- "status":"SUCCESS",
- "message":"user deleted"
- }
options方法是条件检查的不错选择。下面是options方法响应处理程序,它将检查具有给定id的用户是否存在:
- options("/users/:id", (request, response) -> {
- response.type("application/json");
- return new Gson().toJson(
- new StandardResponse(StatusResponse.SUCCESS,
- (userService.userExist(
- request.params(":id"))) ? "User exists" : "User does not exists" ));
- });
现在让我们测试一下路线:
要求:
OPTIONS http://localhost:4567/users/1012
回复:
- {
- "status":"SUCCESS",
- "message":"User exists"
- }
在本文中,我们快速介绍了用于快速 Web 开发的 Spark 框架。
该框架主要用于在 Java 中生成微服务。具有 Java 知识且想要利用基于 JVM 库构建的库的Node.js开发人员应该对使用此框架感到宾至如归。
和往常一样,您可以在Github 项目中找到本教程的所有资源。