Giới thiệu Google Guice – Aspect Oriented Programming (AOP)

Trong các bài trước, tôi đã giới thiệu với các bạn Aspect Oriented Programming (AOP) và cách tự xây dựng một AOP Framework với JDK Proxy. Trong bài này, chúng ta sẽ cùng tìm hiểu cách sử dụng AOP với thư viện Google Guice.

1. Giới thiệu Google Guice AOP

Ngoài việc hỗ trợ mạnh mẽ về Dependency Injection (DI), nhiều loại Binding, Injection và Scope. Google còn hỗ trợ về AOP.

Guice AOP tuân thủ các đặc tả về cài đặt của  AOP Alliance cho lập trình hướng khía cạnh (AOP). Điều này cho phép sử dụng cùng một Interceptor trên nhiều Framework khác nhau.

Hai class quan trọng của Guice AOP:

  • Matcher : là một interface dùng để chấp nhận hoặc từ chối một giá trị. Trong Guice AOP, chúng ta cần hai matcher: một để xác định lớp nào tham gia và một để xác định các phương thức của các lớp đó.
  • MethodInterceptor : phương thức này được thực thi khi một phương thức thõa mãn matcher được gọi. Chúng ta có thể lấy được thông tin về phương thức gọi, các đối số và instance nhận được. Chúng ta có thể thực hiện logic cross-cutting concern và sau đó ủy quyền lại cho phương thức cơ bản (core concern). Cuối cùng, chúng ta có thể kiểm tra giá trị trả về hoặc throw ngoại lệ và return kết quả.

2. Ví dụ sử dụng AOP với thư viện Google Guice

Để sử dụng AOP với Guice, đơn giản chỉ thực hiện theo một vài bước sau:

  • Tạo một class implements lại phương thức invoke() của interface MethodInterceptor : dùng để cài đặt logic cross-cutting concern – các xử lý trước và sau khi logic xử lý chính được gọi (core concerns).
  • Tạo một annotation : dùng để đánh dấu phương thức nào sẽ được áp dụng logic được cài đặt với MethodInterceptor. Nếu không thích sử dụng Annotation, bạn thể có thể sử config Matcher trên package, sub-package, …
  • Cấu hình Binding cho Matcher trong file Module của Guice.
  • Đánh dấu Annotation đã tạo ở trên cho các phương thức cần áp dụng logic cross-cutting concern.

Chúng ta sẽ sử dụng lại ví dụ tự xây dựng một AOP Framework với JDK Proxy ở bài viết trước. Chương trình của chúng ta được viết lại bằng cách sử dụng Google Guice AOP như sau:

Account.java

package com.maixuanviet.patterns.creational.googleguice.aop.account;
 
import lombok.AllArgsConstructor;
import lombok.Data;
 
@Data
@AllArgsConstructor
public class Account {
 
    private String owner;
    private String currency;
    private int balance;
}

AccountService.java

package com.maixuanviet.patterns.creational.googleguice.aop.account;
 
public interface AccountService {
 
    void addAccount(Account account);
 
    void removeAccount(Account account);
     
    int getSize();
}

AccountServiceImpl.java

package com.maixuanviet.patterns.creational.googleguice.aop.account;
 
import java.util.ArrayList;
import java.util.List;
 
import com.maixuanviet.patterns.creational.googleguice.aop.handler.Loggable;
 
public class AccountServiceImpl implements AccountService {
 
    private List<Account> accounts = new ArrayList<>();
 
    @Loggable
    @Override
    public void addAccount(Account account) {
        System.out.println("addAccount: " + account);
        accounts.add(account);
    }
 
    @Loggable
    @Override
    public void removeAccount(Account account) {
        System.out.println("removeAccount: " + account);
        accounts.remove(account);
    }
 
    @Loggable
    @Override
    public int getSize() {
        System.out.println("getSize: " + accounts.size());
        return accounts.size();
    }
}

LoggingInterceptor.java

package com.maixuanviet.patterns.creational.googleguice.aop.handler;
 
import java.util.logging.Logger;
 
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
 
import com.google.inject.Inject;
 
/**
 * Implement the AOP Alliance's MethodInterceptor
 */
public class LoggingInterceptor implements MethodInterceptor {
 
    @Inject
    private Logger logger;
 
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        logger.info("LoggingInterceptor");
         
        // Handle before
        System.out.println("Handling before actual method execution");
         
        // Inspect the call: the method, its arguments
        System.out.println("Gets the method being called: " + invocation.getMethod().getName());
        Object[] objectArray = invocation.getArguments();
        for (Object object : objectArray) {
            System.out.println("Get the arguments: " + object);
        }
         
        // Proceeds to the next interceptor in the chain. 
        Object result = invocation.proceed();
         
        // Handle after
        System.out.println("Handling after actual method execution");
        System.out.println("---");
         
        return result;
    }
}

BasicModule.java

package com.maixuanviet.patterns.creational.googleguice.aop.handler;
 
import com.google.inject.AbstractModule;
import com.maixuanviet.patterns.creational.googleguice.aop.account.AccountService;
import com.maixuanviet.patterns.creational.googleguice.aop.account.AccountServiceImpl;
 
