spring AOP 認(rèn)知分享(先導(dǎo)篇 AspectJ、JDK_Proxy距境、CGLIB_Proxy)

我對(duì)spring的認(rèn)識(shí)是從《Spring 技術(shù)內(nèi)幕》開(kāi)始的申尼,這本書(shū)也是極力推薦給大家的;它采用的是自底而上的一個(gè)種方式來(lái)為我們一步一步解讀spring的設(shè)計(jì)原理垫桂,很清晰的展示了spring運(yùn)行時(shí)方法調(diào)用的過(guò)程师幕,同時(shí)也有上層實(shí)現(xiàn)的設(shè)計(jì)原理剖析。

一诬滩、概述AOP

  1. AOP技術(shù)其實(shí)是解決了在面向?qū)ο笤O(shè)計(jì)上沒(méi)有關(guān)聯(lián)的幾個(gè)類(lèi)霹粥,卻需要共同調(diào)用某一塊代碼邏輯的現(xiàn)象,最最常見(jiàn)的可就是日志的輸出打印了疼鸟。

  2. AOP的實(shí)現(xiàn)技術(shù)

AspectJ后控,源代碼和字節(jié)碼級(jí)別的編織器,需要使用AspectJ語(yǔ)言和Acj編譯器空镜。
AspectWerkz AOP框架浩淘,使用字節(jié)碼動(dòng)態(tài)編織器和XML配置
JBoss-AOP矾利, 基于攔截器和元數(shù)據(jù)的AOP框架,運(yùn)行在JBoss應(yīng)用服務(wù)器上
BCEL馋袜, Java字節(jié)碼操作類(lèi)庫(kù)
Javassist, Java字節(jié)碼操作類(lèi)庫(kù)
注:摘自《Spring 技術(shù)內(nèi)幕》舶斧。只做一個(gè)簡(jiǎn)單的了解欣鳖,實(shí)現(xiàn)AOP的技術(shù)方案其實(shí)有很多種

二、AspectJ茴厉、JDK_Proxy泽台、CGLIB_Proxy使用

注:項(xiàng)目搭建、代碼示例使用IntelliJ IDEA矾缓;項(xiàng)目源碼地址統(tǒng)一寫(xiě)在了結(jié)尾

1. AspectJ

首先需要在https://www.eclipse.org/aspectj/downloads.php官網(wǎng)下載aspectj.jar安裝包怀酷;我這里下載的是aspectj-1.9.1.jar,使用命令java -jar aspectj-xxx.jar 一直下一步安裝就好了嗜闻。
這里要記下具體安裝的目錄蜕依,因?yàn)楹竺嫖覀冃枰渲镁幾g環(huán)境


環(huán)境配置好后我們就可以進(jìn)行代碼的編寫(xiě)了。創(chuàng)建一個(gè)HellowWorld_AspectJ_Main程序運(yùn)行的入口

public class HellowWorld_AspectJ_Main {
    public static void main(String[] args) {
        System.out.println("mian method is print");
    }
}

再創(chuàng)建一個(gè)定位切面和執(zhí)行邏輯的類(lèi)HellowWorld_Pointcut

public aspect HellowWorld_Pointcut {

    pointcut helloWorld(): execution(* HellowWorld_AspectJ_Main.main(..));

    before(): helloWorld(){
        System.out.println("enter the HellowWorld_AspectJ_Main aop");

    }

}

直接運(yùn)行就可以了


最后要指出的重點(diǎn)是編譯后的兩個(gè)類(lèi)

HellowWorld_AspectJ_Main.class 反編譯后

public class HellowWorld_AspectJ_Main {
    public HellowWorld_AspectJ_Main() {
    }

    public static void main(String[] args) {
        HellowWorld_Pointcut.aspectOf().ajc$before$HellowWorld_Pointcut$1$4303d2e1(); //編譯后自動(dòng)添加的代碼
        System.out.println("mian method is print");
    }
}

HellowWorld_Pointcut.class 反編譯后

import org.aspectj.lang.NoAspectBoundException;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class HellowWorld_Pointcut {
    static {
        try {
            ajc$postClinit();
        } catch (Throwable var1) {
            ajc$initFailureCause = var1;
        }

    }

    public HellowWorld_Pointcut() {
    }

    @Before(
        value = "helloWorld()",
        argNames = ""
    )
    public void ajc$before$HellowWorld_Pointcut$1$4303d2e1() {
        System.out.println("enter the HellowWorld_AspectJ_Main aop");
    }

    public static HellowWorld_Pointcut aspectOf() {
        if (ajc$perSingletonInstance == null) {
            throw new NoAspectBoundException("HellowWorld_Pointcut", ajc$initFailureCause);
        } else {
            return ajc$perSingletonInstance;
        }
    }

    public static boolean hasAspect() {
        return ajc$perSingletonInstance != null;
    }
}

