Kiến Trúc Core Container Và Cơ Chế IOC
Spring Framework hoạt động dựa trên nền tảng container, nơi việc khởi tạo và quản lý vòng đời đối tượng được giao phó cho container thay vì lập trình viên. Khái niệm IOC (Inversion of Control) thể hiện qua việc chuyển giao quyền kiểm soát này sang BeanFactory. Spring quét các package để đóng gói thông tin bean vào BeanDefinition, lưu trữ trong map nội bộ. Khi cần, container sử dụng reflection để khởi tạo đối tượng từ định nghĩa này và đưa vào IOC container.
DI (Dependency Injection) là cơ chế đi kèm, cho phép container tự động bơm các phụ thuộc cần thiết vào đối tượng. Lưu ý rằng bean đơn lẻ (singleton) trong Spring không mặc định an toàn về mặt thread nếu nó chứa trạng thái thay đổi (mutable state). Nếu bean có thuộc tính thành viên không phải final và bị sửa đổi trong phương thức, nhiều luồng concurrent truy cập sẽ gây ra xung đột. Giải pháp là chuyển scope sang prototype hoặc sử dụng cơ chế đồng hóa, tuy nhiên best practice là thiết kế các Service và DAO không lưu trạng thái.
Lập Trình Hướng Khía Cạnh (AOP) Và Quản Lý Giao Dịch
AOP giúp tách biệt các logic ngang (cross-cutting concerns) như logging, transaction, security khỏi logic nghiệp vụ chính. Cơ chế này dựa trên dynamic proxy: JDK Proxy áp dụng cho đối tượng có interface, trong khi CGLIB tạo subclass để proxy đối tượng không có interface. Các thành phần chính bao gồm Aspect, Pointcut, Advice và JoinPoint.
Trong thực tế, AOP thường được dùng để xây dựng annotation xác thực tùy chỉnh. Ví dụ, một aspect sẽ chặn các method trong controller có gắn annotation xác thực, lấy token từ header, validate với Redis và quyết định cho phép truy cập hay không. Ngoài ra, AOP cũng hữu ích để ghi log hoạt động hệ thống bằng cách bọc quanh (around advice) các method target, thu thập thông tin như tên class, method, tham số đầu vào và lưu vào database.
Cơ Chế Transaction Và Các Trường Hợp失效
Spring hỗ trợ giao dịch lập trình (Programmatic) thông qua TransactionTemplate và giao dịch khai báo (Declarative) qua annotation @Transactional. Giao dịch khai báo được thực hiện dựa trên AOP proxy. Tuy nhiên, có nhiều trường hợp annotation này không hoạt động:
- Ngoại bị bắt (catch) trong method mà không ném lại (re-throw), khiến transaction manager không nhận diện được lỗi để rollback.
- Ném ngoại lệ checked exception hoặc Error mà không cấu hình rollbackFor trong annotation.
- Method không có modifier public, vì Spring chỉ proxy public method.
- Tự gọi method trong cùng một class (self-invocation) khi method đó không phải là proxy object.
Vòng Đời Bean Và Vấn Đề Phụ Thuộc Vòng
Vòng đời một bean bắt đầu từ việc Spring đọc cấu hình (XML hoặc annotation) để tạo BeanDefinition. Sau đó, container khởi tạo đối tượng qua constructor, bơm phụ thuộc (DI), gọi các method set nếu implement Aware interface, thực hiện BeanPostProcessor.before, khởi tạo (init-method hoặc @PostConstruct), thực hiện BeanPostProcessor.after và cuối cùng là hủy (destroy hoặc @PreDestroy) khi container đóng.
Về vấn đề phụ thuộc vòng (Circular Dependency), Spring giải quyết trường hợp setter injection nhờ cơ chế 3 cấp cache:
- Cấp 1 (singletonObjects): Chứa bean đã khởi tạo hoàn chỉnh.
- Cấp 2 (earlySingletonObjects): Chứa bean chưa khởi tạo xong (raw object).
- Cấp 3 (singletonFactories): Chứa ObjectFactory để tạo proxy nếu cần AOP.
Khi Bean A cần B và B cần A, A được khởi tạo một phần và đưa vào cache cấp 3. Khi B cần A, nó lấy factory từ cache cấp 3 để tạo đối tượng A (có thể là proxy) đưa vào cache cấp 2 để注入 vào B. Sau khi B hoàn tất, A tiếp tục hoàn tất khởi tạo. Trường hợp circular dependency trong constructor thì không thể giải quyết bằng cache mà cần dùng @Lazy.
Luồng Xử Lý Trong Spring MVC
Spring MVC hoạt động dựa trên mô hình Front Controller với DispatcherServlet làm trung tâm. Luồng xử lý cơ bản như sau:
- DispatcherServlet nhận request từ client.
- Gọi HandlerMapping để tìm handler phù hợp dựa trên URL.
- DispatcherServlet gọi HandlerAdapter để thực thi handler (Controller).
- Controller xử lý và trả về ModelAndView (hoặc dữ liệu JSON nếu dùng @ResponseBody).
- Nếu là view, ViewResolver sẽ phân giải tên view logic thành view thực tế.
- DispatcherServlet render dữ liệu vào view và trả response về client.
Trong kiến trúc hiện đại (前后端分离), Controller thường trả về dữ liệu JSON trực tiếp thông qua HttpMessageConverter thay vì chuyển hướng view.
Persistence Layer Với MyBatis
MyBatis là framework ánh xạ SQL, giúp tách biệt code Java và câu lệnh SQL. Nó hỗ trợ caching ở hai cấp độ:
- Cache cấp 1: Mặc định bật, phạm vi SqlSession. Dữ liệu bị xóa khi session commit hoặc close.
- Cache cấp 2: Mặc định tắt, phạm vi namespace (mapper). Cần cấu hình trong XML và entity phải implement Serializable. Dữ liệu từ cache cấp 1 sẽ được đẩy lên cấp 2 khi session đóng.
Cả hai cấp cache đều bị vô hiệu hóa nếu có thao tác insert, update, delete xảy ra giữa các lần select.
Dynamic SQL Và Best Practices
MyBatis cung cấp các tag để xây dựng SQL động linh hoạt. Dưới đây là ví dụ về việc tìm kiếm tài khoản với điều kiện tùy chọn:
<select id="findAccounts" parameterType="map" resultType="com.example.domain.Account">
SELECT * FROM accounts
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="status != null">
AND status = #{status}
</if>
<if test="idList != null and idList.size() > 0">
AND id IN
<foreach item="id" collection="idList" open="(" separator="," close=")">
#{id}
</foreach>
</if>
</where>
</select>
Để lấy khóa chính tự tăng sau khi insert, cấu hình useGeneratedKeys và keyProperty:
<insert id="createCategory" useGeneratedKeys="true" keyProperty="categoryId">
INSERT INTO categories (category_name) VALUES (#{categoryName})
</insert>
Phân biệt giữa #{} và ${}: #{} sử dụng PreparedStatement để chống SQL injection và tự động thêm dấu ngoặc kép, phù hợp cho giá trị. ${} thay thế chuỗi trực tiếp, chỉ dùng khi cần truyền tên bảng hoặc cột động (ví dụ: ORDER BY).
MyBatis cũng hỗ trợ lazy loading cho các quan hệ 1-1 hoặc 1-n. Cơ chế này sử dụng CGLIB tạo proxy cho đối tượng, khi truy cập thuộc tính liên quan thì mới thực thi câu lệnh SQL thực sự.
Tự Động Hóa Cấu Hình Với Spring Boot
Spring Boot giảm thiểu cấu hình thủ công thông qua cơ chế Auto-configuration. Annotation @SpringBootApplication kết hợp @EnableAutoConfiguration, sử dụng @Import để load các configuration class được định nghĩa trong file META-INF/spring.factories của các thư viện starter. Các bean chỉ được đăng ký vào container nếu thỏa mãn điều kiện nhất định (ví dụ: class đó có tồn tại trong classpath).
Quản Lý Profile Và Cấu Hình
Spring Boot hỗ trợ đa môi trường thông qua các file cấu trúc như application-dev.yml, application-prod.yml. File bootstrap.yml có độ ưu tiên cao hơn và thường dùng để load cấu hình từ remote config server trước khi application context khởi tạo. Thứ tự ưu tiên load cấu hình: Command line args > External config > Classpath config.
Các Annotation Phổ Biến
- Core: @Component, @Service, @Repository, @Controller, @Configuration, @Bean.
- DI: @Autowired, @Qualifier, @Resource.
- MVC: @RequestMapping, @GetMapping, @PostMapping, @RequestBody, @PathVariable, @RequestParam.
- AOP: @Aspect, @Before, @After, @Around, @Pointcut.
- Boot: @SpringBootConfiguration, @EnableAutoConfiguration, @ComponentScan.
Để tích hợp kiểm thử, Spring Boot cung cấp @SpringBootTest để load toàn bộ context, cho phép inject các bean và chạy test case như trong môi trường thực tế.