JDK动态代理原理

JDK动态代理原理

通过一个例子引入JDK动态代理

需求描述

Dao层类中的方法进行增强。例如:使用日志记录操作数据库所花的时间

类结构图

在这里插入图片描述

Mapper接口定义

public interface Mapper {
    void insert();
}

Mapper接口实现类

public class RealMapper implements Mapper {
    @Override
    public void insert() {
        System.out.println("do insert...");
    }
}

代理处理逻辑的封装

public class MapperInvocationHandler implements InvocationHandler {
    Object target;

    public MapperInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("do something...");
        return method.invoke(target, args);
    }
}

代理对象工厂

public class MapperProxyFactory {
    public static Object newInstance(Object realMapper, Class<?>... interfaces) {
        return Proxy.newProxyInstance(MapperProxyFactory.class.getClassLoader(), interfaces, new MapperInvocationHandler(realMapper));
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        RealMapper real = new RealMapper();
        Mapper mapper = (Mapper) MapperProxyFactory.newInstance(real, Mapper.class);
        mapper.insert();
    }
}

结果验证


可以看到最终我们实现了对原有实现的增强,下面我们源码查看内部的实现原理。

实现原理

Proxy#newProxyInstance

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    {
        Objects.requireNonNull(h);
        final Class<?>[] intfs = interfaces.clone();
        // 删除一些安全的验证代码
        Class<?> cl = getProxyClass0(loader, intfs);
        try {
        	// 通过反射创建代理对象
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            return cons.newInstance(new Object[]{h});
        } catch (Exception e) {
			// 异常的处理
        }
    }

该方法执行了以下几步

  1. 参数检查以及数据准备
  2. 调用getProxyClass0方法获取Class对象
  3. 通过反射创建代理对象并返回

获取Class对象(Proxy#getProxyClass0)

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // If the proxy class defined by the given loader implementing
    // the given interfaces exists, this will simply return the cached copy;
    // otherwise, it will create the proxy class via the ProxyClassFactory
    return proxyClassCache.get(loader, interfaces);
}

ProxyClassFactory#apply

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
		// nextUniqueNumber是当前类的静态常量
		// 是原子类 AtomicLong nextUniqueNumber = new AtomicLong();
        long num = nextUniqueNumber.getAndIncrement();
        // proxyPkg是包路径、eg:com.keminapera.proxy
        // proxyClassNamePrefix是当前类的静态常量
        // 始终固定不变 String proxyClassNamePrefix = "$Proxy";
		// 所以使用JDK动态代理生成的类名格式:$Proxy+一个数字  eg:$Proxy0、$Proxy1
        String proxyName = proxyPkg + proxyClassNamePrefix + num;
		// 这里通过ProxyGenerator生成代理类的字节数组
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
        try {
        	// 将上面生成代理类的字节数组通过指定的loader加载到JVM并得到该代理类的Class对象
        	// 对应的类名就是上面动态生成的proxyName---com.keminapera.proxy.$Proxy0
            return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            // 处理异常
        }
    }
}

代理类创建时序图

在这里插入图片描述

ProxyGenerator#generateProxyClass测试

上面我们知道最终通过调用ProxyGenerator类的generateProxyClass静态方法生成代理类的字节数组,我们通过模拟ProxyClassFactory#apply调用该方法来查看生成代理类的格式是什么样的。

测试类

public class ProxyGeneratorTest {
    private static final String proxyClassNamePrefix = "$Proxy";
    private static final AtomicLong nextUniqueNumber = new AtomicLong();
    public static void main(String[] args) throws Exception {
        String proxyClassName = getProxyClassName();
        byte[] bytes = ProxyGenerator.generateProxyClass(proxyClassName, new Class[]{Mapper.class}, Modifier.PUBLIC);
        genetateClassFile(bytes, "E:\\Chrome_Workspace", proxyClassName);
    }
	// 动态生成类名
    private static String getProxyClassName() {
        String name = ProxyGeneratorTest.class.getName();
        name = name.substring(0, name.lastIndexOf('.') + 1);
        return name + proxyClassNamePrefix + nextUniqueNumber.get();
    }

