Java动态代理-JDK实现(2)

Java1.8动态代理:

  1. JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。
  2. 其中动态代理分为两种:JDK 动态代理 和CGLIB动态代理, 本文主要讲述 JDK代理,CGLIB代码下一篇会讲到

简单实现:

  1. 计算一个 Sql 调用的总时间

代码逻辑:

  1. 创建一个SqlService 接口类:
public interface SqlService {
    void executeSql() throws InterruptedException;
}
  1. 创建 SqlServiceImpl 实现 SqlService 接口:
/**
 *  Describe: SqlService 的实现
 */
public class SqlServiceImpl implements SqlService {

    @Override
    public void executeSql() throws InterruptedException {
        System.out.println("Sql 开始执行.....");
        Thread.sleep(1000);
        System.out.println("Sql 执行结束.....");
    }
}
  1. 创建 SqlInvocationHandler 并实现 InvocationHandler 接口, 其中 invoke() 中计算 业务逻辑执行的时间。
    Object result = method.invoke(target, args); 此处是调用业务逻辑关键。
public class SqlInvocationHandler<T> implements InvocationHandler {

    // 需要代理的对象
    private T target;

    // 接受需要代理对象
    public SqlInvocationHandler(T target) {
        this.target = target;
    }

    // 代理方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 开始执行时间
        Long startTime = System.currentTimeMillis();
        // 此处是调用业务逻辑 关键 
        Object result = method.invoke(target, args);
        // 执行结束
        Long endTime = System.currentTimeMillis();
        System.out.println(target.getClass().getName()+"执行executeSql耗时"+(endTime-startTime)+"ms");
        return result;
    }
}
  1. 创建 Test 方法:
public class Test {

    public static void main(String[] args) throws InterruptedException {
        // 创建一个实例对象,这个对象是被代理的对象
        SqlService target = new SqlServiceImpl();

        // 创建一个与代理对象相关联的 InvocationHanler
        InvocationHandler handler = new SqlInvocationHandler<SqlService>(target);

      /**
         * 创建一个代理对象 SqlService 来代理 target, 代理对象的每个执行方法都会执行 Invovrtion 中的 invoke方法
         * ClassLoader loader : 类加载器
         * Class<?>[] interfaces : 被代理对象的 接口数组
         * InvocationHandler h : 代理类
         */
        SqlService sqlService = (SqlService) Proxy.newProxyInstance(SqlService.class.getClassLoader(), new Class<?>[]{SqlService.class}, handler);

        // 代理执行方法
        sqlService.executeSql();
    }
}

执行结果:

Sql 开始执行.....
Sql 执行结束.....
com.lot.jdkproxy.SqlServiceImpl执行executeSql耗时1000ms
代理类写入成功

动态代理逻辑分析:

  1. SqlService 和 SqlServiceImpl 就是正常的业务逻辑,不详聊
  2. 重点在 SqlInvocationHandler 类中, 首先接收到 需要代理的 对象并且 赋值到 target ,并实现java.lang.reflect.InvocationHandler 中 invoke() 方法,其中三个参数:
    Object proxy : 动态代理后的对象。
    Method method :方法
    Object[] args : 执行方法时的参数。
    其中 method.invoke(target, args) 则是执行业务逻辑代码。
  3. 在 Test 中 第一步,先实例化需要被代理的对象(target),然后传入到代理类中(SqlInvocationHandler), 最重要的是 : Proxy.newProxyInstance(SqlService.class.getClassLoader(), new Class<?>[]{SqlService.class}, handler) 这行代码,其来生成代理类,具体功能是由JDK 中 java.lang.reflect.Proxy 来实现的,原理下面讲,最后则是用代理类来执行业务逻辑

Proxy.newProxyInstance源码逻辑拆分:

  1. 当我们通过 java.lang.reflect.Proxy#newProxyInstance() 生成的 sqlService 对象 是 SqlServiceImpl 类实例化的吗,其实不是,当 Debug 运行main 方法时我们可以在IDEA 中 Debuggeer栏目 看到 sqlService 的类是 Proxy0:
    在这里插入图片描述
  2. 我们在Test 类main 方法后面加入以下代码,讲Proxy0 反编译到 Proxy0.class 中:
 // 代理执行方法
sqlService.executeSql();
        
// --------------------  将代理类反编译到文件中 ------------------------
byte[] classFile = ProxyGenerator.generateProxyClass(sqlService.getClass().getSimpleName() , sqlService.getClass().getInterfaces());
// Path: 为反编译后保存的文件路径
String path = "F:/mycode/Git/spring-mvc/learn/design-pattern-learn/src/main/java/com/lot/jdkproxy/Proxy0.class";
 try {
     FileOutputStream fos = new FileOutputStream(path);
     fos.write(classFile);
     fos.flush();
     System.out.println("代理类写入成功");
 } catch (Exception e) {
     e.printStackTrace();
 }
  1. 通过 Idea 打开 Proxy0.class 如下 :
/**
 * 代理类 其实是被代理类 的接口另一个实现
 */
public final class $Proxy0 extends Proxy implements SqlService {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1){
        super(var1);
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            // 接口方法
            m3 = Class.forName("com.lot.jdkproxy.SqlService").getMethod("executeSql");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

    /**
     * 代理的方法
     */
    public final void executeSql() throws InterruptedException {
        try {
            // super.h : 是main方法中实例化的InvocationHandler。
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | InterruptedException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    /**
     * 省略 equals 方法....
     * 省略 toString 方法....
     * 省略 hashCode 方法....
     */
}
  1. 通过查看 Proxy 原码就一目了然了,以上面的代码为例,整个动态代理的流程如下:
    Proxy.newProxyInstance : 生成代理类Proxy0并实例化 赋值sqlService,在执行sqlService.executeSql()时 : 先 $Proxy0.executeSql() ,再SqlInvocationHandler.invoke(),最后则是SqlServiceImpl.executeSql() 结束整个流程

$Proxy0 生成过程:

  1. 下面的代码是由 java.lang.reflect.Proxy#newProxyInstance 复制出来,加一些我自己的理解,大家有兴趣可以看一下:
/**
     * @param loader 类加载器
     * @param interfaces 接口数组
     * @param h  代理类
     * @return 生成的代理对象
     */
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h){

        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        // 生成代理类 ($Proxy0)
        Class<?> cl = getProxyClass0(loader, intfs);
        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;
                    }
                });
            }
            // 返回实例化的对象
            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);
        }
    }

总结:

使用JDK 动态代理,该类必须要实现一个接口,然后通过动态代理生成一个改接口的代理类Proxy0 (数字依次变大),实际调用的是 Proxy 类,并在其内部 执行InvocationHandler Invoke()方法,最后调用业务类(兄弟类)的方法。

后续:

1、静态代理: https://blog.csdn.net/zhangyong01245/article/details/90519769
2、Java 动态代理实现: https://blog.csdn.net/zhangyong01245/article/details/90598309
3、CGLIB动态代理以及逻辑: https://blog.csdn.net/zhangyong01245/article/details/90644933

以上是博主自己的理解与想法, 如有异议请大家留言,博主一定会第一时间解决

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值