Spring Framework 5.0 đánh dấu một bước tiến quan trọng, được xây dựng dựa trên Java 8 và tương thích hoàn toàn với JDK 9 (và các phiên bản Java mới hơn). Phiên bản này đã loại bỏ nhiều lớp và phương thức đã lỗi thời khỏi thư viện lõi, mang đến những cải tiến đáng kể về hiệu suất, khả năng tương thích với Java hiện đại và trải nghiệm của nhà phát triển.
1. Hỗ trợ Log4j2
Spring 5 đã gỡ bỏ Log4jConfigListener và khuyến nghị mạnh mẽ việc sử dụng Log4j2 làm hệ thống ghi nhật ký chính thức. Framework này cung cấp một cơ chế đóng gói nhật ký chung, giúp việc tích hợp Log4j2 vào ứng dụng trở nên dễ dàng và hiệu quả hơn. Để tích hợp Log4j2, bạn cần tạo một tệp cấu hình có tên log4j2.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Cấu hình Log4j2 -->
<!-- Mức độ ưu tiên của log (từ cao đến thấp): OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!-- Thuộc tính 'status' của <configuration> điều khiển việc xuất thông tin nội bộ của Log4j2.
Đặt là 'INFO' để xem các thông báo quan trọng từ Log4j2. -->
<configuration status="INFO">
<appenders>
<!-- Định nghĩa appender để xuất log ra console -->
<Console name="ConsoleOutput" target="SYSTEM_OUT">
<!-- Định dạng của thông điệp log -->
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</appenders>
<loggers>
<!-- Cấu hình logger gốc (root logger) -->
<!-- Mọi log không được định nghĩa cụ thể sẽ sử dụng cấu hình này -->
<root level="info">
<appender-ref ref="ConsoleOutput"/>
</root>
</loggers>
</configuration>
2. Chú thích @Nullable trong container lõi
Spring Framework 5 giới thiệu chú thích @Nullable từ Spring Framework trong container lõi. Chú thích này có thể được áp dụng cho tham số phương thức, kiểu trả về của phương thức hoặc trường dữ liệu để chỉ rõ rằng giá trị tương ứng có thể là null. Điều này giúp tăng cường khả năng đọc mã, hỗ trợ các công cụ phân tích tĩnh trong việc phát hiện lỗi liên quan đến null và làm rõ các hợp đồng API.
- Ví dụ sử dụng cho kiểu trả về của phương thức:
public class Document { @Nullable public String getShortDescription() { // Phương thức có thể trả về null nếu tài liệu không có mô tả ngắn return null; } } - Ví dụ sử dụng cho tham số phương thức:
public class DataProcessor { public void processDataItem(@Nullable String dataIdentifier) { if (dataIdentifier != null) { System.out.println("Processing data with identifier: " + dataIdentifier); } else { System.out.println("No specific identifier provided, processing default data."); } } } - Ví dụ sử dụng cho trường dữ liệu:
public class ApplicationConfig { @Nullable private String optionalFeatureToggle; // Trường này có thể không được thiết lập giá trị // ... }
3. GenericApplicationContext theo phong cách hàm (Functional Style)
Spring 5 cho phép định nghĩa và đăng ký các bean theo phong cách lập trình hàm sử dụng GenericApplicationContext. Cách tiếp cận này giúp giảm thiểu việc sử dụng XML hoặc các chú thích @Configuration truyền thống cho các trường hợp đơn giản hoặc khi cần linh hoạt hơn trong việc tạo bean. Nó cung cấp một cách nhẹ nhàng và minh bạch để quản lý vòng đời của bean.
import org.junit.jupiter.api.Test;
import org.springframework.context.support.GenericApplicationContext;
// Định nghĩa một lớp POJO đơn giản để sử dụng làm bean
class Product {
private String name = "Default Product";
public Product() {} // Constructor mặc định
public Product(String name) {
this.name = name;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
@Override
public String toString() {
return "Product{name='" + name + "'}";
}
}
public class FunctionalBeanRegistrationTest {
@Test
public void testProductBeanRegistration() {
// 1. Khởi tạo GenericApplicationContext
GenericApplicationContext appContext = new GenericApplicationContext();
// 2. Đăng ký một bean Product theo phong cách hàm
// Sử dụng lambda expression để định nghĩa cách tạo instance của bean
appContext.registerBean("myCustomProduct", Product.class, () -> {
Product newProduct = new Product("Custom Widget");
// Có thể thực hiện thêm các bước khởi tạo hoặc cấu hình khác tại đây
return newProduct;
});
// Quan trọng: Làm mới ngữ cảnh để các bean được khởi tạo và dependency injection diễn ra
appContext.refresh();
// 3. Lấy bean đã đăng ký từ ngữ cảnh Spring
Product retrievedProduct = appContext.getBean("myCustomProduct", Product.class);
System.out.println("Sản phẩm đã lấy từ Spring Context: " + retrievedProduct);
// Kiểm tra xem bean có được tạo đúng cách không
assert retrievedProduct.getName().equals("Custom Widget");
}
}
4. Tích hợp JUnit 5
Spring 5 cung cấp hỗ trợ tuyệt vời cho việc tích hợp với JUnit 5 (hay còn gọi là Jupiter), thay thế JUnit 4 trong các dự án mới. Việc này mang lại nhiều cải tiến trong việc viết và chạy các bài kiểm thử.
Để minh họa, chúng ta sẽ sử dụng một dịch vụ đơn giản và một tệp cấu hình Spring XML:
// Giả định đây là một dịch vụ xử lý đơn hàng
class OrderProcessingService {
public void processOrder() {
System.out.println("Đang xử lý đơn hàng...");
// Logic xử lý đơn hàng thực tế
}
}
<!-- Giả định bean-config.xml chứa định nghĩa của OrderProcessingService -->
<!-- Đảm bảo bạn có tag <beans ...> bọc ngoài -->
<bean id="orderService" class="your.package.OrderProcessingService"/>
4.1. Tích hợp JUnit 4 (Để so sánh)
Để tích hợp với JUnit 4, bạn cần thêm thư viện spring-test và junit. Các bài kiểm thử sẽ sử dụng chú thích @RunWith và @ContextConfiguration.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class) // Chỉ định runner của Spring cho JUnit 4
@ContextConfiguration("classpath:bean-config.xml") // Tải cấu hình Spring từ classpath
public class OldOrderServiceTest {
@Autowired
private OrderProcessingService orderService; // Inject dịch vụ
@Test
public void testProcessSimpleOrder() {
orderService.processOrder();
System.out.println("Đơn hàng đã được xử lý bằng JUnit 4.");
}
}
4.2. Tích hợp JUnit 5 với @ExtendWith
Với JUnit 5, bạn cần thêm thư viện spring-test và junit-jupiter-api, junit-jupiter-engine. Chú thích @ExtendWith(SpringExtension.class) được sử dụng thay thế cho @RunWith.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension; // JUnit 5 extension
@ExtendWith(SpringExtension.class) // Mở rộng JUnit 5 với khả năng của Spring
@ContextConfiguration("classpath:bean-config.xml") // Tải cấu hình Spring
public class NewOrderServiceTest {
@Autowired
private OrderProcessingService orderService;
@Test
public void testProcessDetailedOrder() {
orderService.processOrder();
System.out.println("Đơn hàng đã được xử lý bằng JUnit 5.");
}
}
4.3. Sử dụng chú thích tổng hợp @SpringJUnitConfig
Để đơn giản hóa việc cấu hình kiểm thử, Spring 5 cung cấp chú thích tổng hợp @SpringJUnitConfig. Chú thích này thay thế cả @ExtendWith và @ContextConfiguration, giúp mã nguồn kiểm thử gọn gàng hơn.
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; // Chú thích tổng hợp
@SpringJUnitConfig(locations = "classpath:bean-config.xml") // Thay thế 2 chú thích trên
public class SimplifiedOrderServiceTest {
@Autowired
private OrderProcessingService orderService;
@Test
public void testProcessAdvancedOrder() {
orderService.processOrder();
System.out.println("Đơn hàng đã được xử lý bằng cấu hình JUnit 5 đơn giản hóa.");
}
}