1. 程式人生 > >AOP的靜態代理和動態代理

AOP的靜態代理和動態代理

AOP(Aspect Orient Programming),我們一般稱為面向方面(切面)程式設計,作為面向物件的一種補充,用於處理系統中分佈於各個模組的橫切關注點,比如事務管理、日誌、快取等等。AOP實現的關鍵在於AOP框架自動建立的AOP代理,AOP代理主要分為靜態代理和動態代理,靜態代理的代表為AspectJ;而動態代理則以Spring AOP為代表。本文會分別對AspectJ和Spring AOP的實現進行分析和介紹。

用AspectJ的編譯時增強實現AOP

之前提到,AspectJ是靜態代理的增強,所謂的靜態代理就是AOP框架會在編譯階段生成AOP代理類,因此也稱為編譯時增強。

舉個例項的例子來說。首先我們有一個普通的Hello類

public class Hello {
    public void sayHello() {
        System.out.println("hello");
    }

    public static void main(String[] args) {
        Hello h = new Hello();
        h.sayHello();
    }
}

使用AspectJ編寫一個Aspect

public aspect TxAspect {
    void around():call(void
Hello.sayHello()){ System.out.println("開始事務 ..."); proceed(); System.out.println("事務結束 ..."); } }

這裡模擬了一個事務的場景,類似於Spring的宣告式事務。使用AspectJ的編譯器編譯

ajc -d . Hello.java TxAspect.aj

編譯完成之後再執行這個Hello類,可以看到以下輸出

開始事務 ...
hello
事務結束 ...

顯然,AOP已經生效了,那麼究竟AspectJ是如何在沒有修改Hello類的情況下為Hello類增加新功能的呢?

檢視一下編譯後的Hello.class

public class Hello {
    public Hello() {
    }

    public void sayHello() {
        System.out.println("hello");
    }

    public static void main(String[] args) {
        Hello h = new Hello();
        sayHello_aroundBody1$advice(h, TxAspect.aspectOf(), (AroundClosure)null);
    }
}

可以看到,這個類比原來的Hello.java多了一些程式碼,這就是AspectJ的靜態代理,它會在編譯階段將Aspect織入Java位元組碼中, 執行的時候就是經過增強之後的AOP物件。

public void ajc$around$com_listenzhangbin_aop_TxAspect$1$f54fe983(AroundClosure ajc$aroundClosure) {
        System.out.println("開始事務 ...");
        ajc$around$com_listenzhangbin_aop_TxAspect$1$f54fe983proceed(ajc$aroundClosure);
        System.out.println("事務結束 ...");
    }

從Aspect編譯後的class檔案可以更明顯的看出執行的邏輯。proceed方法就是回撥執行被代理類中的方法。

用Spring AOP實現AOP

與AspectJ的靜態代理不同,Spring AOP使用的動態代理,所謂的動態代理就是說AOP框架不會去修改位元組碼,而是在記憶體中臨時為方法生成一個AOP物件,這個AOP物件包含了目標物件的全部方法,並且在特定的切點做了增強處理,並回調原物件的方法。

Spring AOP中的動態代理主要有兩種方式,JDK動態代理和CGLIB動態代理。JDK動態代理通過反射來接收被代理的類,並且要求被代理的類必須實現一個介面。JDK動態代理的核心是InvocationHandler介面和Proxy類。

如果目標類沒有實現介面,那麼Spring AOP會選擇使用CGLIB來動態代理目標類。CGLIB(Code Generation Library),是一個程式碼生成的類庫,可以在執行時動態的生成某個類的子類,注意,CGLIB是通過繼承的方式做的動態代理,因此如果某個類被標記為final,那麼它是無法使用CGLIB做動態代理的。

為了驗證以上的說法,可以做一個簡單的測試。首先測試實現介面的情況。

定義一個介面

public interface Person {
    String sayHello(String name);
}

實現類

@Component
public class Chinese implements Person {

    @Timer
    @Override
    public String sayHello(String name) {
        System.out.println("-- sayHello() --");
        return name + " hello, AOP";
    }

    public void eat(String food) {
        System.out.println("我正在吃:" + food);
    }

}

這裡的@Timer註解是我自己定義的一個普通註解,用來標記Pointcut。

定義Aspect

@Aspect
@Component
public class AdviceTest {

    @Pointcut("@annotation(com.listenzhangbin.aop.Timer)")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void before() {
        System.out.println("before");
    }
}

執行

@SpringBootApplication
@RestController
public class SpringBootDemoApplication {

    //這裡必須使用Person介面做注入
    @Autowired
    private Person chinese;

