深入理解動態(tài)代理

代理模式是在不修改原有代碼邏輯的情況下,對原有代碼邏輯增強的一種方式,要了解什么是動態(tài)代理,首先要知道什么是靜態(tài)代理。

一、靜態(tài)代理

假設一種場景,一個汽車類(Car)有一個啟動方法(run),我在不修改業(yè)務邏輯的情況下,我想知道啟動汽車花了多長時間該如何實現(xiàn)呢?

公共的汽車接口
/**
 * 機動車接口
 *
 */
public interface Auto {
    void run() throws Exception;
}
小汽車實現(xiàn)類
/**
 * 機動車接口
 *
 */
public class Car implements Auto {
    public void run() throws Exception {
        //模擬方法執(zhí)行過程
        Thread.sleep(new Random().nextInt(10));
        System.out.println("Car run");
    }
}

通過代理類實現(xiàn)對原有方法的增強,代理類實現(xiàn)相同的接口,并將被代理類指派給代理類的屬性對象。

/**
 * 代理類
 */
public class AutoProxy implements Auto {
    Auto auto;

    public AutoProxy(Auto auto) {
        this.auto = auto;
    }

    @Override
    public void run() throws Exception {
        long start = System.currentTimeMillis();
        auto.run();
        long end = System.currentTimeMillis();
        System.out.println("啟動時間:" + (end - start));
    }
}

運行代理類

public class Main {
    public static void main(String[] args) throws Exception {
        AutoProxy proxy = new AutoProxy(new Car());
        proxy.run();
    }
}
Car run
啟動時間:1

靜態(tài)代理的幾個要素:
接口:Auto
被代理類:Car
代理類:AutoProxy,實現(xiàn)相同接口,對原實現(xiàn)類進行增強
靜態(tài)代理的特點是在程序編譯期就已經(jīng)編譯好了,即代理類在運行之前就已經(jīng)存在了。假如隨著業(yè)務的發(fā)展,不光有小汽車,還出現(xiàn)了公交車(Bus),警車(Police Car)等等,如果用靜態(tài)代理,需要為每個被代理類實現(xiàn)一個代理類,那有沒有更優(yōu)雅的方式呢?答案就是動態(tài)代理。

二、動態(tài)代理

動態(tài)代理模式下,代理類實現(xiàn)InvocationHandler接口,被代理的業(yè)務無任何侵入,即可實現(xiàn)對一組接口實現(xiàn)類的增強。

代理類
/**
 * 動態(tài)代理
 */
public class DynamicAutoProxy implements InvocationHandler {
    Auto auto;

    public Object bind(Auto auto) {
        this.auto = auto;
        //生成代理對象
        //第一個參數(shù)為被代理類的類加載器
        //第二個參數(shù)為被代理類的接口
        //第三個參數(shù)為代理類實例,這里傳入this
        return Proxy.newProxyInstance(auto.getClass().getClassLoader(), auto.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        //反射調(diào)用實際的方法
        Object object = method.invoke(auto, args);
        long end = System.currentTimeMillis();
        System.out.println("啟動時間:" + (end - start));
        return object;
    }
}

動態(tài)代理類的幾個要素:
1、被代理對象(Car、Bus)
2、執(zhí)行者對象(DynamicAutoProxy)
3、代理對象,在執(zhí)行者對象中通過Proxy.newProxyInstance生成的代理對象。

運行代理類

public class Main {
    public static void main(String[] args) throws Exception {
        DynamicAutoProxy proxy = new DynamicAutoProxy();
        Auto car = (Auto) proxy.bind(new Car());
        car.run();

        Auto bus = (Auto) proxy.bind(new Bus());
        bus.run();
    }
}
Car run
啟動時間:1
Bus run
啟動時間:5
代理對象Proxy是如何生成的呢,跟蹤下源碼可以看出,只是對創(chuàng)建動態(tài)代理類的過程進行了封裝。
@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        // 執(zhí)行者對象判空
        Objects.requireNonNull(h);
        // 復制被代理類的接口
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        // 通過類加載器生成指定代理類
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 實例化一個Proxy代理對象
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

動態(tài)代理本質(zhì)上是在代碼運行的過程中動態(tài)的創(chuàng)建了Proxy代理對象。


jdk動態(tài)代理的前提是被代理類實現(xiàn)了接口,如果沒有實現(xiàn)接口又將如何代理呢,cglib動態(tài)代理就是為了解決這個問題。
springboot中通過@EnableAspectJAutoProxy與@Aspect注解實現(xiàn)動態(tài)代理,實際上@SpringBootApplication會自動開啟@EnableAspectJAutoProxy,不需要手工指定。
可通過下面代碼跟蹤
@SpringBootApplication->@EnableAutoConfiguration->spring.factories->@AopAutoConfiguration->@EnableAspectJAutoProxy

public @interface EnableAspectJAutoProxy {

    // true: 使用cglib代理,false:使用jdk動態(tài)代理,默認為false
    boolean proxyTargetClass() default false;

    //是否通過aop框架暴露代理對象
    boolean exposeProxy() default false;

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

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