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](https://github.com/activej/activej/blob/007785780e15d3f4938c14583a81185202566c44/core-inject/src/main/java/io/activej/inject/module/AbstractModule.java)類別。此外,我們也提供了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實例。我們可以看到,所有依賴項都已填入。
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.承諾
讓我們創建我們將使用的模型:
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 。
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()方法建立了Eventloop的一個實例,並使用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()方法。在這個方法內部,我們得到了PersonService findAndVerifyPerson()方法的結果。然後我們將其映射到 JSON 並將其作為帶有 200 回應代碼的 HTTP 回應正文傳回。異常情況單獨映射,預設回傳 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()方法來停止執行。
5. 結論
在本文中,我們了解了 ActiveJ 框架的主要特性。有了它們,我們已經可以建立高效、輕量級的網路應用程式。然而,這個框架提供的遠不止這些。我們可以將其用於資料處理、分散式系統和許多其他情況。它的模組化特性幫助我們避免專案超載,只包含必要的元件。
與往常一樣,程式碼可在 GitHub 上取得。