這是一篇純例項的部落格,網路上關於AspectJ的例項部落格非常少,很多的部落格都是在講解AOP思想的一些概念以及從AspectJ語言本身出發去講解AspectJ,但小編覺得作為急需在專案中使用,換句話說對於初學者來說,沒有必要從AspectJ語言本身去深入瞭解它,如果對AOP思想不明白的,請參考我的部落格一起來談談 Spring AOP!
本文將從AspectJ在Spring中的應用入手講解。
首先明白一點,AOP思想的實現框架或者說庫有很多,最知名的無非就是AspectJ、Cglib、JDK動態代理三種了,歸根到底它們都是使用了代理這種設計模式,區別在於下面表格:
名稱 | 代理型別 | 基本原理 | 特性 |
---|---|---|---|
AspectJ | 靜態代理 | 代理類在編譯期間就生成,但是在生成時將相應的切面織入 到代理類中 |
|
Cglib | 動態代理 | 代理類在執行時動態生成,底層使用位元組碼處理框架ASM,來轉換位元組碼並生成新的類 | 彌補JDK動態代理只能代理介面的不足,cglib可以動態代理類 |
JDK動態代理 | 動態代理 | 代理類在執行時動態生成,原始碼級別會呼叫一個Native方法 | 只能代理介面 |
關於cglib代理,可以閱讀小編的另外一篇部落格cglib代理的使用
一、Spring 中使用 AspectJ
1、xml 中開啟 aspectJ註解支援
applicationContext.xml
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
複製程式碼
2、宣告切面類
@Aspect
@Component
public class ExecutionAspect {
@Before("execution(* wokao666.club.myapp.aspectJ.*.before*(..))")
public void doBefore(JoinPoint joinPoint) throws Throwable {
System.err.println("這是一個前置通知,在方法呼叫之前被執行!!!");
}
@After("execution(* wokao666.club.myapp.aspectJ.*.after*(..))")
public void doAfter(JoinPoint joinPoint) throws Throwable {
System.err.println("這是一個後置通知,在方法呼叫之後被執行!!!");
}
@Around("execution(* wokao666.club.myapp.aspectJ.*.around*(..))")
public void doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.err.println("這是一個環繞通知,在方法呼叫前後都會執行!!!");
System.err.println("執行前");
joinPoint.proceed();
System.err.println("執行後");
}
}
複製程式碼
說明下上面的切入點表示式,execution
表示連線點型別,第一個*
表示攔截的方法但返回值型別,*
表示任意型別返回值;第二部分wokao666.club.myapp.aspectJ.*.before*(..)
表示以wokao666.club.myapp.aspectJ
包下的任意類的所有以before
開頭的方法。
3、測試類Main.java
public class Main {
private static ClassPathXmlApplicationContext ctx = null;
public static void main(String[] args) {
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
TestMethod test = (TestMethod) ctx.getBean("bean");
test.before("before");
System.err.println("=====================================================");
test.after("after");
System.err.println("=====================================================");
test.around("around");
}
複製程式碼
4、bean類
@Component("bean")
public class TestMethod {
public void before(String name) {
System.err.println("the param Name is " + name);
}
public void after(String name) {
System.err.println("the param Name is " + name);
}
public void around(String name) {
System.err.println("the param Name is " + name);
}
}
複製程式碼
pom.xml
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.1</version>
</dependency>
複製程式碼
執行結果:
這是一個前置通知,在方法呼叫之前被執行!!!
the param Name is before
=====================================================
the param Name is after
這是一個後置通知,在方法呼叫之後被執行!!!
=====================================================
這是一個環繞通知,在方法呼叫前後都會執行!!!
執行前
the param Name is around
執行後
複製程式碼
二、基於自定義註解的攔截實現
有時候我們會使用自定義註解來標識我們的業務方法,下面將講解AspectJ攔截基於註解的實現,跟生面的差別在於切入點表示式的區別而已。
1、建立一個自定義註解RpcService
/**
* 遠端服務註解,主要用於攔截日誌、錯誤碼等方面處理
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.TYPE })
public @interface RpcService {
}
複製程式碼
2、宣告業務方法
@Component("bean")
public class TestMethod {
@RpcService
public void around(String name) {
System.err.println("the param Name is " + name);
}
}
複製程式碼
3、宣告切面類
@Aspect
@Component
public class AnnotationAspect {
@Around("@annotation(wokao666.club.myapp.annotation.RpcService)")
public void doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.err.println("這是一個環繞通知,在方法呼叫前後都會執行!!!");
System.err.println("執行前");
joinPoint.proceed();
System.err.println("執行後");
}
}
複製程式碼
4、測試類
public class Main {
private static ClassPathXmlApplicationContext ctx = null;
public static void main(String[] args) {
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
TestMethod test = (TestMethod) ctx.getBean("bean");
test.around("around");
}
}
複製程式碼
5、執行結果
這是一個環繞通知,在方法呼叫前後都會執行!!!
執行前
the param Name is around
執行後
複製程式碼
畢業了,怎麼感覺有點心浮氣躁,穩住穩住!
如果我們想獲取被切方法的返回值,那麼我們可以使用
MethodSignature si = (MethodSignature) joinPoint.getSignature();
System.err.println(si.getReturnType());
複製程式碼