什么是 ActiveJ ?

 

1. 简介

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

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

2. 注入

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

2.1. 依赖

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

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

2.2. 使用模块进行依赖注入

让我们创建一个仓库 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 类。此外,我们提供了 PersonServicePersonRepository 和 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实例。可以看到,所有依赖都已被注入。

3. 异步 I/O

**ActiveJ Async I/O 提供了高效编写异步流程的组件。**我们将通过 Promises 和 Event Loop 等关键元素来理解这一特性。

3.1. 依赖

让我们添加activej-promise依赖:

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

3.2. Promise

让我们创建要用到的模型:

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()包装。

接下来,添加验证方法:

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 合并。然后对模型进行验证,返回最终结果的`Promise_。

3.3. 事件循环

**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()创建事件循环实例,并用run()方法启动。接着调用findAndVerifyPerson()方法。如果漏掉run()_,此时会遇到如下异常:

IllegalStateException: No reactor in current thread

4. HTTP 服务器

ActiveJ 的另一个强大特性是高性能异步 HTTP 服务器。

4.1. 依赖

首先添加 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>

4.2. 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()方法。在方法内部,获取PersonServicefindAndVerifyPerson()结果。然后将其转为 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();
}

我们调用了接口并异步获取了预期结果。为了在测试后停止服务器,调用了EventloopbreakEventloop()方法终止执行。

5. 总结

本文介绍了 ActiveJ 框架的关键特性。借助这些特性,我们已经可以构建高效轻量的 Web 应用。当然,该框架还远不止于此。

我们还可以用它进行数据处理、分布式系统等多种场景。其模块化特性帮助我们避免项目臃肿,只引入必要组件。

 

请登录后发表评论

    没有回复内容