Javalin 与 Java 10 和 Google Guice
Javalin 与 Java 10 和 Google Guice
您将学到什么
在本教程中,我们将学习如何在 Javalin 之上创建模块化应用程序。
我们将使用Google Guice实现模块化,并使用Java 10来完成 Java 10 的事情:
var amazingFramework = "Javalin"; // java10
// vs
String amazingFramework = "Javalin"; // not java10
依赖项
让我们创建一个包含依赖项的 Maven 项目(→ 教程)。我们将使用 Javalin 作为 Web 服务器,使用 slf4j 进行日志记录,使用 jackson 将响应渲染为 JSON,并使用 Guice 进行依赖注入:
<dependencies>
<dependency>
<groupId>io.javalin</groupId>
<artifactId>javalin</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.17</version>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-multibindings</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
</dependencies>
并添加 Java 10 的属性
<properties>
<maven.compiler.source>10</maven.compiler.source>
<maven.compiler.target>10</maven.compiler.target>
</properties>
高级架构
控制器
负责处理请求。如果你愿意,可以充当保镖或人脸识别,仅此而已
服务
实际的业务逻辑执行器,可能需要也可能不需要其他服务
存储库
与任何数据存储进行通信,仅此而已
Java 应用程序
首先,让我们在 io.kidbank.user 包中创建一个控制器。
UserController 负责处理请求,而业务逻辑由提供 UserService。
package io.kidbank.user;
import io.javalin.Context;
import io.kidbank.user.services.UserService;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
class UserController {
private UserService userService;
@Inject
public UserController(UserService userService) {
this.userService = userService;
}
public void index(Context ctx) {
ctx.json(userService.getAllUsersUppercase());
}
}
现在我们有了控制器,我们应该将端点绑定到UserController。该类Routing帮助我们从 Google Guice 解析UserController。它保证存在一个方法 bindRoutes(),我们稍后会用到它。
package io.kidbank.user;
import io.alzuma.Routing;
import io.javalin.Javalin;
import javax.inject.Inject;
import javax.inject.Singleton;
import static io.javalin.apibuilder.ApiBuilder.get;
import static io.javalin.apibuilder.ApiBuilder.path;
@Singleton
class UserRouting extends Routing<UserController> {
private Javalin javalin;
@Inject
public UserRouting(Javalin javalin) {
this.javalin = javalin;
}
@Override
public void bindRoutes() {
javalin.routes(() -> {
path("api/kidbank/users", () -> {
get(ctx -> getController().index(ctx));
});
});
}
}
安装并绑定io.kidbank.user包的所有依赖项。
看一下 Multibinder,它是一个 Google Guice 扩展。这就是我们在应用程序中启用多个路由的方法。要添加更多路由,只需添加Multibinder.newSetBinder(...)
。
稍后我们将注入所有路由,以便将它们绑定到JavalinWeb 服务器中。
package io.kidbank.user;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;
import io.alzuma.Routing;
import io.kidbank.user.repositories.UserRepositoryModule;
import io.kidbank.user.services.UserServiceModule;
public class UserModule extends AbstractModule {
@Override
protected void configure() {
bind(UserController.class);
install(new UserServiceModule());
install(new UserRepositoryModule());
Multibinder.newSetBinder(binder(), Routing.class).addBinding().to(UserRouting.class);
}
}
绑定Javalin路由并启动 Web 服务器。这不是什么黑魔法,只是注入而已,记住这一点。
仔细看看private Set<Routing> routes
。这是 Google Guice 注入所有Routes受 约束的内容的地方Multibinder。
记住,我们讨论的是Routing类及其提供的保证。基于此,我们可以bindRoutes() 在 中的每条记录上调用该方法Set<Routing>
。然后,我们就Javalin用路由填充了。
package io.kidbank;
import com.google.inject.Inject;
import io.alzuma.AppEntrypoint;
import io.alzuma.Routing;
import io.javalin.Javalin;
import javax.inject.Singleton;
import java.util.Collections;
import java.util.Set;
@Singleton
class WebEntrypoint implements AppEntrypoint {
private Javalin app;
@Inject(optional = true)
private Set<Routing> routes = Collections.emptySet();
@Inject
public WebEntrypoint(Javalin app) {
this.app = app;
}
@Override
public void boot(String[] args) {
bindRoutes();
app.port(7000);
app.start();
}
private void bindRoutes() {
routes.forEach(r -> r.bindRoutes());
}
}
创建WebModule我们的Kid bank项目。在模块内部,我们定义项目“以 Web 服务器身份运行”。
现在我们将使用MapBinder。它类似于我们使用,但我们使用Routing而不是,这样我们就可以将多个“运行方式”存储
到MultibinderMapBinderHashMap<EntrypointType, AppEntrypoint>
package io.kidbank;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.MapBinder;
import io.alzuma.AppEntrypoint;
import io.alzuma.EntrypointType;
import io.javalin.Javalin;
import org.jetbrains.annotations.NotNull;
class WebModule extends AbstractModule {
private Javalin app;
private WebModule(Javalin app) {
this.app = app;
}
@NotNull
public static WebModule create() {
return new WebModule(Javalin.create());
}
@Override
protected void configure() {
bind(Javalin.class).toInstance(app);
MapBinder.newMapBinder(binder(), EntrypointType.class, AppEntrypoint.class).addBinding(EntrypointType.REST).to(WebEntrypoint.class);
}
}
我们需要某种解析器来决定执行哪个“Run as”。为此,我们创建了一个类Startup并注入了所有可能的入口点。
import com.google.inject.Inject;
import io.alzuma.AppEntrypoint;
import io.alzuma.EntrypointType;
import javax.inject.Singleton;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
@Singleton
public class Startup {
@Inject(optional = true)
private Map<EntrypointType, AppEntrypoint> entrypoints = Collections.emptyMap();
public void boot(EntrypointType entrypointType, String[] args) {
var entryPoint = Optional.ofNullable(entrypoints.get(entrypointType));
entryPoint.orElseThrow(() -> new RuntimeException("Entrypoint not defined")).boot(args);
}
}
至于我们的最后一个模块,我们定义AppModule。我们在哪里安装我们的项目模块。
import com.google.inject.AbstractModule;
import io.kidbank.KidBankModule;
public class AppModule extends AbstractModule {
protected void configure() {
bind(Startup.class);
install(new KidBankModule());
}
}
现在我们准备启动我们的网络服务器。
创建注入器,用于AppModule触发路径下的所有绑定和安装。Startup使用 Javalin 解析并启动 REST。
public class App {
public static void main(String[] args) {
var injector = Guice.createInjector(new AppModule());
injector.getInstance(Startup.class).boot(EntrypointType.REST, args);
}
}
在浏览器中打开 http://localhost:7000/api/kidbank/users 并等待响应[“BOB”,”KATE”,”JOHN”]
结论
我们创建了模块化应用程序,它不仅能够作为 Web 服务器自行运行。
习惯 Guice 模块需要时间,但是一旦你习惯了,天空就是你的极限!
最重要的部分。玩得开心!
最后编辑:Jeebiz 更新时间:2025-05-04 00:55