天天看點

動态代理是如何實作的?JDK Proxy 和 CGLib 有什麼差別?

文章目錄

  • ​​動态代理的應用SpringAOP實作原理​​
  • ​​動态代理是如何實作的?JDK Proxy 和 CGLib 有什麼差別?​​
  • ​​JDK Proxy 和 CGLib 的差別​​
  • ​​JDK Proxy 動态代理實作​​
  • ​​CGLib 的實作​​
  • ​​Lombok 原理分析​​

動态代理的應用SpringAOP實作原理

動态代理是如何實作的?JDK Proxy 和 CGLib 有什麼差別?

動态代理的重用功能是反射,反射機制是指程式在運作期間可以通路,檢測和修改其本身狀态或行為的一種能力,使用反射我們可以調用任意一個類對象,以及類對象中包含的屬性及方法

但動态代理不止有反射一種實作方式,例如通過CGlib實作,==基于ASM(一個java位元組碼操作架構)==而非反射實作的.簡單來說,動态代理是一種行為方式,而反射或者ASM隻是它的一種實作手段而已

JDK Proxy 和 CGLib 的差別

JDK Proxy 和 CGLib 的差別主要展現在以下幾個方面

  • JDK Proxy 是Java語言自帶的功能,無需通過加載第三方類實作
  • java對jdk Proxy 提供了穩定的支援,并且會持續地更新和更新jdk Proxy 例如 Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多;
  • JDK Proxy是通過攔截器加反則地反射實作地
  • JDK Proxy 隻能代理繼承接口地類
  • JDK Proxy實作和調用用起來比較簡單
  • CGlib是第三方提供地工具,基于​

    ​ASM​

    ​實習,性能比較高
  • CGlib無需通過接口來實作,他是通過實作子類地方式來完成調用的
1.JDK Proxy 和 CGLib 的使用及代碼分析

JDK Proxy 動态代理實作

JDK Proxy 動态代理的實作無需引用第三方類,隻要需要實作​

​InvocationHandler​

接口,重寫​

​invoke()​

​方法即可

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * JDK Proxy 相關示例
 */
public class ProxyExample {
    static interface Car {
        void running();
    }

    static class Bus implements Car {
        @Override
        public void running() {
            System.out.println("The bus is running.");
        }
    }

    static class Taxi implements Car {
        @Override
        public void running() {
            System.out.println("The taxi is running.");
        }
    }

    /**
     * JDK Proxy
     */
    static class JDKProxy implements InvocationHandler {
        private Object target; // 代理對象

        // 擷取到代理對象
        public Object getInstance(Object target) {
            this.target = target;
            // 取得代理對象
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(), this);
        }

        /**
         * 執行代理方法
         * @param proxy  代理對象
         * @param method 代理方法
         * @param args   方法的參數
         * @return
         * @throws InvocationTargetException
         * @throws IllegalAccessException
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws InvocationTargetException, IllegalAccessException {
            System.out.println("動态代理之前的業務處理.");
            Object result = method.invoke(target, args); // 執行調用方法(此方法執行前後,可以進行相關業務處理)
            return result;
        }
    }

    public static void main(String[] args) {
        // 執行 JDK Proxy
        JDKProxy jdkProxy = new JDKProxy();
        Car carInstance = (Car) jdkProxy.getInstance(new Taxi());
        carInstance.running();
    }      

可以看出JDK Proxy實作動态代理的核心是實作Invocation 接口,我們檢視Invocation 源碼,就會發現裡面其實隻有一個invoke()方法 ,源碼如下

public interface InvocationHandler {
  public Object invoke(Object proxy, Method method, Object[] args)
          throws Throwable;
}      

因為在的動态代理種有一個重要的角色也就是代理器,用于統一管理被代理被對象,顯然​

​InvocationHandler​

​就是這個代理器,而invoke()方法則是觸發代理的執行方法,我麼通過實作Invocation 接口來擁有動态代理的能力

CGLib 的實作

在使用CGLib 之前,我們我先引用​

​CGLib​

​​架構,在 ​

​pom.xml​

​中添加如下配置

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>      

CGLib 實作代碼如下:

package com.lagou.interview;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLibExample {

    static class Car {
        public void running() {
            System.out.println("The car is running.");
        }
    }

    /**
     * CGLib 代理類
     */
    static class CGLibProxy implements MethodInterceptor {
        private Object target; // 代理對象

        public Object getInstance(Object target) {
            this.target = target;
            Enhancer enhancer = new Enhancer();
            // 設定父類為執行個體類
            enhancer.setSuperclass(this.target.getClass());
            // 回調方法
            enhancer.setCallback(this);
            // 建立代理對象
            return enhancer.create();
        }

        @Override
        public Object intercept(Object o, Method method,
                                Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("方法調用前業務處理.");
            Object result = methodProxy.invokeSuper(o, objects); // 執行方法調用
            return result;
        }
    }

    // 執行 CGLib 的方法調用
    public static void main(String[] args) {
        // 建立 CGLib 代理類
        CGLibProxy proxy = new CGLibProxy();
        // 初始化代理對象
        Car car = (Car) proxy.getInstance(new Car());
        // 執行方法
        car.running();
     }      

可以看出 CGLib 和 JDK Proxy 的實作代碼比較類似,都是通過實作代理器的接口,再調用某一個方法完成動态代理的,唯一不同的是,CGLib 在初始化被代理類時,是通過 Enhancer 對象把代理對象設定為被代理類的子類來實作動态代理的。是以被代理類不能被關鍵字 final 修飾,如果被 final 修飾,再使用 Enhancer 設定父類時會報錯,動态代理的建構會失敗。

Lombok 原理分析

Lombok 是在編譯期就為我們生成了對應的位元組碼。

其實 Lombok 是基于 Java 1.6 實作的 JSR 269: Pluggable Annotation Processing API 來實作的,也就是通過編譯期自定義注解處理器來實作的,它的執行步驟如下:

動态代理是如何實作的?JDK Proxy 和 CGLib 有什麼差別?

在編譯期階段,當java源碼被抽象成文法樹(AST)之後,LOMBOKhui 回根據自身的注解處理器動态修改AST,增加新的代碼(節點),在這一切執行之後就生成了最終的位元組碼(.class)檔案,這既是lombok執行原理

靜态代理和動态代理差別

靜态代理其實就是事先寫好代理類,可以手工編寫 也可以使用工具生成,但他的缺點是每個業務類都要對應一個代理類,特别不靈活也不友善,于是有了動态代理