1. 程式人生 > >Spring AOP 前篇(一):Java代理之JDK靜態代理、JDK動態代理、CGLIB動態代理

Spring AOP 前篇(一):Java代理之JDK靜態代理、JDK動態代理、CGLIB動態代理

Spring AOP 前篇:Java代理之JDK靜態代理、JDK動態代理、CGLIB動態代理

該文章參考多篇文章的基礎上進行了簡化並做少許修改,方便理解。原文章地址如下:

  1. Java之代理(jdk靜態代理,jdk動態代理,cglib動態代理,aop,aspectj)

  2. AOP的底層實現-CGLIB動態代理和JDK動態代理

  3. 靜態代理、JDK動態代理和CGLIB動態代理之間的區別?

  4. java 靜態代理,jdk動態代理,CGLIB動態代理詳解

一、代理是Java常用的設計模式,代理類通過呼叫被代理類的相關方法,並對相關方法進行增強。加入一些非業務性程式碼,如事務、日誌、報警發郵件等操作。

​ AOP是Spring框架中的核心之一,在應用中具有非常重要的作用,也是Spring其他元件的基礎。AOP的核心功能的底層實現機制:如何用動態代理來實現切面攔截。

AOP的攔截功能是由java中的動態代理來實現的。

  • JDK動態代理:由java內部的反射機制來實現的 ;

  • CGLIB動態代理:底層則是藉助asm來實現的 ;

總的來說,反射機制在生成類的過程中比較高效,而asm在生成類之後的相關執行過程中比較高效(可以通過asm生成的類進行快取,這樣解決asm生成類過程低效問題)。

並且:JDK動態代理的應用前提必須是 目標類基於統一的介面;如果沒有上述前提,JDK動態代理不能應用。由此可以看出,JDK動態代理有一定的侷限性。CGLIB動態代理這種第三方類庫實現的動態代理應用更加廣泛,且在效率上更有優勢。

Spring在給某個類提供動態代理時,會自動在JDK動態代理和CGLIB動態代理中動態的選擇。

二、JDK靜態代理(設計模式中的簡單代理模式)

代理類與被代理類實現相同的方法。(圖文來自《圖解設計模式》)

在這裡插入圖片描述

代理類:(只在必要時生成例項)
在這裡插入圖片描述

在這裡插入圖片描述

三、JDK動態代理

​ JDK動態代理要求目標物件必須實現介面,因為它建立代理物件的時候是根據介面建立的。如果不實現介面,JDK無法給目標物件建立代理物件。被代理物件可以實現多個介面,建立代理時指定建立某個介面的代理物件就可以呼叫該介面定義的方法了。

JDK動態代理只能代理實現介面的類,沒有實現介面的類無法代理。

在java動態代理機制中,有兩個重要的類或介面,一個是InvocationHandler(Interface)、另一個則是Proxy(Class),這個類和介面是實現我們動態代理所必須用到的。

  1. 為目標類(target)定義統一的介面類Service,這個是JDK動態代理必須的前提。

  2. Service介面,及該介面實現類AService(需要被代理的類)。

    public class AService implements Service {
    	public void add() {
    		System.out.println("AService add>>>>>>>>>>>>>>>>>>");
    	}
     
    	public void update() {
    		System.out.println("AService update>>>>>>>>>>>>>>>");
    	}
    }
    
  3. 實現動態代理類MyInvocationHandler:實現InvocationHandler介面,並且實現介面中的invoke方法。每個動態代理類都必須要實現InvocationHandler這個介面,並且每個dialing類的例項都關聯到了一個handler:當我們通過代理物件呼叫一個方法的時候,這個方法的呼叫就會被轉發為由InvocationHandler這個介面的invoke方法來進行呼叫。Invocationhandler介面中的唯一一個方法------invoke()方法:

    Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    
    • proxy:我們所代理的那個真實物件 ;
    • method:所要呼叫真實物件的某個方法的Method物件 ;
    • args:呼叫真實物件某個方法時接受的引數 ;

    Proxy這個類的作用就是用來動態建立一個代理物件的類,它提供了許多的方法,但我們用的最多的就是 newProxyInstance這個方法:

