動態代理兩種方式


1.動態代理兩種方式簡述

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

CGLib動態代理:利用ASM(開源的Java字節碼編輯庫,操作字節碼)開源包,將代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。

區別:JDK代理只能對實現接口的類生成代理;CGlib是針對類實現代理,對指定的類生成一個子類,並覆蓋其中的方法,這種通過繼承類的實現方式,不能代理final修飾的類。

2.動態代理的特點

1.字節碼隨用隨創建,隨用隨加載。
2.它與靜態代理的區別也在於此。因為靜態代理是字節碼一上來就創建好,並完成加載。
3.裝飾者模式就是靜態代理的一種體現。

3.動態代理常用的有兩種方式

3.1 基於接口的動態代理

提供者:JDK 官方的 Proxy 類。
要求:被代理類最少實現一個接口。

3.2 基於子類的動態代理

提供者:第三方的 CGLib,如果報 asmxxxx 異常,需要導入 asm.jar。
要求:被代理類不能用 final 修飾的類(最終類)。

4.使用JDK官方的Porxy類創建對象

實體類:

package com.jh.spring13jdk動態代理;

import lombok.Data;

@Data
public class Game implements Open{
    //游戲的網速
    private int ms = 460;

    @Override
    public int openApp() {
        System.out.println("打開游戲后的網速是:" + this.getMs());
        return this.getMs();
    }
}

接口:

package com.jh.spring13jdk動態代理;

public interface Open {
    int openApp();
}

測試類:

package com.jh.spring13jdk動態代理;

import org.junit.Test;

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


public class Spring13Test {
    //JDK動態代理 省略了代理對象!!! 直接有jdk中的代理類來實現,但是目標類必須有接口
    @Test
    public void test01() {
        final Game game = new Game();
        /**
         * 代理:
         * 間接。
         * 獲取代理對象:
         * 要求:
         * 被代理類最少實現一個接口
         * 創建的方式
  * Proxy.newProxyInstance(三個參數)
         * 參數含義:
 * ClassLoader:和被代理對象使用相同的類加載器。 * Interfaces:和被代理對象具有相同的行為。實現相同的接口。 * InvocationHandler:如何代理。
         * 策略模式:使用場景是:
         * 數據有了,目的明確。
         * 如何達成目標,就是策略。
         *
         */
        //使用JDK動態類對象,當作迅游加速器的類,代替了靜態的代理類
        //Proxy:代理的意思。 newProxyInstancece創建代理對象
        Open jdkProxy = (Open) Proxy.newProxyInstance(
                game.getClass().getClassLoader(), //類的加載器
                game.getClass().getInterfaces(), //類的所有接口
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Integer ms = (Integer) method.invoke(game, args);
                        if (ms != null) {
                            ms = ms - 400;
                        }
                        return ms;
                    }
                }
        );
        int i = jdkProxy.openApp();
        System.out.println("i=" + i);
    }
}

5.使用CGLib的Enhancer類創建代理對象

對於沒有接口的類,如何實現動態代理呢,這就需要CGLib了,CGLib采用了非常底層的字節碼技術,其原理是通過字節碼技術為一個類創建子類,並在子類中采用方法攔截的技術攔截所有父類方法的調用,順勢織入橫切邏輯。但因為采用的是繼承。所以不能對final修飾的類進行代理。

實體類:

package com.jh.spring14cjlib動態代理;

import lombok.Data;

/**
 * 目標類
 * 父母
 */
@Data
public class Parents {
    //成績
    private int score = 599;

    //高考
    public int gaoKao(){
        System.out.println("父母參加高考,分數是:"+this.getScore());
        return this.getScore();
    }
}

工廠類:

package com.jh.spring14cjlib動態代理;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

//MethodInterceptor:方法上的攔截
public class ParentsFactory implements MethodInterceptor {
    private Parents parents;

    public ParentsFactory() {
        parents = new Parents();
    }


    /**
     * 基於子類的動態代理
     * 要求:
     * 被代理對象不能是最終類
     * 用到的類:
     * Enhancer
     * 用到的方法:
     * create(Class, Callback)
     * 方法的參數:
     * Class:被代理對象的字節碼
     * Callback:如何代理 */
    //增強器 ,把parents創造一個子類
    public Parents createParentsSon() {
        //使用字節碼增強器,去增強我們的父類
        Enhancer enhancer = new Enhancer(); //字節碼增強器,可以讀懂class文件
        //enhancer 指定一個對象
        enhancer.setSuperclass(Parents.class);//反射
        //使用工廠,換行(創建子類)
        enhancer.setCallback(this);
        //創建子類
        Parents son = (Parents) enhancer.create(); //多態
        return son;
    }

    /**
     * 執行被代理對象的任何方法,都會經過該方法。在此方法內部就可以對被代理對象的任何
     方法進行增強。
     *
     * 參數:
     * 前三個和基於接口的動態代理是一樣的。
     * MethodProxy:當前執行方法的代理對象。
     * 返回值:
     * 當前執行方法的返回值
     */
    //方法的攔截
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Integer score = (Integer) method.invoke(parents, objects);
        if (score != null) {
            score = score + 30;
        }
        return score;
    }
}

測試類:

package com.jh.spring14cjlib動態代理;

import org.junit.Test;

public class Spring14Test {
    @Test
    public void test01(){
        ParentsFactory parentsFactory = new ParentsFactory();
        Parents parentsSon = parentsFactory.createParentsSon();
        int score = parentsSon.getScore();
        System.out.println(score);
    }
}

6.總結:

CGLib創建的動態代理對象比JDK創建的動態代理對象的性能更高,但是CGLib創建對象時所花費的時間卻比JDK多的的。

7.問題:

為什么,要在不改變源代碼的基礎上,去寫一個代理類增強一些功能呢?

因為項目大了,就有主要的功能和次要的功能,要想主要功能和次要功能一起運行,必須用AOP

我再解釋下面向對象和面向切面,面向對象OOP,面向的是主要功能的對象,而AOP是面向對象OOP的一個補充,面向次要功能的對象

目的是為了降低耦合度,提高代碼的復用性。(自己總結的,僅供參考,你懂的,嘻嘻)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM