1. 程式人生 > >spring學習筆記(7)AOP前夕[2]CGLib動態代理例項解析

spring學習筆記(7)AOP前夕[2]CGLib動態代理例項解析

CGLib動態代理基本原理

CGLib——Code Generation Library,它是一個動態位元組程式碼生成庫,基於asm。使用CGLib時需要匯入asm相關的jar包。而asm又是何方神聖?

asm是一個java位元組碼操縱框架,它能被用來動態生成類或者增強既有類的功能。ASM 可以直接產生二進位制 class 檔案,也可以在類被載入入 Java 虛擬機器之前動態改變類行為。Java class 被儲存在嚴格格式定義的 .class檔案裡,這些類檔案擁有足夠的元資料來解析類中的所有元素:類名稱、方法、屬性以及 Java 位元組碼(指令)。ASM從類檔案中讀入資訊後,能夠改變類行為,分析類資訊,甚至能夠根據使用者要求生成新類。
瞭解asm的功能原理,有助於更好地理解我們的CGLib,但在本節不會細究asm。我們的主要關注點還是如何使用CGLib實現AOP。為我們後面分析Spring AOP鋪墊。

在上一節中,我們用JDK的動態代理來模擬實現了一個性能監控的例子,用到了JDK內建的反射技術和java.lang.reflect.Proxy代理類,通過例子我們發現,它只能通過讓被代理類實現代理介面的方式來生成代理,而CGLib的區別在於通過在程式執行時動態生成一個被代理類的子類的方式來完成代理。它有幾個核心類:
1. Enhancer 它用於動態生成被代理的類的子類。使用此類生成子類的前奏是指定被代理類和指定CallBack介面
2. CallBack:它是一個很關鍵的介面,我們常常通過CallBack介面來配置我們的攔截方法,
3. MethodInterceptor:是CallBack的實現類,他會攔截我們被代理類的所有方法,來實現自己的增強細節。比如做點日誌記錄,方法處理等,處理完後,還能通過MethodProxy重新呼叫攔截掉的方法。
4. MethodProxy:主要用於重新呼叫MethodInterceptor攔截掉的方法,是jdk反射包中Method的代理類。
5. CallbackFilter:一個Enhancer生成類可以指定多個Callback,這樣我們可以設定條件過濾,讓被代理類中不同的方法被呼叫時使用不同的CallBack來進行處理。

例項匯入,需求分析

在上篇文章的例子基礎上,我們為我們“老類”的每個方法(例子中有method1、method2、method3三個方法)都實現了耗時統計,但現在,對於method3,因為它經常被使用者呼叫,每次被呼叫都統計耗時會對效能造成一定影響,因此,現在需要過濾掉對method3的的耗時統計,而且我們還想對其進行日誌記錄,看看哪些使用者什麼時候呼叫了這個方法
現在,結合前面提到的核心類,我們通過CGLib來完成這一輪新需求

原始碼例項展示

1. 定義被代理物件

我們的被代理物件:OldClass當然是不(能)變的啦。

public class OldClass
{
public void method1() throws InterruptedException{ System.out.println("正在處理業務邏輯1"); Thread.sleep(100);//模擬處理業務邏輯1過程 System.out.println("業務邏輯1處理完成"); } public void method2() throws InterruptedException{ System.out.println("正在處理業務邏輯2"); Thread.sleep(200);//模擬處理業務邏輯2過程 System.out.println("業務邏輯2處理完成"); } public void method3(String userName) throws InterruptedException{ System.out.println("正在處理業務邏輯3"); Thread.sleep(300);//模擬處理業務邏輯3過程 System.out.println("業務邏輯3處理完成"); } //下面還有很多很多。。 }

2. 定義代理生成工廠

public class ProxyFactory {
    private Enhancer enhancer = new Enhancer();//動態的類生成器
    public Object createSubObject(Class<?> clazz){
        enhancer.setSuperclass(clazz);//設定需要建立的類,這個類的父類是clazz類
        //當通過enhancer建立的類中的方法被呼叫時,該方法會被CallBack指定的物件攔截。
        enhancer.setCallbacks(new Callback[]{new MyTimeInterceptor(),new MyRecordInterceptor()});
        enhancer.setCallbackFilter(new MyCallBackFilter());//設定我們自定義的過濾器
        return enhancer.create();//通過位元組碼技術動態建立子類例項
    }
}

3. 定義增強攔截器

下面定義我們的兩個CallBack實現類,一個負責攔截需要統計耗時的方法,另一個攔截需要進行日誌記錄的方法

1. 耗時統計攔截器

public class MyTimeInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object target, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {//攔截所有父類方法的呼叫
        Long beginTime = System.currentTimeMillis();//記錄開始時間
        //呼叫目標物件的方法,同時獲取該方法的返回值,作為我們本代理方法(invoke)的返回值
        Object returnValue = proxy.invokeSuper(target, args);//target為我們方法所在的目標類,args為方法引數
        System.out.println("方法" + method.getName() + "呼叫結束,耗時"+ (System.currentTimeMillis() - beginTime));
        return returnValue;
    }
}

2. 日誌記錄攔截器

public class MyRecordInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object target, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {// 攔截所有父類方法的呼叫
        System.out.println(args[0] + "在"
                + new SimpleDateFormat("yyyy-MM-dd HH-mm").format(new Date())
                + "呼叫了方法" + method.getName());
        Object returnValue = proxy.invokeSuper(target, args);// target為我們方法所在的目標類,args為方法引數
        return returnValue;
    }

}

4. 定義我們的攔截過濾器

public class MyCallBackFilter implements CallbackFilter{//需要實現特定介面

    @Override
    public int accept(Method method) {
        if("method3" .equals(method.getName())){//如果被攔截的方法名滿足特定條件
            //這裡的序號對應於enhancer.setCallbacks(new Callback[]{new MyTimeInterceptor(),new MyRecordInterceptor()})中的Callback陣列的攔截器索引
            return 1;
        }else{
            return 0;
        }
    }
}

5. 測試方法

public static void main(String args[]) throws InterruptedException{
    ProxyFactory cgLibProxy = new ProxyFactory();//建立我們的代理類
    //通過enhancer建立我們的子類
    //因為oldClass是我們子類的父類,所以這裡向上轉型成功
    OldClass oldClass = (OldClass) cgLibProxy.createSubObject(OldClass.class);
    oldClass.method1();//呼叫方法
    oldClass.method2();//呼叫方法
    oldClass.method3("zenghao");//呼叫方法
}

6. 結果分析

執行5中的測試方法,控制檯列印:

正在處理業務邏輯1
業務邏輯1處理完成
方法method1呼叫結束,耗時116
正在處理業務邏輯2
業務邏輯2處理完成
方法method2呼叫結束,耗時201
zenghao在2016-03-24 18-32呼叫了方法method3
正在處理業務邏輯3
業務邏輯3處理完成

在這裡,我們的method1和method2還是被攔截下來統計耗時,但我們的method3就在呼叫前被做了日誌記錄了。

小結

使用CGlib來通過生成子類來完成代理,這樣,我們就不用強迫我們的被代理類實現代理介面了,侵入性更低。而且,使用CGLib還能為我們的攔截方法實現智慧過濾,相對於使用JDK的動態代理,還是優雅了很多。
但在實際的應用場景中,如果我們每次使用AOP,都要進行如上所示一堆配置,還是挺繁瑣的,但如果我們把配置的工作,交給spring完成,那麼我們只要通過簡潔的配置,就能輕鬆實現我們的動態代理。甚至結合上spring的許多特性,我們的代理功能還會更加的靈活強大。
通過對JDK和CGLib動態代理的例項理解,我們對AOP有一個更全面而感性地認識,從下篇文章我們開始進入springAOP部分的學習分析。

原始碼下載

相關推薦

spring學習筆記(7)AOP前夕[2]CGLib動態代理例項解析

CGLib動態代理基本原理 CGLib——Code Generation Library,它是一個動態位元組程式碼生成庫,基於asm。使用CGLib時需要匯入asm相關的jar包。而asm又是何方神聖? asm是一個java位元組碼操縱框架,它能被用來

Spring學習筆記02(AOP)

學了IOC之後,緊跟著就是AOP(面向切面程式設計) 來個官方一點的,面向切面程式設計,是通過預編譯方式和執行期動態代理實現程式功能的統一維護的一種技術。 簡單舉個例子來說,我們通過java程式碼對資料庫進行增刪改操作的時候,需要對事務進行控制,我們阻止事務的自動提交,事務成功後手動提交

spring學習筆記(8)AOP增強(advice)配置與應用

增強型別 增強(advice)主要包括如下五種型別 1. 前置增強(BeforeAdvice):在目標方法執行前實施增強 2. 後置增強(AfterReturningAdvice):在目標方法執行後實施增強 3. 環繞增強(MrthodIntercept

Cglib動態代理實現解析

在 JDK 動態代理原始碼解讀 已經知道了JDK 動態代理的實現邏輯,這裡我們來學習一下Cglib 的實現邏輯。以方便對動態代理有一個全面的認識。 首先,我們來看一下生成代理類的時序圖,對比起JDK的實現,它複雜了很多。 整體看上去比較難以理解,那我們來

【趣味設計模式系列】之【代理模式2--JDK動態代理原始碼解析

## 1. 圖解 ![](https://img2020.cnblogs.com/blog/1765702/202008/1765702-20200813090502793-1476832292.png) 上圖主要描述了JDK動態代理的執行過程,下面做詳細分析。 ## 2. Proxy原始碼分析 上一篇

Spring 學習筆記(六)AOP 之思想概念和作用、JDK代理Cglib子類代理

概念 AOP為Aspect Oriented Programming的縮寫,意味:面向切面程式設計。 與IOC一樣,是一種思想。 作用 對業務邏輯進行分離,降低耦合度,提高程式的重用性,提高開發效率。 JDK動態代理(介面代理) 弱點:JDK動態代理

Spring學習筆記(十一)AOP的註解方式cglib代理

JDK動態代理與CGLib動態代理均是實現Spring AOP的基礎,切點,切面,如何定義切點,前置、後置、放回、異常、環繞通知 1.切點、切面 紅色的地方就是切面,增加額外的功能 連線點+增加功能的位置 = 切點 2.專案結構

CgLib動態代理學習Spring AOP基礎之一】

div 目前 .get 不知道 ctu get() 內容 想要 外部依賴   如果不了解JDK中proxy動態代理機制的可以先查看上篇文章的內容:Java動態代理學習【Spring AOP基礎之一】   由於Java動態代理Proxy.newProxyInstance()的

(來換項目系列)Spring學習筆記2

servlet span on() 購物車 集成 因此 proxy center str 3.1.1 配置profile bean 使用@profile 註解 例如 @Configuration @Profile("dev") public class xxxConfig{

Spring 學習筆記(九)AOP 之註解方式與零配置方式

術語先來一發 目標物件(Target) 代理物件(Proxy) 連線點(Joinpoint) 切入點(Pointcut) 通知(增強)(Advice) 切面(Aspect、Advisor) 織入、切入(weaving) 1. 建立目標物件和代理物

Spring 學習筆記(八)AOP 之XML方式

術語先來一發 目標物件(Target) 代理物件(Proxy) 連線點(Joinpoint) 切入點(Pointcut) 通知(增強)(Advice) 切面(Aspect、Advisor) 織入、切入(weaving) 第一步,建立目標類和切面類

Spring 學習筆記(七)AOPAOP相關術語介紹

  截圖來自 51CTO  徐仕鋒 《Spring4深入淺出開發視訊教程》《 2-4 AOP相關屬於介紹》 http://edu.51cto.com//center/course/lesson/index?id=199916 講的清楚,特做記錄。

2.Spring學習筆記之 ————IoC(控制反轉)

控制反轉(IoC),是Spring裡一個專有的名詞,其意思就是說,物件的例項由Spring容器來進行建立而不是我們自己手動建立,當我們在Spring容器中設定好Bean屬性後,Spring容器就會自動建立其例項,我們只要去呼叫Spring的Bean就行。 接下來是例子:

Spring學習筆記(一)——AOP和IoC

用學習筆記的形式記錄自己在學習Spring的時候遇到的問題和自己的理解。 對AOP和IoC的理解 什麼是AOP? 在網上百度應有各種各樣的理解和答案,AOP(Aspect Oriented Programming)即面向切面程式設計。 所謂的切面即把各個模組分割開

Spring學習筆記2Spring專案 環境搭建

作者:藝術就是爆炸  出處:https://blog.csdn.net/lianjiww/article/details/53571795 準備好相應的環境: 我們需要如下內容:  - eclipse,這裡我選用的是Eclipse Java EE IDE for Web

Spring學習筆記-2:JSP標準標籤庫

個人部落格站已經上線了,網址 www.llwjy.com ~歡迎各位吐槽~-------------------------------------------------------------------------------------------------   

Spring學習筆記 —— AOP(面向切面程式設計) 之AspectJ

引言 在上一篇文章, Spring學習筆記 —— AOP(面向切面程式設計) 之Spring內建AOP中,我們簡單介紹了AOP的概念,也分析了Spring中使用proxyFactroyBean生成代理物件的實現原理。 當我們只需要對一個物件進行代理

Spring學習筆記四(AOP中的通知引數和註解開發)

 1.前言 上一篇部落格介紹瞭如何通過AOP來切入我們想實現的公共性的功能,這篇部落格來講一下,當我們攔截到方法後,如何來獲取通知引數。這也是AOP的精髓所在,通過AOP可以實現偷樑換柱的功能。我

Spring學習筆記 —— AOP標籤詳解()

引言 但是,除了面向切面程式設計之外,AOP的名字空間中還包含了一些重要的標籤,比如”scoped-proxy”。這篇文章就會詳細介紹這個標籤的作用,以及它的實現方式分析。 scoped-proxy 標籤介紹 在 Spring學習筆記 —

Spring學習筆記 —— AOP(面向切面程式設計) 之使用ProxyFactoryBean實現AOP

引言 到上一篇文章Spring學習筆記 —— Spring Context為止,我們已經基本瞭解Spring中的基本物件——Bean——的建立、相關屬性的注入以及獲取。其實在這不難發現,Spring的容器設計與Java的物件設計之間是有相似的地方的,