成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

理解 Java AOP - JDK動(dòng)態(tài)代理《二》

voidking / 2168人閱讀

摘要:原理動(dòng)態(tài)代理要求代理目標(biāo)必須是接口的實(shí)現(xiàn)類,通過接口生成模板類,模板類實(shí)現(xiàn)所有接口方法,實(shí)現(xiàn)方法是一個(gè)個(gè)模板方法,只是簡(jiǎn)單的通過反射把請(qǐng)求委托給處理。

使用 業(yè)務(wù)接口: IBiz
public interface IBiz {
    void doSomething();
}
業(yè)務(wù)實(shí)現(xiàn)類: BizImpl
public class BizImpl implements IBiz {

    public void doSomething() {
        System.out.println("做一些業(yè)務(wù)邏輯");
    }
}
橫切邏輯: PerformanceMonitor
public class PerformanceMonitor {

    public void start() {
        System.out.println("開始時(shí)間: " + String.valueOf(System.currentTimeMillis()));
    }

    public void end() {
        System.out.println("結(jié)束時(shí)間: " + String.valueOf(System.currentTimeMillis()));
    }
}
代理調(diào)用處理器: BizInvocationHandler

為接口生成的模板代理類,所有方法調(diào)用時(shí)都會(huì)委托給InvocationHandler.invoke(...)代為處理,它根據(jù)傳入的Method信息,使用反射機(jī)制調(diào)用真實(shí)的方法。

public class BizInvocationHandler implements InvocationHandler {

    private IBiz target;
    private PerformanceMonitor monitor;

    public BizInvocationHandler(IBiz target) {
        this.target = target;
        this.monitor = new PerformanceMonitor();
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        monitor.start();
        method.invoke(target);
        monitor.end();
        return null;
    }
}
代理生成器: ProxyBuilder
public class ProxyBuilder {

    private Class [] interfaces;
    private InvocationHandler handler;
    private ClassLoader classLoader = ProxyBuilder.class.getClassLoader();

    public static ProxyBuilder newProxyBuilder() {
        return new ProxyBuilder();
    }

    public ProxyBuilder setInterFaces(Class[] interFaces) {
        this.interfaces = interFaces;
        return this;
    }

