ActiveJ 简介

在本文中,我们了解了 ActiveJ 框架的主要功能。有了它们,我们已经可以构建高效、轻量级的 Web 应用程序。但是,这个框架还提供了更多功能。我们可以将它用于数据处理、分布式系统和许多其他情况。它的模块化特性帮助我们避免项目过载,只包含必要的组件。

ActiveJ 是一个用于高性能应用程序的轻量级 Java 框架。我们可以使用它来创建具有快速启动和较小内存占用的简约模块化应用程序。它提供异步 I/O、依赖注入、高效序列化和反应式编程支持等功能。

在本教程中,我们将讨论 ActiveJ 的主要功能,包括其 Inspect 模块、强大的事件循环和高级网络功能。


我们将从ActiveJ Inject开始。它是一个轻量级且性能优化的依赖注入库,我们可以使用它来设置 bean 之间的依赖关系。让我们看看如何使用它。

依赖项
让我们将 Active Inject依赖项添加到我们的项目中:

<dependency>
    <groupId>io.activej</groupId>
    <artifactId>activej-inject</artifactId>
    <version>6.0-rc2</version>
</dependency>

使用模块进行依赖注入
让我们创建一个存储库 bean:

public class PersonRepository {
    private final DataSource dataSource;
    public PersonRepository(DataSource dataSource) {
          this.dataSource = dataSource;
    }
}

在我们的PersonRepository中,我们刚刚指定了对DataSource实例的依赖。现在让我们创建一个服务类:

public class PersonService {
    private final PersonRepository personRepository;
    public PersonService(PersonRepository personRepository) {
        this.personRepository = personRepository;
    }
}

在PersonService类中,我们指定了对PersonRepository 的依赖。

接下来,让我们创建一个PersonModule类,在其中配置我们 bean 之间的所有关系:

public class PersonModule extends AbstractModule {
    @Provides
    PersonService personService(PersonRepository personRepository) {
        return new PersonService(personRepository);
    }
    @Provides
    PersonRepository personRepository(DataSource dataSource) {
        return new PersonRepository(dataSource);
    }
    @Provides
    DataSource dataSource() {
        return new DataSource() {
            //DataSource methods
        };
    }
}

我们扩展了AbstractModule类。此外,我们还提供了PersonService、PersonRepository和DataSource之间的关联bean。

让我们测试依赖注入行为:

public class ActiveJTest {
    @Test
    void givenPersonModule_whenGetTheServiceBean_thenAllTheDependenciesShouldBePresent() {
        PersonModule personModule = new PersonModule();
        PersonService personService = Injector.of(personModule).getInstance(PersonService.class);
        assertNotNull(personService);
        PersonRepository personRepository = personService.getPersonRepository();
        assertNotNull(personRepository);
        DataSource dataSource = personRepository.getDataSource();
        assertNotNull(dataSource);
    }
}

我们创建了PersonModule类的一个实例。然后,使用Injector获取了PersonService实例。我们可以看到,所有依赖项都已填充。

异步 I/O
ActiveJ Async I/O提供了用于高效编写异步流程的组件。我们将研究Promises和Event Loop等关键元素,以更好地理解此功能。

依赖项
让我们添加activej-promise 依赖项:

<dependency>
    <groupId>io.activej</groupId>
    <artifactId>activej-promise</artifactId>
    <version>6.0-rc2</version>
</dependency>

 承诺
让我们创建我们将使用的模型:

public record Person(String name, String description) {
}

现在,让我们向PersonRepository类添加一些逻辑:

public Promise<Person> findPerson(String name) {
    return Promises
      .delay(Duration.ofMillis(100), new Person(name, name + " description"));
}

我们添加了findPerson()方法,该方法模拟按名称搜索的过程。我们使用Promises.delay()来延迟生成Person类的实例。结果将用Promise实例包装。

接下来,让我们创建另一个将在服务层生成的模型:

public record VerifiedPerson(String name, String description, String notes, String result) {
}

在VerifiedPerson中,我们有一个人的信息和一个验证结果。

我们在PersonService类中添加服务业务逻辑:

private Promise<String> findPersonNotes(String name) {
    return Promise.of(name + " notes");
}

在findPersonNotes()方法中,我们模拟了获取人员备注的过程。结果也使用Promise.of()方法通过Promise进行包装。

接下来我们添加验证方法:

private VerifiedPerson verify(VerifiedPerson person) {
    if(person.description().startsWith("Good")) {
        return new VerifiedPerson(person.name(), person.description(), person.notes(),
"SUCCESS");
    }
    return new VerifiedPerson(person.name(), person.description(), person.notes(),
"FAIL");
}

这里我们模拟了验证过程,并根据人员的描述添加了验证结果。

现在我们完成流程并结合所有操作:

public Promise<VerifiedPerson> findAndVerifyPerson(String name) {
    return personRepository.findPerson(name)
      .combine(findPersonNotes(name),
        (person, notes) -> new VerifiedPerson(person.name(), person.description(), notes, null))
      .map(person -> verify(person));
}