    @RequestMapping("/test")
    public void test() {
        chinese.sayHello("listen");
        System.out.println(chinese.getClass());
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringBootDemoApplication.class, args);
    }
}

輸出

before
-- sayHello() --
class com.sun.proxy.$Proxy53

可以看到型別是com.sun.proxy.$Proxy53,也就是前面提到的Proxy類,因此這裡Spring AOP使用了JDK的動態代理。

再來看看不實現介面的情況,修改Chinese類

@Component
public class Chinese {

    @Timer
//    @Override
    public String sayHello(String name) {
        System.out.println("-- sayHello() --");
        return name + " hello, AOP";
    }

    public void eat(String food) {
        System.out.println("我正在吃:" + food);
    }

}

執行

@SpringBootApplication
@RestController
public class SpringBootDemoApplication {

    //直接用Chinese類注入
    @Autowired
    private Chinese chinese;

    @RequestMapping("/test")
    public void test() {
        chinese.sayHello("listen");
        System.out.println(chinese.getClass());
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringBootDemoApplication.class, args);
    }
}

輸出

before
-- sayHello() --
class com.listenzhangbin.aop.Chinese$$EnhancerBySpringCGLIB$$56b89168

可以看到類被CGLIB增強了,也就是動態代理。這裡的CGLIB代理就是Spring AOP的代理,這個類也就是所謂的AOP代理,AOP代理類在切點動態地織入了增強處理。

小結

AspectJ在編譯時就增強了目標物件,Spring AOP的動態代理則是在每次執行時動態的增強,生成AOP代理物件,區別在於生成AOP代理物件的時機不同,相對來說AspectJ的靜態代理方式具有更好的效能,但是AspectJ需要特定的編譯器進行處理,而Spring AOP則無需特定的編譯器處理。

參考文獻

相關推薦

AOP靜態代理動態代理

AOP(Aspect Orient Programming),我們一般稱為面向方面(切面)程式設計,作為面向物件的一種補充,用於處理系統中分佈於各個模組的橫切關注點,比如事務管理、日誌、快取等等。AOP實現的關鍵在於AOP框架自動建立的AOP代理,AOP代理

Spring AOP的實現原理—— 靜態代理動態代理

AOP(Aspect Orient Programming),我們一般稱為面向方面(切面)程式設計,作為面向物件的一種補充,用於處理系統中分佈於各個模組的橫切關注點,比如事務管理、日誌、快取等等。 AOP實現的關鍵在於AOP框架自動建立的AOP代理,

Spring AOP裡的靜態代理動態代理,你真的瞭解嘛?

什麼是代理?   為某一個物件建立一個代理物件,程式不直接用原本的物件,而是由建立的代理物件來控制原物件,通過代理類這中間一層,能有效控制對委託類物件的直接訪問,也可以很好地隱藏和保護委託類物件,同時也為實施不同控制策略預留了空間 什麼是靜態代理?   由程式建立或特定工具自動生成原始碼,在程式執行前,代理類

spring靜態代理動態代理

row main 同時 rac images 處理 解決 pro abstract 本節要點: Java靜態代理 Jdk動態代理 1 面向對象設計思想遇到的問題 在傳統OOP編程裏以對象為核心,並通過對象之間的協作來形成一個完整的軟件功能,由於對象可以繼承,因此我們可以

java 靜態代理動態代理

www. 同時 訪問方式 自身 代理模式 動態 滿足 size 基礎 代理模式 一、概述   代理是一種模式,提供了對目標對象的間接訪問方式,即通過代理訪問目標對象。如此便於在目標實現的基礎上增加額外的功能操作,前攔截,後攔截等,以滿足自身的業務需求,同時代理模式便於擴展目

靜態代理動態代理

代理模式 操作 改變 final false 為我 什麽 csdn 思考 代理模式(靜態代理) 代理模式是為其他對象提供一種代理以控制對這個對象的訪問。 定義上也不算好理解, 上一個 《大話設計模式》 的圖。 Subject 類(一般是抽象類或接口), 定義了一個方法

Java 8中的靜態代理動態代理的簡單心得

歡迎來到我的第一個部落格 個人學習的一點心得,第一次寫寫的不好見諒 看看程式碼吧 程式碼1. public interface CarSales { void sell(); } 程式碼2. public class QQCar implements

