解析動態代理jdk的Proxy與spring的CGlib(包括區別介紹)

1. 為什麼要使用動態代理?

動態代理:在不改變原有代碼的情況下上進行對象功能增強 使用代理對象代替原來的對象完成功能 進而達到拓展功能的目的

2.JDK Proxy 動態代理面向接口的動態代理

特點:

  • 一定要有接口和實現類的存在 代理對象增強的是實現類 在實現接口的方法重寫的方法
  • 生成的代理對象隻能轉換成 接口的不能轉換成 被代理類
  • 代理對象隻能增強接口中定義的方法 實現類中其他和接口無關的方法是無法增強的
  • 代理對象隻能讀取到接口中方法上的註解 不能讀取到實現類方法上的註解
  • 使用方法:
public class Test1 {
    public static void main(String[] args) {
        Dinner dinner=new Person("張三");
        // 通過Porxy動態代理獲得一個代理對象,在代理對象中,對某個方法進行增強
//        ClassLoader loader,被代理的對象的類加載器
        ClassLoader classLoader = dinner.getClass().getClassLoader();
//        Class<?>[] interfaces,被代理對象所實現的所有接口
        Class[] interaces= dinner.getClass().getInterfaces();
//        InvocationHandler h,執行處理器對象,專門用於定義增強的規則
        InvocationHandler handler = new InvocationHandler(){
            // invoke 當我們讓代理對象調用任何方法時,都會觸發invoke方法的執行
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//                Object proxy, 代理對象
//                Method method,被代理的方法
//                Object[] args,被代理方法運行時的實參
                Object res=null;
               if(method.getName().equals("eat")){
                   System.out.println("飯前洗手");
                   // 讓原有的eat的方法去運行
                   res =method.invoke(dinner, args);
                   System.out.println("飯後刷碗");
               }else{
                   // 如果是其他方法,那麼正常執行就可以瞭
                   res =method.invoke(dinner, args);
               }
                return res;
            }
        };
        Dinner dinnerProxy =(Dinner) Proxy.newProxyInstance(classLoader,interaces,handler);
        //dinnerProxy.eat("包子");
        dinnerProxy.drink();
    }
}
interface Dinner{
    void eat(String foodName);
    void drink();
}
class Person implements Dinner{
    private String name;
    public Person(String name) {
        this.name = name;
    }
    @Override
    public void eat(String foodName) {
        System.out.println(name+"正在吃"+foodName);
    }
    @Override
    public void drink( ) {
        System.out.println(name+"正在喝茶");
    }
}
class Student implements Dinner{
    private String name;
    public Student(String name) {
        this.name = name;
    }
    @Override
    public void eat(String foodName) {
        System.out.println(name+"正在食堂吃"+foodName);
    }
    @Override
    public void drink( ) {
        System.out.println(name+"正在喝可樂");
    }
}

3.CGlib動態代理

cglib動態代理模式是面向父類

特點:

面向父類的和接口沒有直接關系
2.不僅可以增強接口中定義的方法還可以增強其他方法
3.可以讀取父類中方法上的所有註解

使用實例

public class Test1 {
    @Test
    public void testCglib(){
        Person person =new Person();
        // 獲取一個Person的代理對象
        // 1 獲得一個Enhancer對象
        Enhancer enhancer=new Enhancer();
        // 2 設置父類字節碼
        enhancer.setSuperclass(person.getClass());
        // 3 獲取MethodIntercepter對象 用於定義增強規則
        MethodInterceptor methodInterceptor=new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                /*Object o,  生成之後的代理對象 personProxy
                Method method,  父類中原本要執行的方法  Person>>> eat()
                Object[] objects, 方法在調用時傳入的實參數組
                MethodProxy methodProxy  子類中重寫父類的方法 personProxy >>> eat()
                */
                Object res =null;
                if(method.getName().equals("eat")){
                    // 如果是eat方法 則增強並運行
                    System.out.println("飯前洗手");
                    res=methodProxy.invokeSuper(o,objects);
                    System.out.println("飯後刷碗");
                }else{
                    // 如果是其他方法 不增強運行
                    res=methodProxy.invokeSuper(o,objects); // 子類對象方法在執行,默認會調用父類對應被重寫的方法
                }
                return res;
            }
        };
        // 4 設置methodInterceptor
        enhancer.setCallback(methodInterceptor);
        // 5 獲得代理對象
        Person personProxy = (Person)enhancer.create();
        // 6 使用代理對象完成功能
        personProxy.eat("包子");
    }
}
class Person  {
    public Person( ) {
    }
    public void eat(String foodName) {
        System.out.println("張三正在吃"+foodName);
    }
}

原理區別:

java動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用InvokeHandler來處理。

而cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。

1、如果目標對象實現瞭接口,默認情況下會采用JDK的動態代理實現AOP 
2、如果目標對象實現瞭接口,可以強制使用CGLIB實現AOP 

3、如果目標對象沒有實現瞭接口,必須采用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換

如何強制使用CGLIB實現AOP?
 (1)添加CGLIB庫,SPRING_HOME/cglib/*.jar
 (2)在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

JDK動態代理和CGLIB字節碼生成的區別?
 (1)JDK動態代理隻能對實現瞭接口的類生成代理,而不能針對類
 (2)CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法
   因為是繼承,所以該類或方法最好不要聲明成final 

兩個動態代理的區別

  • JDK動態代理是面向接口的,隻能增強實現類中接口中存在的方法。CGlib是面向父類的,可以增強父類的所有方法
  • JDK得到的對象是JDK代理對象實例,而CGlib得到的對象是被代理對象的子類 生活雖然苦悶,但跑起來總是帶風!

到此這篇關於解析動態代理jdk的Proxy與spring的CGlib的文章就介紹到這瞭,更多相關動態代理jdk的Proxy與spring的CGlib內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: