AspectJ 在 Spring 中的使用

擁抱心中的夢想發表於2018-05-31

這是一篇純例項的部落格,網路上關於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());
複製程式碼

相關文章