代理模式(靜態代理動態代理

Java 靜態代理 靜態代理通常用於對原有業務邏輯的擴充。比如持有二方包的某個類,並呼叫了其中的某些方法。然後出於某種原因,比如記錄日誌、列印方法執行時間,但是又不好將這些邏輯寫入二方包的方法裡。所以可以建立一個代理類實現和二方方法相同的方法,通過讓代理類持有真實物件,然後在原始碼中呼叫代理類方法,

Java設計模式之代理模式的靜態代理動態代理

前言 代理模式分為靜態和動態。同時靜態代理又分為普通代理和強制代理。因此本文會通過示例的方式詳細介紹著三種代理模式。我們依然以網上購物為例。   普通代理 定義:要求客戶端只能訪問代理角色,而不能直接訪問真實角色。我們需要修改真實角色: 1 public class RealCon

java中的代理靜態代理動態代理

之前本人在設計模式中有寫過靜態代理和動態代理的相關程式碼測試,可以看下。 今天我們主要學一下理論相關知識。 AOP的原理就是動態代理機制。RPC框架也是實現了AOP機制。 靜態代理 靜態代理:在程式碼編譯時就確定了被代理的類是哪一個。 這個靜態代理比較簡單,代理類和被代

Java的靜態代理動態代理

什麼是代理 什麼是代理呢,其實很好理解,就是不直接訪問目標,而是通過一箇中間層來訪問,就好像下面這樣: Java的靜態代理 舉個例子,如果我們一些水果,比如:香蕉、蘋果等,寫成Java程式碼,大概是下面這個樣子: //Fruit.java /** * 水果

Spring 靜態代理動態代理

靜態代理: 優點:   1、  實現鬆散耦合。   2、做到在不修改目標物件的功能前提下,對目標功能擴充套件。   缺點:  如果專案中有多個類,則需要編寫多個代理類,工作量大,不好修改,不好維護,不能應對變化。 模擬某位學生去考試作為例子:   下面

JDK動態代理[1]---靜態代理動態代理

靜態代理 定義介面 public interface Subject { void visit(); } 建立真正實現的類 public class RealSubject implements Subject { private Stri

設計模式 - Java靜態代理動態代理

本篇部落格的由來,之前我們學習大話設計,就瞭解了代理模式,但為什麼還要說呢? 原因: 1,通過DRP這個專案,瞭解到了動態代理,認識到我們之前一直使用的都是靜態代理,那麼動態代理又有什麼好處呢?它們二者的區別是什麼呢? 2,通過學習動態代理了解到動態代理是一種符合AOP設計思想的技術,那

Java 靜態代理動態代理的使用及原理解析

代理模式是軟體開發中常見的設計模式,它的目的是讓呼叫者不用持有具體操作者的引用,而是通過代理者去對具體操作者執行具體的操作。 靜態代理的實現 操作介面: public interface Operate { void doSomething(); } 複製程式碼 操作者: public cla

java靜態代理動態代理(一)

代理Proxy:  Proxy代理模式是一種結構型設計模式,主要解決的問題是:在直接訪問物件時帶來的問題。  代理是一種常用的設計模式,其目的就是為其他物件提供一個代理以控制對某個物件的訪問。代理類負責為委託類預處理訊息,過濾訊息並轉發訊息,以及進行訊息被委託類執行後的後續處理。 &

Spring 代理的講解(靜態代理動態代理)

1、靜態代理的角色分析: 抽象角色:一般使用介面或抽象類來實現   (租) 真實角色:被代理的角色   (房東) 代理角色:代理真實角色--代理真實角色後一般會做一些附屬操作  (代理) 客    

java學習之代理(2):靜態代理動態代理

一,代理的概念 代理是一個物件,代理物件為其他物件提供一種代理,以控制對這個物件的訪問,代理物件起到中介作用,可以去掉或者增加額外的服務。 如:火車票代售點就是火車站售票處的一個代理物件,可通過訪問代售點進行業務處理。 二,靜態代理的2種實現方式:繼承和聚合 靜態代理中的代

java靜態代理動態代理的區別

本篇部落格的由來,之前我們學習大話設計,就瞭解了代理模式,但為什麼還要說呢?原因:1,通過DRP這個專案,瞭解到了動態代理,認識到我們之前一直使用的都是靜態代理,那麼動態代理又有什麼好處呢?它們二者的區別是什麼呢?2,通過學習動態代理了解到動態代理是一種符合AOP設計思想的技

Java設計模式之—靜態代理動態代理

靜態代理 代理從字面意思來看就是,替代XX去做某事,在我們的程式中,一般替代實際物件去進行操作,扮演著中間人的角色: 客戶端 –> 業務類 客戶端 –> 代理 –>業務類(代理) 代理介面 interface O