2. JDK_Proxy

準(zhǔn)備三個(gè)類(lèi)琉雳,ProxyInstanceJDK定義方法的接口样眠、ProxyInstanceJDKImpl實(shí)現(xiàn)類(lèi)、ProxyHandlerJDK回調(diào)類(lèi)翠肘,這三個(gè)類(lèi)我都放在了main所在的類(lèi)下面檐束,所以沒(méi)有用public修飾。

interface ProxyInstanceJDK {
    void print();
}

class ProxyInstanceJDKImpl implements ProxyInstanceJDK {
    @Override
    public void print() {
        System.out.println("target jdk class");
    }

}

class ProxyHandlerJDK implements InvocationHandler {

    private ProxyInstanceJDK targetClass;

    public ProxyHandlerJDK(ProxyInstanceJDK targetClass) {
        this.targetClass = targetClass;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("enter the jdk proxy invoke, proxy className is " + proxy.getClass().getSimpleName());

        return method.invoke(targetClass, args);
    }
}

main方法調(diào)用

    public static void main(String[] args) throws Exception {
        
        proxyJdk(); 
//        printJDKProxyClassByte();
//        proxyCglib();
    }

具體生成代理類(lèi)的過(guò)程可以查看java.lang.reflect.Proxy類(lèi)的源碼束倍。我這里把最后動(dòng)態(tài)生成代理類(lèi)的字節(jié)碼輸出在了一個(gè)**.class文件中被丧,反編譯后就可以看到生成的代理類(lèi)了。

  /**
     * 可以將生成好的代理類(lèi)字節(jié)碼打印出來(lái)
     * @throws Exception
     */
    public static void printJDKProxyClassByte() throws Exception {

        byte[] classFile = ProxyGenerator.generateProxyClass("$ProxyMyTest", ProxyInstanceJDKImpl.class.getInterfaces());

        FileOutputStream fos = new FileOutputStream(new File("***/example.class"));

        fos.write(classFile);
        fos.flush();
        fos.close();
    }

反編譯后代理類(lèi)的具體實(shí)現(xiàn)