    public ProxyBuilder setClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
        return this;
    }

    public ProxyBuilder setInvocationHandler(InvocationHandler handler) {
        this.handler = handler;
        return this;
    }

    public Object build() {
        return Proxy.newProxyInstance(classLoader, interfaces, handler);
    }

    public void buildClassFile(String className, String dir) {
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(className, interfaces);

        StringBuilder strBuilder = new StringBuilder();
        strBuilder.append(dir).append("/").append(className).append("class");
        String classFileName = strBuilder.toString();

        FileOutputStream out = null;
        try {
            out = new FileOutputStream(classFileName);
            out.write(proxyClassFile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

其中build()生成代理對(duì)象;buildClassFile(...)生成代理類的class文件。

測(cè)試
public class JDKProxyTest {

    @Test
    public void testBiz() {
        IBiz biz = new BizImpl();
        BizInvocationHandler hander = new BizInvocationHandler(biz);

        IBiz proxy = (IBiz)ProxyBuilder.newProxyBuilder()
            .setClassLoader(Thread.currentThread().getContextClassLoader())
            .setInterFaces(new Class[] {IBiz.class})
            .setInvocationHandler(hander)
            .build();
        proxy.doSomething();
    }
}

執(zhí)行輸出:

開始時(shí)間: 1500530510627
做一些業(yè)務(wù)邏輯
結(jié)束時(shí)間: 1500530510628
使用小結(jié)

JDK動(dòng)態(tài)代理使用步驟如下:

實(shí)現(xiàn)InvocationHandler接口,為其載入代理的目標(biāo)實(shí)例&橫切邏輯,通過實(shí)現(xiàn)invoke方法實(shí)現(xiàn)橫切邏輯織入。

通過Proxy.newProxyInstance(...)把要代理的接口和InvocationHandler實(shí)例聯(lián)系起來生成最終的代理實(shí)例。

通過強(qiáng)制類型轉(zhuǎn)換可以把生成的代理實(shí)例轉(zhuǎn)換成任何一個(gè)代理的接口類型,從而調(diào)用接口方法。

原理

JDK動(dòng)態(tài)代理要求代理目標(biāo)必須是接口的實(shí)現(xiàn)類,通過接口生成 模板類 ,模板類實(shí)現(xiàn)所有接口方法,實(shí)現(xiàn)方法是一個(gè)個(gè) 模板方法 ,只是簡(jiǎn)單的通過反射把請(qǐng)求委托給InvocationHandler.invoke(...)處理。

回頭看下ProxyBuilder.buildClassFile(...),它通過ProxyGenerator.generateProxyClass(...)生成IBiz的代理類。

生成代理類
public class JDKProxyTest {

    @Test
    public void testBuildClassFile() {
        IBiz biz = new BizImpl();
        BizInvocationHandler hander = new BizInvocationHandler(biz);

        ProxyBuilder.newProxyBuilder()
            .setClassLoader(Thread.currentThread().getContextClassLoader())
            .setInterFaces(new Class[] {IBiz.class})
            .setInvocationHandler(hander)
            .buildClassFile("proxy", ".");
    }
}
反編譯生成的代理類

直接通過Idea打開生成的proxy.class文件即可,反編譯后的代碼一下(注意:這里去掉了hashCode、toString等無強(qiáng)關(guān)聯(lián)性代碼):

public final class proxy extends Proxy implements IBiz {
    private static Method m1;
    
    ...

    public proxy(InvocationHandler var1) throws  {
        super(var1);
    }

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

    static {
        try {
            m1 = Class.forName("jdkproxy.IBiz").getMethod("doSomething", new Class[0]);
            
            ...
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

哈哈,代碼非常簡(jiǎn)單,無謂多說。

代理類實(shí)例化過程

入口: Proxy.newProxyInstance(...)

    public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        ...
        
        final Class[] intfs = interfaces.clone();
        
        // 獲取或生成指定接口的代理類,這里會(huì)對(duì)生成的代理類進(jìn)行緩存,下面展開。
        Class cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            ...

            // 獲取代理類的構(gòu)造方法
            final Constructor cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                // 構(gòu)造方法不為public的話,修改其訪問屬性
                AccessController.doPrivileged(new PrivilegedAction() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            
            // 通過反射調(diào)用代理類的構(gòu)造方法實(shí)例化代理對(duì)象返回。
            return cons.newInstance(new Object[]{h});
        } catch ...
        
        ...
    }

Proxy.getProxyClass0(...)

    private static Class getProxyClass0(ClassLoader loader,
                                           Class... interfaces) {
        // 限制接口數(shù)量
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // 這是一個(gè)WeakCach。
        // 如果cache中存在由loader加載并且實(shí)現(xiàn)了interfaces接口的代理類,就直接返回。
        // 否則就通過ProxyClassFactory創(chuàng)建代理類
        // proxyClassCache = (new WeakCache<>(new KeyFactory(), new ProxyClassFactory());)
        return proxyClassCache.get(loader, interfaces);
    }

ProxyClassFactory.apply(...)

最終生成代理類的邏輯就在這里

        public Class apply(ClassLoader loader, Class[] interfaces) {
            ....
            // 生成代理類的字節(jié)碼,buildClassFile.buildClassFile也是這樣生成代理類的。
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                // 返回定義的代理類
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                ...
            }
        }
總結(jié)

通過JDK代理生成的代理類是一個(gè)模板類,它通過反射找到接口的所有方法,并為每一個(gè)方法生成模板方法,通過反射調(diào)InvocationHandler.invoke(...),通常業(yè)務(wù)邏輯和橫切都在這里被調(diào)用。

由于JDK代理生成的代理類相對(duì)cglib生成子類要輕量級(jí)一些,所以在生成代理的效率上要優(yōu)于cglib代理,但是在調(diào)用時(shí),GDK代理通過反射的方式調(diào)用,相對(duì)cglib直接調(diào)用效率上要低。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/67434.html

相關(guān)文章

  • Spring AOP就是這么簡(jiǎn)單啦

    摘要:是一種特殊的增強(qiáng)切面切面由切點(diǎn)和增強(qiáng)通知組成,它既包括了橫切邏輯的定義也包括了連接點(diǎn)的定義。實(shí)際上,一個(gè)的實(shí)現(xiàn)被拆分到多個(gè)類中在中聲明切面我們知道注解很方便,但是,要想使用注解的方式使用就必須要有源碼因?yàn)槲覀円? 前言 只有光頭才能變強(qiáng) 上一篇已經(jīng)講解了Spring IOC知識(shí)點(diǎn)一網(wǎng)打盡!,這篇主要是講解Spring的AOP模塊~ 之前我已經(jīng)寫過一篇關(guān)于AOP的文章了,那篇把比較重要的知...

    Jacendfeng 評(píng)論0 收藏0
  • 【好好面試】學(xué)完Aop,連動(dòng)態(tài)代理的原理都不懂?

    摘要:總結(jié)動(dòng)態(tài)代理的相關(guān)原理已經(jīng)講解完畢,接下來讓我們回答以下幾個(gè)思考題。 【干貨點(diǎn)】 此處是【好好面試】系列文的第12篇文章。文章目標(biāo)主要是通過原理剖析的方式解答Aop動(dòng)態(tài)代理的面試熱點(diǎn)問題,通過一步步提出問題和了解原理的方式,我們可以記得更深更牢,進(jìn)而解決被面試官卡住喉嚨的情況。問題如下 SpringBoot默認(rèn)代理類型是什么 為什么不用靜態(tài)代理 JDK動(dòng)態(tài)代理原理 CGLIB動(dòng)態(tài)代理...

    Keven 評(píng)論0 收藏0
  • 從源碼入手,一文帶你讀懂Spring AOP面向切面編程

    摘要:,,面向切面編程。,切點(diǎn),切面匹配連接點(diǎn)的點(diǎn),一般與切點(diǎn)表達(dá)式相關(guān),就是切面如何切點(diǎn)。例子中,注解就是切點(diǎn)表達(dá)式,匹配對(duì)應(yīng)的連接點(diǎn),通知,指在切面的某個(gè)特定的連接點(diǎn)上執(zhí)行的動(dòng)作。,織入,將作用在的過程。因?yàn)樵创a都是英文寫的。 之前《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》詳細(xì)講了Spring容器的初始化和加載的原理,后面《你真的完全了解Java動(dòng)態(tài)代理嗎?看這篇就夠了》介紹了下...

    wawor4827 評(píng)論0 收藏0
  • Spring AOP() 修飾者模式和JDK Proxy

    摘要:修飾者模式設(shè)計(jì)模式中的修飾者模式能動(dòng)態(tài)地給目標(biāo)對(duì)象增加額外的職責(zé)。修飾者模式調(diào)用的時(shí)序圖如下圖所示。的實(shí)現(xiàn)原理和修飾者模式類似。 ?在上邊一篇文章中我們介紹了Spring AOP的基本概念,今天我們就來學(xué)習(xí)一下與AOP實(shí)現(xiàn)相關(guān)的修飾者模式和Java Proxy相關(guān)的原理,為之后源碼分析打下基礎(chǔ)。 修飾者模式 ?Java設(shè)計(jì)模式中的修飾者模式能動(dòng)態(tài)地給目標(biāo)對(duì)象增加額外的職責(zé)(Respon...

    Jackwoo 評(píng)論0 收藏0
  • Java 代理模式與 AOP

    摘要:本文首發(fā)于作者最近在學(xué),研究了下和代理模式,寫點(diǎn)心得和大家分享下。所以下面來重點(diǎn)分析下代理模式。這里代理模式分為靜態(tài)代理和動(dòng)態(tài)代理兩種,我們分別來看下。代理模式,代理,意味著有一方代替另一方完成一件事。 本文首發(fā)于 https://jaychen.cc作者 jaychen 最近在學(xué) Spring,研究了下 AOP 和代理模式,寫點(diǎn)心得和大家分享下。 AOP 先說下AOP,AOP 全稱 ...

    jk_v1 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<