Giới thiệu về @ControllerAdvice
@ControllerAdvice là một annotation trong Spring Framework dùng để định nghĩa các thành phần xử lý ngoại lệ và ràng buộc dữ liệu ở phạm vi toàn ứng dụng. Annotation này giúp tập trung hóa việc xử lý lỗi và cung cấp cơ chế phản hồi thống nhất khi có exception xảy ra.
Cách sử dụng cơ bản của @ControllerAdvice
package org.springframework.web.bind.annotation;
import java.lang.annotation.*;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] assignableTypes() default {};
Class<? extends Annotation>[] annotations() default {};
}
Một số điểm cần lưu ý:
- @ControllerAdvice mang tính chất đặc biệt vì nó được đánh dấu bởi @Component, do đó sẽ được Spring container nhận diện và khởi tạo như một bean.
- Khi Spring phát hiện class có gắn @ControllerAdvice, nó sẽ tự động đăng ký vào ngữ cảnh ứng dụng.
- Có thể kết hợp với các annotation khác để mở rộng chức năng:
- @ExceptionHandler: Xác định phương thức xử lý ngoại lệ cụ thể. Khi controller ném ra exception, Spring sẽ tìm đến handler phù hợp để xử lý.
- @InitBinder: Định nghĩa phương thức tùy chỉnh quá trình binding dữ liệu từ request vào đối tượng.
- @ModelAttribute: Cung cấp dữ liệu chung cho tất cả các yêu cầu trước khi thực thi method xử lý chính.
Ví dụ minh họa
1. Sử dụng @ExceptionHandler
Xử lý ngoại lệ tùy chỉnh:
- Tạo một lớp ngoại lệ riêng:
public class BusinessException extends RuntimeException {}
- Xây dựng bộ xử lý ngoại lệ:
package com.example.app.handler;
import com.example.app.exception.BusinessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@ControllerAdvice(basePackages = "com.example.app.controller")
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
@ResponseBody
public ResponseEntity handleBusinessError(BusinessException ex){
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.getMessage());
}
}
- Ném ngoại lệ trong controller:
@RequestMapping("/api/test")
public String execute() throws BusinessException {
throw new BusinessException("Lỗi nghiệp vụ!");
}
Khi chương trình gặp lỗi, hệ thống sẽ chuyển hướng sang phương thức xử lý đã định nghĩa và trả kết quả về phía client.
2. Sử dụng @ModelAttribute
Gán tham số toàn cục:
- Định nghĩa giá trị chung:
// Cách 1: Thêm trực tiếp vào Model
@ModelAttribute
public void populateCommonData(Model model){
model.addAttribute("username", "admin");
model.addAttribute("role", "manager");
}
// Cách 2: Trả về Map và đặt tên key cụ thể
@ModelAttribute("appInfo")
public Map<String, String> getAppDetails(){
Map<String, String> info = new HashMap<>();
info.put("version", "1.0.0");
info.put("env", "production");
return info;
}
- Lấy giá trị tại controller:
@RequestMapping("/page1")
public void displayPage1(Model model){
Map<String, Object> attributes = model.asMap();
System.out.println(attributes);
}
@RequestMapping("/page2")
public void displayPage2(@ModelAttribute("role") String userRole){
System.out.println(userRole);
}
@RequestMapping("/page3")
public void displayPage3(ModelMap modelMap) {
System.out.println(modelMap);
}
3. Sử dụng @InitBinder
Tùy chỉnh binding tham số:
- Cấu hình binder:
@InitBinder
public void configureBinding(WebDataBinder binder){
binder.addCustomFormatter(new DateFormatter("dd/MM/yyyy"));
}
- Sử dụng trong API:
@RequestMapping("/process-date")
public String processInputDate(Date inputDate){
System.out.println("Ngày nhận được: " + inputDate);
return "success";
}