AOP底層實(shí)現(xiàn):JDK動(dòng)態(tài)代理詳解

一.創(chuàng)建代理的三要素:

  • 原始對(duì)象
  • 額外功能
  • 代理對(duì)象和原始對(duì)象實(shí)現(xiàn)相同的接口

打個(gè)比方,我去賣房子,然后我要帶客戶去看房子;可是有一天,我不想每天都帶那么多的客戶去看房子,我該怎么辦呢?找中介.讓中介代替我,領(lǐng)著客戶去看房子.那么中介需要偽裝成是房東(此時(shí)就要重寫原始對(duì)象中的方法).

二.JDK動(dòng)態(tài)代理:

Proxy.newProxyInstance(classloader,interfaces,invocationHandler)

2.1 invocationHandler介紹:

2.1.1 MethodInterceptor攔截器回顧:

我們?cè)趯W(xué)習(xí)spring動(dòng)態(tài)代理之MethodInterceptor攔截器的時(shí)候,需要實(shí)現(xiàn)MethodInterceptor接口,此時(shí)重寫了接口中的invoke方法,invoke參數(shù)中有一個(gè)MethodInvocation,它此時(shí)代表額外功能增加的那個(gè)原始方法,代碼如下:

public class Arroud implements MethodInterceptor {

    /**
     * spring動(dòng)態(tài)代理之MethodInterceptor攔截器
     * @param methodInvocation :額外功能增加的那個(gè)原始方法,如:register(),login()
     * @return: 因?yàn)槊總€(gè)方法的返回值都不一樣,所以需要object類來接受
     * @throws Throwable
     */
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("額外功能----");
        //表示:該原始方法執(zhí)行了,如register,login方法
        Object proceed = methodInvocation.proceed();
        System.out.println("方法的返回值:object="+proceed);
        return proceed;
    }
}

我們會(huì)發(fā)現(xiàn),這個(gè)methodInvocation是被spring進(jìn)行封裝了的,但是我們的JDK的代理還是原生的.

2.1.2 InvocationHandler介紹和演示:

invocationHandler是一個(gè)接口.我們需要重寫invoke方法,從而來增加額外的功能

/**
         * 2.額外功能
         *
         *
         */
        final InvocationHandler invocationHandler=new InvocationHandler() {
            /**
             * 為什么是method.invoke? 因?yàn)閯?dòng)態(tài)代理. 省去了service.register,service.login()
             *
             * @param proxy 代理對(duì)象
             * @param method 原始對(duì)象的原始方法
             * @param args  原始方法的參數(shù)
             * @return   原始方法的返回值
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //2.1 新增額外功能
                System.out.println("jdk proxy...");
                //2.2 執(zhí)行原始對(duì)象的原始方法
                Object invoke = method.invoke(userService, args);
                return invoke;
            }
        };

語(yǔ)法糖: jdk1.8之后,我們可以省略掉上面的final修飾符

三. interfaces介紹:

代理創(chuàng)建3要素的第三條,代理對(duì)象和原始對(duì)象需要實(shí)現(xiàn)相同的接口,這里的interfaces就是實(shí)現(xiàn)的接口.
此時(shí),我們?cè)趺传@取到這個(gè)接口呢?通過getClass().getInterfaces()可以獲取到類的所有接口定義.

四. classloader介紹:

4.1 類加載器怎么獲取呢?

(1).我們知道,一個(gè)user.java文件,通過編譯成user.class的字節(jié)碼文件,這里每個(gè)類的class文件,自動(dòng)會(huì)分配與之對(duì)應(yīng)的classloader類加載器;
(2).類加載器會(huì)把user.class字節(jié)碼文件放入到JVM虛擬機(jī)中;
(3).jvm虛擬機(jī)如何去創(chuàng)建一個(gè)user對(duì)象呢?此時(shí)肯定需要拿到user的class對(duì)象才能去創(chuàng)建user對(duì)象.此時(shí)也是通過類加載器去生成Class對(duì)象.

此時(shí),問題來了,我現(xiàn)在是動(dòng)態(tài)代理呀,我拿不到具體的.java文件,我也沒有具體的.class文件呀,順其自然的,我也就沒有對(duì)應(yīng)的類加載器呀,我該怎么辦?
答: 去借一個(gè)

五.最終的JDK動(dòng)態(tài)代理代碼如下:

package com.baizhiedu.jdl;


import com.baizhiedu.proxy.User;
import com.baizhiedu.proxy.UserService;
import com.baizhiedu.proxy.UserServiceImpl;

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

/**
 * JDK動(dòng)態(tài)代理
 */
public class TestJdkProxy {

    public static void main(String[] args) {

        //1.創(chuàng)建原始對(duì)象UserService
        final UserService userService = new UserServiceImpl();


        /**
         * 2.額外功能
         *
         *
         */
        final InvocationHandler invocationHandler=new InvocationHandler() {
            /**
             * 為什么是method.invoke? 因?yàn)閯?dòng)態(tài)代理. 省去了service.register,service.login()
             *
             * @param proxy 代理對(duì)象
             * @param method 原始對(duì)象的原始方法
             * @param args  原始方法的參數(shù)
             * @return   原始方法的返回值
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //2.1 新增額外功能
                System.out.println("jdk proxy...");
                //2.2 執(zhí)行原始對(duì)象的原始方法
                Object invoke = method.invoke(userService, args);
                return invoke;
            }
        };

        /**
         * 3.代理對(duì)象和原始對(duì)象實(shí)現(xiàn)相同的接口  interfaces:原始對(duì)象所實(shí)現(xiàn)的接口
         * 先借用userService的classLoader TestJdkProxy也可以
         */

        UserService service = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), invocationHandler);
        service.login(new User());
        service.register("Nisy",20);
    }
}

5.1 運(yùn)行結(jié)果:

jdk proxy...
登錄功能的核心代碼...
jdk proxy...
注冊(cè)功能的核心代碼...
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容