public class MyInvocationHandler implements InvocationHandler {
	private Object target;
 
	MyInvocationHandler() {
		super();
	}
 
	MyInvocationHandler(Object target) {
		super();
		this.target = target;
	}
 
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// 程式執行前加入邏輯,MethodBeforeAdviceInterceptor
		System.out.println("before-----------------------------");
		// 程式執行
		Object result = method.invoke(target, args);
		// 程式執行後加入邏輯,MethodAfterAdviceInterceptor
		System.out.println("after------------------------------");
		return result;
	}
 
}
  1. 測試類:
public class Test {
	public static void main(String[] args) {
        // 被代理的物件
		Service aService = new AService();
        //動態代理類
		MyInvocationHandler handler = new MyInvocationHandler(aService);
		// Proxy為InvocationHandler實現類動態建立一個符合某一介面的代理例項
        // 即建立代理物件
		Service aServiceProxy = 
            (Service) Proxy.newProxyInstance(
            	aService.getClass().getClassLoader(),	// proxy 物件
            	aService.getClass().getInterfaces(), 	// method 物件
            	handler);							  // args 引數
		// 由動態生成的代理物件來aServiceProxy 代理執行程式,其中aServiceProxy 符合Service介面
		aServiceProxy.add();
		System.out.println();
		aServiceProxy.update();
	}
}
  1. 列印結果如下;在目標類AService的add和update方法前後已經加入了自定義的切面邏輯,AOP攔截機制生效了。

    before-----------------------------
    AService add>>>>>>>>>>>>>>>>>>
    after------------------------------
    before-----------------------------
    AService update>>>>>>>>>>>>>>>

    after------------------------------

四、CGLIB動態代理

​ CGLIB包的底層是通過使用一個小而快的位元組碼處理框架ASM,來轉換位元組碼並生成新的類。CGLIB是針對類來實現代理的,原理是對指定的目標生成一個子類,並覆蓋其中方法實現增強,但因為採用的是繼承,所以不能對final修飾的類進行代理。

​ 需要注意的是:JDK動態代理只可以為介面去完成操作;而CGLIB動態dialing可以為沒有實現介面的類去做代理,也可以為實現介面的類去做代理。

  • CGLIB動態代理通過 Enhancer類生成代理類。

    //CGLIB動態代理類實現MethodInterceptor介面,實現intercept()方法。
    public class CglibProxyFactory implements MethodInterceptor {
        //得到目標物件
        private Object target;
     
        //使用構造方法傳遞目標物件
        public CglibProxyFactory(Object target) {
            super();
            this.target = target;
        }
     
        //建立代理物件
        public Object createProxy(){
            //1.建立Enhancer
            Enhancer enhancer = new Enhancer();
            //2.傳遞目標物件的class
            enhancer.setSuperclass(target.getClass());
            //3.設定回撥操作
            enhancer.setCallback(this);
     		// 返回dialing類物件
            return enhancer.create();
        }
     
        @Override
        //引數一:代理物件;引數二:需要增強的方法;引數三:需要增強方法的引數;引數四:需要增強的方法的代理
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            // 增強前
            //新增切面邏輯(advise),此處是在目標類程式碼執行之前,即為MethodBeforeAdviceInterceptor。
            System.out.println("這是增強方法前......");
            Object invoke = methodProxy.invoke(target, args);
            // 增強後
            //新增切面邏輯(advise),此處是在目標類程式碼執行之後,即為MethodAfterAdviceInterceptor。
            System.out.println("這是增強方法後......");
            return invoke;
        }
     
        public static void main(String[] args) {
            // 1.建立物件
            UserServiceImpl userService = new UserServiceImpl();
            // 2.建立代理物件
            CglibProxyFactory proxy = new CglibProxyFactory(userService);
            // 3.呼叫代理物件的增強方法,得到增強後的物件
            IUserService createProxy = (IUserService) proxy.createProxy();
            createProxy.regist();
        }
    }