[Thực hành] Bổ sung aspect log cho ứng dụng quản lý danh sách khách hàng

9.AOP

Mục tiêu

Luyện tập ứng dụng lập trình hướng khía cạnh để phát triển dự án phần mềm một cách trong sáng.

Mô tả

Bổ sung các mối quan tâm về ghi log cho dự án có sẵn.

Hướng dẫn

Bước 1: bắt đầu làm việc với dự án có sẵn

Tải dự án quản lý danh sách khách hàng tại file đính kèm, thực hiện các cài đặt cần thiết để khởi chạy dự án. Đọc mã và tìm hiểu các chức năng.

Bước 2:định vị một mối quan tâm xuyên suốt

Tạo class Logger đại diện cho một mối quan tâm về việc ghi log các sự kiện quan trong khi app vận hành. Định nghĩa một hành vi ghi log với tên là error, dùng khi trong hệ thống có những exception được phát sinh. Thông thường các lỗi phát sinh bên dưới được giấu khỏi view hiển thị của người dùng, và việc đó gây khó khăn cho nhà phát triển khi debug những lỗi xảy ra khi app vận hành trên môi trường production, vì thế ghi log là rất quan trọng.

package cg.wbd.grandemonstration.concern;

public class Logger {
public void error() {
System.out.println("[CMS] ERROR!");
}
}

Tiếp theo, sử dụng các annotation phù hợp để định nghĩa các vai trò trong AOP:

@Aspect
public class Logger {
@AfterThrowing(pointcut = "execution(public * cg.wbd.grandemonstration.service.CustomerService.findAll(..))")
public void log() {

Point cut được viết ở trên mô tả rằng phương thức log được thực thi khi phương thức triển khai findAll từ interface CustomerService tung ra ngoại lệ thực thi. Dấu .. ngu ý rằng poincut này áp dụng cho mọi bộ tham số của findAll.

Để ứng dụng có thể khởi động lên được, cần thêm thư viện phụ thuộc spring-aspects vào file build.gradle

compile group: 'org.springframework', name: 'spring-aspects', version: '4.3.25.RELEASE'

Enable aspect proxy bằng cách sử dụng annotation sau tại AppConfig:

@EnableAspectJAutoProxy
public class AppConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {

Thêm bean aspect vào AppConfig:

@Bean
public Logger logger() {
return new Logger();
}

Để giả lập việc showList tung một expection, có thể thêm dòng lệnh sau vào thân method findAll, chỉ để bật dự án lên và kiểm tra Spring AOP đã hoạt động tốt, (kiểm tra bằng cách xem log tại server xem có dòng log liên quan được viết ra hay không):

@Override
public Page<Customer> findAll(Pageable pageInfo) throws Exception {
if (true) throw new Exception("a dummy exception");

Bước 3: mở rộng phạm vi áp dụng

Xóa bỏ exception giả ở trên, đưa nghiệp vụ chương trình trở lại bình thường. Để log được thực thi khi toàn bộ các phương thức public của class, sửa point cut thành như sau:

execution(public * cg.wbd.grandemonstration.service.CustomerService.*(..))

Chỉnh sửa triển khai của phương thức findOne của service để xuất bản một expection khi có ngoại lệ:

@Override
public Customer findOne(Long id) throws Exception {
Customer target = customerRepository.findOne(id);
if (target == null) {
throw new Exception("customer not found!");
}
return target;
}

Xử lý exception tại controller:

@GetMapping("{id}")
public ModelAndView showInformation(@PathVariable Long id) {
try {
ModelAndView modelAndView = new ModelAndView("customers/info");
Customer customer = null;
customer = customerService.findOne(id);
modelAndView.addObject("customer", customer);
return modelAndView;
} catch (Exception e) {
return new ModelAndView("redirect:/customers");
}
}

Chạy thử chương trình với một tham số không phù hợp cho màn hình info, quan sát log để thấy logger đã hoạt động.

Bổ sung tham số cho khai báo advice, để lấy thông tin về exception:

@AfterThrowing(pointcut = "execution(public * cg.wbd.grandemonstration.service.CustomerService.*(..))", throwing = "e")
public void log(Exception e) {
System.out.println("[CMS] co loi xay ra: " + e.getMessage());
}

Chạy thử chương trình và tái hiện lại exception để quan sát kết quả.

Mở rộng phạm vi của pointcut để áp dụng cho tất cả các phương thức của tất cả các class trong package service. Sử dụng đối tượng joinpoint để lấy thông tin về class, method, và bộ tham số nhằm debug dễ hơn:

@AfterThrowing(pointcut = "execution(public * cg.wbd.grandemonstration.service.*.*(..))", throwing = "e")
public void log(JoinPoint joinPoint, Exception e) {
String className = joinPoint.getTarget().getClass().getSimpleName();
String method = joinPoint.getSignature().getName();
String args = Arrays.toString(joinPoint.getArgs());
System.out.println(String.format("[CMS] co loi xay ra: %s.%s%s: %s", className, method, args, e.getMessage()));
}

Chạy thử chương trình và kiểm tra kết quả. Tạo thêm các exception hợp lý khác để chương trình hoạt động tốt hơn và quan sát logger hoạt động. Những log này giúp ích gì cho quá trình phát triển sản phẩm?

cms.zip cms.zip 

Leave a Reply

Your email address will not be published. Required fields are marked *