import com.programmatic.springprogrammatic.ProxyInstanceJDK;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $ProxyMyTest extends Proxy implements ProxyInstanceJDK {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $ProxyMyTest(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void print() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.programmatic.springprogrammatic.ProxyInstanceJDK").getMethod("print");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

3. CGLIB_Proxy

只需要準(zhǔn)備一個(gè)具體的方法實(shí)現(xiàn)類(lèi)ProxyInstanceCglib

class ProxyInstanceCglib {
    public void print() {
        System.out.println("target cglib class");
    }
}

生成代理類(lèi)

  public static void proxyCglib() {

        Enhancer enhancer = new Enhancer();

        enhancer.setSuperclass(ProxyInstanceCglib.class);
        enhancer.setCallback(((MethodInterceptor) (o, method, objects, methodProxy) -> {
            System.out.println("enter the cglib proxy invoke");
            return methodProxy.invokeSuper(o, objects);
        }));

        ProxyInstanceCglib instanceCglib = (ProxyInstanceCglib) enhancer.create();
        instanceCglib.print();
        System.out.println(instanceCglib.getClass().getSimpleName());

    }

cglib沒(méi)有獲取代理類(lèi)的字節(jié)碼的類(lèi)方法(P髅谩甥桂!確實(shí)沒(méi)找到!S士酢)格嘁,不過(guò)可以通過(guò)設(shè)置System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "**/src/main/resources/templates");將運(yùn)行期間所有動(dòng)態(tài)創(chuàng)建的類(lèi)打印出來(lái)廊移。其中ProxyInstanceCglib$$EnhancerByCGLIB$$366568d是cglib是我們生成的代理類(lèi)糕簿,源碼有點(diǎn)長(zhǎng)就不貼出來(lái)了,我把它上傳到了github上狡孔,大家有興趣的可以看一下懂诗。

三、結(jié)語(yǔ)

把這幾種常用的AOP技術(shù)操作運(yùn)行起來(lái)苗膝,會(huì)對(duì)我們理解spring AOP的時(shí)候起到一個(gè)支撐的作用殃恒。雖然并沒(méi)有深入的研究,但或多或少的解決了“從哪里來(lái)”的問(wèn)題!


aspect github項(xiàng)目地址:https://github.com/lotusfan/aspectj
proxy github項(xiàng)目地址:https://github.com/lotusfan/spring-programmatic
使用到的類(lèi):com.programmatic.springprogrammatic.ProxyTest
Cglib生成的代理類(lèi):resources/templates/com/programmatic.springprogrammatic/ProxyInstanceCglib$$EnhancerByCGLIB$$366568d

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末离唐,一起剝皮案震驚了整個(gè)濱河市病附,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌亥鬓,老刑警劉巖完沪,帶你破解...
    沈念sama閱讀 212,029評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異嵌戈,居然都是意外死亡覆积,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)熟呛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)宽档,“玉大人,你說(shuō)我怎么就攤上這事庵朝÷鹪” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,570評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵九府,是天一觀(guān)的道長(zhǎng)欣孤。 經(jīng)常有香客問(wèn)我,道長(zhǎng)昔逗,這世上最難降的妖魔是什么降传? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,535評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮勾怒,結(jié)果婚禮上婆排,老公的妹妹穿的比我還像新娘。我一直安慰自己笔链,他們只是感情好段只,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著鉴扫,像睡著了一般赞枕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上坪创,一...
    開(kāi)封第一講書(shū)人閱讀 49,850評(píng)論 1 290
  • 那天炕婶,我揣著相機(jī)與錄音,去河邊找鬼莱预。 笑死柠掂,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的依沮。 我是一名探鬼主播涯贞,決...
    沈念sama閱讀 39,006評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼枪狂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了宋渔?” 一聲冷哼從身側(cè)響起州疾,我...
    開(kāi)封第一講書(shū)人閱讀 37,747評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎皇拣,沒(méi)想到半個(gè)月后严蓖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,207評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡审磁,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了岂座。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片态蒂。...
    茶點(diǎn)故事閱讀 38,683評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖费什,靈堂內(nèi)的尸體忽然破棺而出钾恢,到底是詐尸還是另有隱情,我是刑警寧澤鸳址,帶...
    沈念sama閱讀 34,342評(píng)論 4 330
  • 正文 年R本政府宣布瘩蚪,位于F島的核電站,受9級(jí)特大地震影響稿黍,放射性物質(zhì)發(fā)生泄漏疹瘦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評(píng)論 3 315
  • 文/蒙蒙 一巡球、第九天 我趴在偏房一處隱蔽的房頂上張望言沐。 院中可真熱鬧,春花似錦酣栈、人聲如沸险胰。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,772評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)起便。三九已至,卻和暖如春窖维,著一層夾襖步出監(jiān)牢的瞬間榆综,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,004評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工铸史, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奖年,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,401評(píng)論 2 360
  • 正文 我出身青樓沛贪,卻偏偏與公主長(zhǎng)得像陋守,于是被迫代替她去往敵國(guó)和親震贵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理水评,服務(wù)發(fā)現(xiàn)猩系,斷路器,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139
  • 說(shuō)明:本文主要內(nèi)容來(lái)自慕課網(wǎng)中燥。配合視頻食用口味更佳寇甸。主要是順著已經(jīng)學(xué)習(xí)的視頻順序總結(jié)一遍,以深化理解和方便日后復(fù)習(xí)...
    stoneyang94閱讀 849評(píng)論 3 5
  • 本文主要講實(shí)現(xiàn)AOP的 代理模式原理疗涉,以及靜態(tài)代理拿霉,動(dòng)態(tài)代理的區(qū)別和具體實(shí)現(xiàn)。 對(duì)SpringAOP的概念和使用咱扣,...
    _Zy閱讀 748評(píng)論 0 1
  • 在理解Spring AOP以及理清它與Aspect和cglib之間關(guān)系之前绽淘,有很多基礎(chǔ)工作要做,比如闹伪,先對(duì)代理模式...
    maxwellyue閱讀 1,200評(píng)論 0 5
  • 我上小學(xué)的時(shí)候偏瓤,挨過(guò)很多次打杀怠。每次打的都很疼痛。肉體上的疼痛厅克,我躲不了赔退,忍忍也就過(guò)了≈ぶ郏可是他的冷言呵斥還有那些否定...
    蘇瑪05閱讀 201評(píng)論 0 0