JDK 动态代理实现内幕

1. jdk 动态代理使用

要求:jdk 动态代理要求被代理的对象至少实现一个接口。
代理逻辑
jdk 会先产生一个继承 Proxy 类,并实现所有目标对象 implement 接口的代理类,然后调用代理类的构造方法(参数类型为 InvocationHandler)。代理类会:

  • 复写目标对象所有 implement 接口的所有方法,
  • 还会对 Object 类中的 equals, toString, hashCode 方法进行复写
    这里就是说,代理对象也会对 equals, toString, hashCode 这些方法进行代理。所以,如果 InvocationHandler 中只是简单的反射调用 target 对象的所有方法,那么代理对象.hashCode() 实际返回的是 target 对象的 hashCode (spring aop 没有对这些方法进行代理,就是因为其 InvocationHandler 实现类 JdkDynamicAopProxy 的 invoke 中对这些方法从做了判断,没有反射调用 target 对象的方法)

这些方法内部都是使用 InvocationHandler 对象invoke(Object targetObj, Method method, Object[] params) 方法,从而实现代理。

如下例子,接口为 Animal, 实现类为 Cat, InvocationHandler 为 MyDynamicProxy

// Animal.java
public interface Animal {
    void eat();
    void sayHello(String word);
}

// Cat.jjava
public class Cat implements Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }

    @Override
    public void sayHello(String word) {
        System.out.println(word);
    }

    @Override
    public String toString() {
        System.out.println("我是小花猫");
        return "我是小花猫";
    }
}

最终生成的代理类为 sun.proxy.$Proxy0

package com.sun.proxy;

import com2.test.proxy.Animal;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Animal {
	private static Method m0;   // Object 的 hashCode()
    private static Method m1;   // Object 的 equals()
    private static Method m2;   // Object 的 toString()
    private static Method m3;   // Animal 的 eat()
    private static Method m4;   // Animal 接口的 sayHello()
    // 构造器
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    // Object 的方法
    public final boolean equals(Object var1) throws  {
        return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
    }
    // 实现接口的方法
    public final void sayHello(String var1) throws  {
        super.h.invoke(this, m4, new Object[]{var1});
    }
	// 实现接口的方法
    public final void eat() throws  {
        super.h.invoke(this, m3, (Object[])null);
    }
	// Object 的方法
    public final String toString() throws  {
        return (String)super.h.invoke(this, m2, (Object[])null);
    }
	// Object 的方法
    public final int hashCode() throws  {
        return (Integer)super.h.invoke(this, m0, (Object[])null);
    }

    static {
        m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
        m4 = Class.forName("com2.test.proxy.Animal").getMethod("sayHello", Class.forName("java.lang.String"));
        m3 = Class.forName("com2.test.proxy.Animal").getMethod("eat");
        m2 = Class.forName("java.lang.Object").getMethod("toString");
        m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    }
}

看到,代理类代理执行方法,是先通过静态代码块获取代理的方法对象(Method),然后在自己实现的方法内,调用 InvocationHandler#invoke() 。 invoke() 方法则是通过反射调用 target 对象的方法进行代理
查看示例代码

2. jdk 动态代理类的生成

动态代理类的生成,需要客户端调用代码:

Proxy.newProxyInstance(target.getClass().getClassLoader(),
                       target.getClass().getInterfaces(),
                       this
                      );

源码中,生成代理对象分为三步:

  • (1)用目标对象的 ClassLoader, 实现的 interface 列表 生成代理类 Class
  • (2)获取代理类的一个参数,且参数类型为 InvocationHandler 的构造函数
  • (3)反射调用构造函数生成代理对象
// Proxy.java
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
{       
        // 1. 根据 ClassLoader、interfaces 生成代理类 Class
        Class<?> cl = getProxyClass0(loader, intfs);
        
        // 2. 从代理类 Class 中获取参数类型是 InvocationHandler 的构造方法
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        
        // 3. 通过构造方法生成代理类对象
        return cons.newInstance(new Object[]{h});
}

后两步很简单, 关键是如何生成代理类。生成过程在 ProxyClassFactoryapply()

  • (1)生成代理类的包名:
    遍历所有接口,如果目标对象 implement 了一个非 public 接口,则代理类包名和这个接口包名相同;如果都是 public 接口,代理类包名为 com.sun.proxy
  • (2)生成代理类类名:$Proxy 加数字
    数字是一个 static final AtomicLong
  • (3)生成代理类二进制数组 (该过程可先忽略,看上面的 class 文件即可)
  • (4)将二进制数据用 classLoader 加载进内存
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

      Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
      // ...
      String proxyPkg = null;     // (1)生成代理类所在包名
      int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
      // 遍历所有接口,如果目标对象 implement 了一个非 public 接口,则代理类包名和这个接口包名相同
      for (Class<?> intf : interfaces) {
          int flags = intf.getModifiers();
          if (!Modifier.isPublic(flags)) {
              accessFlags = Modifier.FINAL;
              String name = intf.getName();
              int n = name.lastIndexOf('.');
              String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
              if (proxyPkg == null) {
                  proxyPkg = pkg;
              } else if (!pkg.equals(proxyPkg)) {
                  throw new IllegalArgumentException(
                      "non-public interfaces from different packages");
              }
          }
      }

      if (proxyPkg == null) {
          // com.sun.proxy
          proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
      }

      // (2)代理类类名
      long num = nextUniqueNumber.getAndIncrement();
      // String proxyClassNamePrefix = "$Proxy"
      String proxyName = proxyPkg + proxyClassNamePrefix + num;

      // (3)生成代理类二进制数组
      byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
          proxyName, interfaces, accessFlags);
      // (4)将二进制数据用 classLoader 加载进内存
      return defineClass0(loader, proxyName,
                              proxyClassFile, 0, proxyClassFile.length);
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值