在我们的最终方法中,我们使用Promise类的Combine()方法将来自存储库findPerson()和服务findPersonNotes()方法的承诺结合起来。然后我们验证准备好的模型并返回带有最终结果的Promise 。

事件循环
ActiveJ Eventloop在事件循环和线程内异步执行代码。让我们看看如何使用它来运行基于Promise的流程:

@Test
void givenEventloop_whenCallFindAndVerifyPerson_thenExpectedVerificationResultShouldBePresent() {
    PersonModule personModule = new PersonModule();
    PersonService personService = Injector.of(personModule).getInstance(PersonService.class);
    Eventloop eventloop = Eventloop.create();
    eventloop.run();
    personService.findAndVerifyPerson("Good person")
      .whenResult(verifiedPerson -> assertEquals(
"SUCCESS", verifiedPerson.result()));
}

我们从PersonModule中获取了PersonService 。然后我们使用Eventloop.create()方法创建Eventloop实例,并使用run()方法启动它。接下来,我们调用findAndVerifyPerson()方法。如果我们错过了run()方法,此时我们会面临异常:

IllegalStateException: No reactor in current thread

HTTP 服务器
我们将要看到的 ActiveJ 的另一个出色的特性是高性能异步HTTP 服务器

依赖项
让我们首先添加 ActiveJ HTTP依赖项:

<dependency>
    <groupId>io.activej</groupId>
    <artifactId>activej-http</artifactId>
    <version>6.0-rc2</version>
</dependency>

接下来,让我们添加 Jackson Databind 依赖项:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.17.0</version>
</dependency>

REST 端点示例
现在,让我们通过 HTTP 端点公开VerifiedPerson流程。我们从PersonController类开始:

public class PersonController implements AsyncServlet {
    private final PersonService personService;
    private final ObjectMapper objectMapper;
    public PersonController(PersonService personService) {
        this.personService = personService;
        this.objectMapper = new ObjectMapper();
    }
    @Override
    public Promise<HttpResponse> serve(HttpRequest httpRequest) {
        return personService.findAndVerifyPerson(httpRequest.getQueryParameter("name"))
          .map((p) -> HttpResponse.ok200().withJson(objectMapper.writeValueAsString(p)).build())
          .mapException(e -> e);
    }
}

这里我们实现了AsyncServlet接口并重写了serve()方法。在这个方法中,我们获取PersonService findAndVerifyPerson()方法的结果。然后我们将其映射到 JSON 中,并将其作为 HTTP 响应主体返回,响应代码为 200。例外情况单独映射,默认情况下返回 500 状态代码。

我们将这个控制器添加到注入上下文中:

public class PersonModule extends AbstractModule {
    @Provides
    PersonController personController(PersonService personService) {
        return new PersonController(personService);
    }
    //Other beans
}

我们已将PersonController添加到PersonModule中并配置了其依赖项。

最后介绍一下我们的HTTP服务器代码:

public class ActiveJIntegrationTest {
    private static final ObjectMapper objectMapper = new ObjectMapper();
    private static HttpServer server;
    private static HttpClient client;
    private static int port;
    private static Eventloop eventloop;
    @BeforeAll
    static void setUp() throws Exception {
        eventloop = Eventloop.create();
        PersonModule personModule = new PersonModule();
        PersonController personController = Injector.of(personModule).getInstance(PersonController.class);
        RoutingServlet servlet = RoutingServlet.builder(eventloop)
          .with(HttpMethod.GET,"/person", personController)
          .build();
        server = HttpServer.builder(eventloop, servlet)
          .withListenPort(8080)
          .build();
        server.listen();
        port = server.getListenAddresses().get(0).getPort();
        InetAddress dnsServerAddress = InetAddress.getByName(
"8.8.8.8");
        DnsClient dnsClient = DnsClient.builder(eventloop, dnsServerAddress).build();
        client = HttpClient.builder(eventloop, dnsClient).build();
    }
    @AfterAll
    static void tearDown() {
        if (server != null) {
            server.close();
        }
    }
}

我们已经设置了HttpServer实例。然后我们使用RoutingServlet添加了 HTTP 映射。

最后,我们使用listen()方法启动了服务器并准备了一个异步 HTTP 客户端。

现在让我们调用我们的端点并检查结果:

@Test
void givenHttpServer_whenCallPersonEndpoint_thenExpectedVerificationResultShouldPresentInResponse() {
    HttpRequest request = HttpRequest.get("http://localhost:" + port + "/person?name=my-name").build();
    client.request(request)
      .whenResult(response -> {
        assertEquals(response.getCode(), 200);
        response.loadBody()
          .whenResult(body -> {
              try {
                  VerifiedPerson responseData = objectMapper.readValue(body.getArray(),
                    VerifiedPerson.class);
                  assertEquals(responseData.result(),
"FAIL");
                  eventloop.breakEventloop();
              } catch (Exception e) {
                throw new RuntimeException(e);
              }
          });
      });
    eventloop.run();
}

我们调用了我们的端点并异步获得了预期的结果。由于我们想在测试后停止服务器,因此我们调用了Eventloop的breakEventloop()方法来停止执行。