public class BasicModule extends AbstractModule {
 
    @Override
    protected void configure() {
        bind(AccountService.class).to(AccountServiceImpl.class);
    }
}

AOPModule.java

package com.maixuanviet.patterns.creational.googleguice.aop.handler;
 
import com.google.inject.AbstractModule;
import com.google.inject.matcher.Matchers;
 
public class AOPModule extends AbstractModule {
 
    @Override
    protected void configure() {
        // Inject interceptor
        LoggingInterceptor loggingInterceptor = new LoggingInterceptor();
        requestInjection(loggingInterceptor);
 
        // Define a binding for a Matcher
        bindInterceptor(Matchers.any(), Matchers.annotatedWith(Loggable.class), loggingInterceptor);
    }
}

Loggable.java

package com.maixuanviet.patterns.creational.googleguice.aop.handler;
 
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Loggable {
     
}

AspectOrientedProgrammingInJdkExample.java

package com.maixuanviet.patterns.creational.googleguice.aop;
 
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.maixuanviet.patterns.creational.googleguice.aop.account.Account;
import com.maixuanviet.patterns.creational.googleguice.aop.account.AccountService;
import com.maixuanviet.patterns.creational.googleguice.aop.account.AccountServiceImpl;
import com.maixuanviet.patterns.creational.googleguice.aop.handler.AOPModule;
import com.maixuanviet.patterns.creational.googleguice.aop.handler.BasicModule;
 
/**
 * This class to verify an AOP example using Google Guice.
 */
public class AspectOrientedProgrammingWithGuice {
 
    public static void main(String[] args) {
 
        Injector injector = Guice.createInjector(new BasicModule(), new AOPModule());
        AccountService proxy = injector.getInstance(AccountServiceImpl.class);
 
        Account account = new Account("maixuanviet", "USD", 100);
        proxy.addAccount(account);
        proxy.getSize();
        proxy.removeAccount(account);
        proxy.getSize();
    }
}

Output của chương trình:

Feb 12, 2019 9:25:04 PM com.maixuanviet.patterns.creational.googleguice.aop.handler.LoggingInterceptor invoke
INFO: LoggingInterceptor
Handling before actual method execution
Gets the method being called: addAccount
Get the arguments: Account(owner=maixuanviet, currency=USD, balance=100)
addAccount: Account(owner=maixuanviet, currency=USD, balance=100)
Handling after actual method execution
---
Feb 12, 2019 9:25:04 PM com.maixuanviet.patterns.creational.googleguice.aop.handler.LoggingInterceptor invoke
INFO: LoggingInterceptor
Handling before actual method execution
Gets the method being called: getSize
getSize: 1
Handling after actual method execution
---
Handling before actual method executionFeb 12, 2019 9:25:04 PM com.maixuanviet.patterns.creational.googleguice.aop.handler.LoggingInterceptor invoke
INFO: LoggingInterceptor
 
Gets the method being called: removeAccount
Get the arguments: Account(owner=maixuanviet, currency=USD, balance=100)
removeAccount: Account(owner=maixuanviet, currency=USD, balance=100)
Handling after actual method execution
---
Feb 12, 2019 9:25:04 PM com.maixuanviet.patterns.creational.googleguice.aop.handler.LoggingInterceptor invoke
INFO: LoggingInterceptor
Handling before actual method execution
Gets the method being called: getSize
getSize: 0
Handling after actual method execution
---

Related posts:

Queue và PriorityQueue trong Java
Java Program to Implement Interval Tree
A Quick Guide to Using Keycloak with Spring Boot
Transaction Propagation and Isolation in Spring @Transactional
Java Program to Check Whether it is Weakly Connected or Strongly Connected for a Directed Graph
@Lookup Annotation in Spring
HashSet trong java
Java Program to Find a Good Feedback Vertex Set
Java Multi-line String
A Guide to @RepeatedTest in Junit 5
Loại bỏ các phần tử trùng trong một ArrayList như thế nào trong Java 8?
Giới thiệu Swagger – Công cụ document cho RESTfull APIs
Java Program to Perform the Sorting Using Counting Sort
Java Program to Implement Gauss Seidel Method
Xử lý ngoại lệ đối với trường hợp ghi đè phương thức trong java
Weak References in Java
Java Program to Check Whether an Undirected Graph Contains a Eulerian Cycle
Java Program to Perform Insertion in a 2 Dimension K-D Tree
Understanding Memory Leaks in Java
Guide to the Java Queue Interface
The Order of Tests in JUnit
Introduction to the Functional Web Framework in Spring 5
Java Program to Check Whether a Given Point is in a Given Polygon
Giới thiệu java.io.tmpdir
Java Program to implement Priority Queue
Java Program to Implement a Binary Search Algorithm for a Specific Search Sequence
Autoboxing và Unboxing trong Java
Send an email using the SMTP protocol
How to Manually Authenticate User with Spring Security
Java Program to Find Minimum Number of Edges to Cut to make the Graph Disconnected
TreeSet và sử dụng Comparable, Comparator trong java
Java Program to Implement D-ary-Heap