	// 读取class文件转换成byte[]
    static void genetateClassFile(byte[] bytes, String filePath, String fileName) throws Exception {
        fileName = fileName.replace(".","//");
        // 此处是上面定义好的Mapper.java编译后的class文件路径
        File file = new File("E:\\Chrome_Workspace\\Mapper.class");
        if (!file.exists()){
            file.createNewFile();
        }
        try(FileOutputStream fos = new FileOutputStream(file)){
            fos.write(bytes);
            fos.flush();
        }
    }
}

生成的内容

package com.keminapera.jdkapi.reflection.proxy;

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

public class $Proxy0 extends Proxy implements Mapper {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

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

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

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

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

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.keminapera.jdkapi.reflection.proxy.Mapper").getMethod("insert");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

生成的代理类有以下特点

  • 生成的代理类实现指定的所有接口并继承Proxy类
  • 重写了Object类的equalshashCodetoString方法
  • 实现了接口的所有方法,内部都是调用InvocationHandler实现类的invoke方法

结论

ProxyGenerator#generateProxyClass方法返回的是编译生成的字节码的字节数组

在这里插入图片描述

代理类实例化阶段

通过上面的流程我们已经拿到了代理对象的Class对象,有了Class对象通过反射可以生成一个真实对象。我们再次回到Proxy#newProxyInstance方法查看代理对象实例化部分代码。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    Class<?> cl = getProxyClass0(loader, intfs);
    // 代理对象实例化阶段开始
    try {
        // 通过反射获取定值参数的代理对象构造器
        // constructorParams参数是Proxy类的静态常量 
        // Class<?>[] constructorParams = { InvocationHandler.class };
        final Constructor<?> cons = cl.getConstructor(constructorParams); 
        final InvocationHandler ih = h;  
        // 通过构造器反射实例化代理对象
        return cons.newInstance(new Object[]{h}); 
    } catch (Exception e) {
        //异常的处理
    }

通过反射获取指定参数的构造器,每个动态生成的代理类都会有该构造器,获取如下构造方法

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

该构造器调用父类构造器,从上面ProxyGenerator的返回结果可以知道生成的代理类继承Proxy类,因此调用Proxy的构造方法

protected Proxy(InvocationHandler h) {
    Objects.requireNonNull(h);
    this.h = h;
}

最终设置Proxy的成员变量h

代理对象生成时序图

代理类生成的方法(Mapper接口的insert方法)

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

通过查看代理类中生成的方法会发现所有方法最终都调用的成员变量hinvoke方法,所以可以看出最后代理逻辑的处理是在用户定义的InvocationHandler实现类的invoke方法中实现的。整体的调用关系图如下:

在这里插入图片描述

动态代理的使用场景

小结

这块不得不提一下静态代理,从本质上来说动态代理就是将静态代理中由开发人员创建的代理类现在变成由JVM创建,如下图所示:
在这里插入图片描述
但静态代理有一个致命的缺点就是如果有成百上千个类需要增强,那就要为每一个类创建对应的增强逻辑,而且增强逻辑一模一样;这样会导致类爆炸和代码冗余
在这里插入图片描述
由于对这些类的增强逻辑是相同的,完全可以将其抽到一个类中实现,如果后续增强逻辑变了,只需要修改这一处即可,方便后续维护。由于现在代理类交给JVM去生成,所以它必须知道调用代理对象的方法时该交给谁去处理,所以JDK定义了一个接口InvocationHandler让开发人员去实现。
在这里插入图片描述
通过上面的分析静态代理和动态代理的区别就在于代理类交给JVM在运行期创建,从而减少代理类的数量。实现这一目标的手段是通过约定了一个InvocationHandler接口,JVM生成的代理类的方法全部交给InvocationHandler#invoke方法去处理,而该方法需要开发人员去实现。所以一般我们在InvocationHandler的实现类中会持有一个真正干活的目标对象,对方法拦截增强之后还是要调用目标对象的原有逻辑。

JDK动态代理的缺点:被代理类必须实现接口

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值