Spring Core – Phần 6: AspectJ là gì? Spring AOP + AspectJ ví dụ với AspectJ.
1. AspectJ là gì?
AspectJ là một thư viện, đặc tả trong Java để thực hiện AOP.
Tương tự với Spring AOP mà ta đã sử dụng ở bài trước. Tuy nhiên nó cung cấp các annotation, sử dụng đơn giản hơn.
Tương tự như Spring AOP, thay vì định nghĩa trong Bean, AspectJ cung cấp các annotation để định nghĩa các pointcut, advice, target object…
Các Annotation chung chủa AspectJ gồm:
- @Before: Chạy trước khi method được thực thi
- @After: Chạy sau khi method trả về một kết quả
- @AfterReturning: Chạy sau khi method trả về một kết quả, lọc lấy kết quả trả về
- @AfterThrowing: Chạy khi method xảy ra exception
- @Around: Gồm các tất cả các advice trên
- @Aspect: Đánh dấu đây là 1 aspect
2. Ví dụ với Spring AOP + AspectJ
Mình vẫn tiếp tục dùng lại ví dụ lần trước nhưng sử dụng AspectJ
Tạo Class Hello.java
package stackjava.com.springaspectj; public class Hello { public void method1() { System.out.println("+++++++++++++++++++++++++++++++"); System.out.println("method 1"); } public String method2() { System.out.println("+++++++++++++++++++++++++++++++"); System.out.println("method 2"); return "hello"; } public void method3() { System.out.println("+++++++++++++++++++++++++++++++"); System.out.println("method 3"); throw new IllegalArgumentException(); } }
Khai báo thư viện để sử dụng Spring AOP và AspectJ
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>stackjava.com</groupId> <artifactId>SpringAspectJ</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <spring.version>4.3.13.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.3.13.RELEASE</version> </dependency> <!-- AspectJ --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.11</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.11</version> </dependency> </dependencies> </project>
Tạo Asepct bằng cách dùng Annotation @Aspect
package stackjava.com.springaspectj; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class LoggerAspectJ { @Before("execution(* stackjava.com.springaspectj.Hello.*(..))") public void logBefore(JoinPoint joinPoint) { System.out.println("before method: " + joinPoint.getSignature().getName()); } }
“execution(* stackjava.com.springaspectj.Hello.*(..))”: định nghĩa pointcut: ở đây thực hiện pointcut với tất cả các method của class Hello.java
Ví dụ mình đổi thành “execution(* stackjava.com.springaspectj.Hello.method2(..))” thì nó chỉ thực hiện pointcut với method2 của class Hello.java
Cấu hình bean và AspectJ trong file config
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd "> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <bean id="hello" class="stackjava.com.springaspectj.Hello" /> <!-- Aspect --> <bean id="logAspect" class="stackjava.com.springaspectj.LoggerAspectJ" /> </beans>
<aop:aspectj-autoproxy />
được dùng để định nghĩa Aspect. để sử dụng thẻ này chúng ta phải khai báo namespace aop cho nó:
Click vào file applicationContext.xml > Chọn tab Namespaces > chọn aop
(Lưu ý, eclipse của bạn phải cài spring tool mới có tab Namespaces, nếu chưa cài thì bạn phải thêm namespace bằng tay thôi =)) )
Sau khi chọn, namespace sẽ tự động được thêm vào file config
Demo:
package stackjava.com.springaspectj; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { public static void main(String[] args) throws Exception { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Hello hello = (Hello) context.getBean("hello"); hello.method1(); System.out.println("\n"); hello.method2(); } }
Kết quả:
before method: method1 +++++++++++++++++++++++++++++++ method 1 before method: method2 +++++++++++++++++++++++++++++++ method 2
Tương tự ta tạo các advice thực hiện @After, @AfterReturning, @AfterThrowing, @Around
package stackjava.com.springaspectj; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class LoggerAspectJ { @Before("execution(* stackjava.com.springaspectj.Hello.*(..))") public void logBefore(JoinPoint joinPoint) { System.out.println("before method: " + joinPoint.getSignature().getName()); } @After("execution(* stackjava.com.springaspectj.Hello.*(..))") public void logAfter(JoinPoint joinPoint) { System.out.println("before method: " + joinPoint.getSignature().getName()); } // chỉ thực hiện log với method2 của Hello.java @AfterReturning(pointcut = "execution(* stackjava.com.springaspectj.Hello.method2(..))", returning = "result") public void logAfterReturning(JoinPoint joinPoint, Object result) { System.out.println("after return method : " + joinPoint.getSignature().getName()); System.out.println("Method returned value is : " + result); } @AfterThrowing(pointcut = "execution(* stackjava.com.springaspectj.Hello.*(..))", throwing = "error") public void logThrow(JoinPoint joinPoint, Throwable error) { System.out.println("exception in method: " + joinPoint.getSignature().getName()); System.out.println("Exception is: " + error); } }
Deomo:
package stackjava.com.springaspectj; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { public static void main(String[] args) throws Exception { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Hello hello = (Hello) context.getBean("hello"); hello.method1(); System.out.println("\n"); hello.method2(); System.out.println("\n"); hello.method3(); } }
Kết quả:
before method: method1 +++++++++++++++++++++++++++++++ method 1 before method: method2 +++++++++++++++++++++++++++++++ method 2 after return method : method2 Method returned value is : hello before method: method3 +++++++++++++++++++++++++++++++ method 3 exception in method: method3 Exception is: java.lang.IllegalArgumentException
Okay, Done!
Download code ví dụ trên tại đây
References:
https://www.eclipse.org/aspectj/doc/released/faq.php#q:whatisaj