深入理解动态代理

简介: 代理模式是在不修改原有代码逻辑的情况下,对原有代码逻辑增强的一种方式,要了解什么是动态代理,首先要知道什么是静态代理。

代理模式是在不修改原有代码逻辑的情况下,对原有代码逻辑增强的一种方式,要了解什么是动态代理,首先要知道什么是静态代理。


一、静态代理


假设一种场景,一个汽车类(Car)有一个启动方法(run),我在不修改业务逻辑的情况下,我想知道启动汽车花了多长时间该如何实现呢?


公共的汽车接口

/**
 * 机动车接口
 *
 */
public interface Auto {
    void run() throws Exception;
}


小汽车实现类

/**
 * 机动车接口
 *
 */
public class Car implements Auto {
    public void run() throws Exception {
        //模拟方法执行过程
        Thread.sleep(new Random().nextInt(10));
        System.out.println("Car run");
    }
}


通过代理类实现对原有方法的增强,代理类实现相同的接口,并将被代理类指派给代理类的属性对象。

/**
 * 代理类
 */
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


静态代理的几个要素:


接口:Auto


被代理类:Car


代理类:AutoProxy,实现相同接口,对原实现类进行增强


静态代理的特点是在程序编译期就已经编译好了,即代理类在运行之前就已经存在了。假如随着业务的发展,不光有小汽车,还出现了公交车(Bus),警车(Police Car)等等,如果用静态代理,需要为每个被代理类实现一个代理类,那有没有更优雅的方式呢?答案就是动态代理。


二、动态代理


动态代理模式下,代理类实现InvocationHandler接口,被代理的业务无任何侵入,即可实现对一组接口实现类的增强。

代理类

/**
 * 动态代理
 */
public class DynamicAutoProxy implements InvocationHandler {
    Auto auto;
    public Object bind(Auto auto) {
        this.auto = auto;
        //生成代理对象
        //第一个参数为被代理类的类加载器
        //第二个参数为被代理类的接口
        //第三个参数为代理类实例,这里传入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();
        //反射调用实际的方法
        Object object = method.invoke(auto, args);
        long end = System.currentTimeMillis();
        System.out.println("启动时间:" + (end - start));
        return object;
    }
}


动态代理类的几个要素:


1、被代理对象(Car、Bus)


2、执行者对象(DynamicAutoProxy)


3、代理对象,在执行者对象中通过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是如何生成的呢,跟踪下源码可以看出,只是对创建动态代理类的过程进行了封装。

@CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        // 执行者对象判空
        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);
        }
    }


动态代理本质上是在代码运行的过程中动态的创建了Proxy代理对象。


image.png

jdk动态代理的前提是被代理类实现了接口,如果没有实现接口又将如何代理呢,cglib动态代理就是为了解决这个问题。


springboot中通过@EnableAspectJAutoProxy与@Aspect注解实现动态代理,实际上@SpringBootApplication会自动开启@EnableAspectJAutoProxy,不需要手工指定。

可通过下面代码跟踪


@SpringBootApplication->@EnableAutoConfiguration->spring.factories->@AopAutoConfiguration->@EnableAspectJAutoProxy

public @interface EnableAspectJAutoProxy {
    // true: 使用cglib代理,false:使用jdk动态代理,默认为false
    boolean proxyTargetClass() default false;
    //是否通过aop框架暴露代理对象
    boolean exposeProxy() default false;
}


相关文章
|
Java 程序员
动态代理
动态代理
51 0
|
6月前
|
设计模式 Java
动态代理详解
【2月更文挑战第7天】
动态代理详解
|
6月前
|
设计模式 缓存 监控
静态代理与动态代理
静态代理与动态代理
41 0
jdk动态代理和cglb动态代理
jdk动态代理和cglb动态代理
|
Java Spring
jdk动态代理和cglib动态代理
只有聪明人才能看见的简介~( ̄▽ ̄~)~
87 0
jdk动态代理和cglib动态代理
|
存储 Java Apache
一文理解动态代理和静态代理
一文理解动态代理和静态代理
200 0
一文理解动态代理和静态代理
|
Java 编译器 Maven
动态代理竟然如此简单!(二)
这篇文章我们来聊一下 Java 中的动态代理。 动态代理在 Java 中有着广泛的应用,比如 AOP 的实现原理、RPC远程调用、Java 注解对象获取、日志框架、全局性异常处理、事务处理等。
97 0
动态代理竟然如此简单!(二)
|
设计模式 Java 程序员
动态代理竟然如此简单!(一)
这篇文章我们来聊一下 Java 中的动态代理。 动态代理在 Java 中有着广泛的应用,比如 AOP 的实现原理、RPC远程调用、Java 注解对象获取、日志框架、全局性异常处理、事务处理等。
151 0
动态代理竟然如此简单!(一)
|
Java 数据库连接 API
动态代理的实际应用
最近在用 Python 的 SQLAlchemy 库时(一个类似于 Hibernate 的 ORM 框架),发现它的 Events 事件还挺好用。 简单说就是当某张表的数据发生变化(曾、删、改)时会有一个事件回调,这样一些埋点之类的需求都可以实现在这里,同时和业务代码完全解耦,维护起来也很方便。
|
设计模式 Java API
静态代理、动态代理(JDK动态代理,Cglib动态代理)(1)
静态代理、动态代理(JDK动态代理,Cglib动态代理)(1)
129 0
静态代理、动态代理(JDK动态代理,Cglib动态代理)(1)