Java 动态代理

以下解析参考了以下文章,加入了自己的一点小见解:
JDK动态代理(1)代理模式实现方式的概要介绍
JDK动态代理(2)JDK动态代理的底层实现之Proxy源码分析
JDK动态代理(3)WeakCache缓存的实现机制
JDK动态代理(4)ProxyGenerator生成代理类的字节码文件解析

代理模式

什么是代理模式?

在日常生活中,我们常常会使用到代理模式,比如通过房产中介买房,客户只需要发出一个请求,由代理去处理这个请求,再将结果返回给客户。

为什么使用代理模式?

比如买房,通过中介的话,客户不需要自己去找房源、去沟通等操作,使得买房这一操作更加便利、节省时间。当然客户也可以选择所有事情都自己来完成,但需要考虑两点,一个是是否有足够的时间和精力,二是是否具备足够的资源和能力。

怎么使用代理模式?

Java中有两种方式可以实现代理:静态代理和动态代理。

静态代理

静态代理,即由开发者实现代理类再进行编译,程序运行前,代理类的.class文件就已存在。

实现

1. 首先声明一个接口

public interface Image {
    void display();
}

2. 定义目标类,实现该接口

public class RealImage implements Image {

    private static final String TAG = "RealImage";

    @Override
    public void display() {
        Log.d(TAG, "display target...");
    }
}

3. 定义代理类,也需要实现该接口,并持有目标类的实例对象

public class ProxyImage implements Image {

    private static final String TAG = "ProxyImage";

    private RealImage realImage;

    public ProxyImage(RealImage realImage) {
        this.realImage = realImage;
    }

    @Override
    public void display() {
        Log.d(TAG, "Before display...");
        realImage.display();
        Log.d(TAG, "After display...");
    }
}

4. 使用代理类处理请求

RealImage realImage = new RealImage();
ProxyImage proxyImage = new ProxyImage(realImage);
proxyImage.display();
  1. 结果
D/ProxyImage: Before display...
D/RealImage: display image...
D/ProxyImage: After display…

缺点

静态代理虽然实现简单且容易理解,但由于代理类持有目标对象的引用,使得代理对象与目标对象耦合在一起,一个代理类不能作用于多个目标对象。如果一个接口下有多种实现,而每种实现都需要代理的话,就需要重新写代理类,不能重用代码。

动态代理

动态代理,即程序运行时通过反射机制创建的代理。

实现

采用上面的接口来实现动态代理,第1、2步就不重复了。
3. 实现InvocationHandler,在invoke()方法中执行代理行为

public class TransactionHandler implements InvocationHandler {

    private static final String TAG = "TransactionHandler";

    Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.d(TAG, "Before invoke target's method...");
        Object result = method.invoke(target, args);
        Log.d(TAG, "After invoke target's method...");
        return result;
    }
}

4. 通过Proxy.newProxyInstance()方法生成代理对象并执行

Object realImage = new RealImage();
Image proxyImage = (Image) Proxy.newProxyInstance(realImage.getClass().getClassLoader(),
        realImage.getClass().getInterfaces(), new TransactionHandler(realImage));
proxyImage.display();

5. 结果

D/TransactionHandler: Before invoke target's method...
D/RealImage: display target...
D/TransactionHandler: After invoke target's method...

动态代理解析

Proxy.newProxyInstance()

前面实现动态代理中讲到可以使用Proxy类提供的静态方法newProxyInstance()来创建一个代理对象,该方法接收3个参数:

  • loader : ClassLoader 目标类的类加载器
  • interfaces : Class<?>[] 目标类实现的接口集合
  • h : InvocationHandler 调用处理器

方法返回一个Object对象,即我们需要的代理对象。
接下来就从这个方法开始看代理类是怎样被一步一步创造出来的。

private static final Class<?>[] constructorParams =
    { InvocationHandler.class };

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    // 验证传入的InvocationHandler对象不能为空
    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);
        }
        // 获取参数类型是InvocationHandler的构造器
        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;
                }
            });

            cons.setAccessible(true);
        }
        // 传入InvocationHandler对象,通过构造器构造一个代理对象并返回
        // 所有代理类都是继承自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.newProxyInstance()方法做了以下事情:

  1. 对传入的参数进行校验
  2. 调用getProxyClass0()方法获取代理类对象
  3. 通过代理类对象获取带参(InvocationHandler)构造器
  4. 检验构造器是否可以访问,若不可以,就将其设为可访问的
  5. 传入InvocationHandler对象,构造代理对象并返回

这里最重要的就是第2步,获取代理类对象,我们来看看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);
}

可以看到这个方法很简单,首先判断接口数是否大于65535,如果是就抛出异常,否则就通过类加载器和接口从缓存中获取类对象。

缓存机制

Proxy使用了缓存机制来缓存动态代理类,类加载器和实现的接口数组作为key,通过KeyFactory来构建key,ProxyClassFactory来构建代理类,后面会详细解说。

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
    proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

我们先来看下WeakCache是怎么实现的。

WeakCache

final class WeakCache<K, P, V> {
    // 引用队列,被回收的弱引用会添加到引用队列中
    private final ReferenceQueue<K> refQueue
        = new ReferenceQueue<>();
    // 缓存的实现,key为一级缓存,value为二级缓存
    private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
        = new ConcurrentHashMap<>();
    // 记录了所有代理类生成器是否可用的状态
    private final ConcurrentMap<Supplier<V>, Boolean> reverseMap
        = new ConcurrentHashMap<>();
    // 二级缓存key的生成工厂,即Proxy.KeyFactory对象
    private final BiFunction<K, P, ?> subKeyFactory;
    // 二级缓存value的生成工厂,即Proxy.ProxyClassFactory对象
    private final BiFunction<K, P, V> valueFactory;
    
    // 构造器,传入二级缓存key的工厂以及二级缓存value的生成工厂
    public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }
    ...
}

首先我们看下WeakCache的成员变量与构造器,WeakCache缓存的内部实现是由ConcurrentMap来完成的,成员变量map是缓存的具体实现,reverseMap是为了缓存的过期机制存在的,subKeyFactoryvalueFactory分别是二级缓存key和value的工厂,通过构造器传入,这里传入的即KeyFactory对象与ProxyClassFactory对象。

从上面我们知道获取代理类对象的时候,是通过缓存的get()方法来获取的,那我们便来看下get()方法做了哪些事。

// Proxy中传入的key为classLoader,parameter为interfaces
public V get(K key, P parameter) {
    // 检查接口数组不为空
    Objects.requireNonNull(parameter);
    // 清除过期缓存
    expungeStaleEntries();
    // 将classLoader封装为CacheKey作为一级缓存key,CacheKey继承WeakReference
    Object cacheKey = CacheKey.valueOf(key, refQueue);

    // 构建一级缓存的value,如果为空则新建一个插入map中,调用ConcurrentMap.putIfAbsent,线程安全
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    if (valuesMap == null) {
        ConcurrentMap<Object, Supplier<V>> oldValuesMap
            = map.putIfAbsent(cacheKey,
                              valuesMap = new ConcurrentHashMap<>());
        if (oldValuesMap != null) {
            valuesMap = oldValuesMap;
        }
    }

    // 调用二级缓存key的工厂生成二级缓存key,并作非空判断
    // 这里调KeyFactory.apply()方法
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    // 从二级缓存中获取subKey对应的值
    Supplier<V> supplier = valuesMap.get(subKey);
    Factory factory = null;

    // 采用轮询机制,直到获取到代理类对象才退出循环
    while (true) {
        if (supplier != null) {
            // 如果subKey对应的值不为空,且获取的value也不为空,直接返回该对象
            // 这里的值可能是代理类生成器,也可能是代理类对象的弱引用,具体可看后面Factory.get()方法的解析
            V value = supplier.get();
            if (value != null) {
                return value;
            }
        }

        // 缓存未命中,构建一个代理类生成器
        if (factory == null) {
            factory = new Factory(key, parameter, subKey, valuesMap);
        }

        // 如果通过key获取的代理类生成器为空,则将新建的代理类生成器对象加入到二级缓存中
        // 如果不为空,则用新建的代理类生成器替换旧的,替换失败的话则用旧的生成器
        // 因为整个操作不是原子操作,有可能其他线程修改了supplier的值,所以需要做相应的判断
        if (supplier == null) {
            supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                // successfully installed Factory
                supplier = factory;
            }
            // else retry with winning supplier
        } else {
            if (valuesMap.replace(subKey, supplier, factory)) {
                supplier = factory;
            } else {
                // retry with current supplier
                supplier = valuesMap.get(subKey);
            }
        }
    }
}

可以看到,WeakCache.get()方法并没有作同步处理,它主要通过ConcurrentMap来实现线程安全,一级缓存、二级缓存使用的都是ConcurrentMap,从而将get()方法的同步代码块缩小,有助于提高WeakCache的性能。接下来看下WeakCache.get()的具体实现步骤:

  1. 检查接口数组是否为空;
  2. 清除过期缓存;
  3. ClassLoader的弱引用作为一级缓存的key,我们知道不同的类加载器加载同一个类得到的对象也是不一样的;
  4. 通过KeyFactory将接口数组封装为二级缓存的key;
  5. 根据上一步构建的key从二级缓存中获取supplier,如果supplier不为空且获取的值也不为空,则缓存命中,返回该值;
  6. 缓存未命中,构建代理类生成器,其中supplier为二级缓存中key对应的值,而factory为新建的对象,如果supplier为空,则将factory插入二级缓存中并赋值给supplier,否则就用新的factory替换二级缓存中旧的数据,替换失败则继续使用旧的对象。
  7. 回到第5步。

那么代理类是在哪里生成的呢?别着急,我们来看下代理类生成器的实现Factory的实现。

private final class Factory implements Supplier<V> {
    // 一级缓存key,即ClassLoader的弱引用
    private final K key;
    // 接口数组
    private final P parameter;
    // 二级缓存key
    private final Object subKey;
    // 二级缓存value
    private final ConcurrentMap<Object, Supplier<V>> valuesMap;

    Factory(K key, P parameter, Object subKey,
            ConcurrentMap<Object, Supplier<V>> valuesMap) {
        this.key = key;
        this.parameter = parameter;
        this.subKey = subKey;
        this.valuesMap = valuesMap;
    }

    @Override
    public synchronized V get() { // 加了同步锁,只能串行访问,线程安全
        // 重新检查二级缓存
        Supplier<V> supplier = valuesMap.get(subKey);
        // 二级缓存中key对应的生成器不是自己了
        if (supplier != this) {
            // 等待锁的时候发生了一些变化,可能是被替换了,或者被移除了,则结束该方法
            return null;
        }
        // create new value
        V value = null;
        try {
            // 调用ProxyClassFactory.apply()方法生成类对象
            value = Objects.requireNonNull(valueFactory.apply(key, parameter));
        } finally {
            if (value == null) { // 生成失败,则从二级缓存中移除该生成器
                valuesMap.remove(subKey, this);
            }
        }
        // 只有value的值不为空才能走到这里
        assert value != null;

        // 将value封装为CacheValue,CacheValue继承自WeakReference
        CacheValue<V> cacheValue = new CacheValue<>(value);

        // 用cacheValue替换二级缓存中key对应的代理类生成器,如果失败则抛出异常
        if (valuesMap.replace(subKey, this, cacheValue)) {
            // 并且把cacheValue添加到reverseMap中
            reverseMap.put(cacheValue, Boolean.TRUE);
        } else {
            throw new AssertionError("Should not reach here");
        }

        // 返回代理类对象
        return value;
    }
}

Factory这个内部工厂类实现了Supplier接口,具体的都在代码中注释了,有几个点需要注意一下:

  • get()方法加了同步锁,所以它是线程安全的。
  • get()方法的开始部分进行了一次检查操作,防止在等待锁的过程中,二级缓存中subKey对应的值发生了变化:被移除或被替换,则结束该方法,因为这个生成器已经无效了。
  • 生成代理类对象后,会将代理类对象封装一下,作为最终的二级缓存中的值,所以在解析WeakCache.get()方法中时,我们说通过subKey获取的值有可能是类对象生成器,也有可能是类对象的弱引用。
  • 类对象的构建是在ProxyClassFactory中实现的,ProxyClassFactoryProxy类的内部类。

Proxy.ProxyClassFactory

private static final class ProxyClassFactory
    implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
    // 所有动态代理类名的前缀
    private static final String proxyClassNamePrefix = "$Proxy";

    // 用原子类来生成代理类的序号,以此来确定唯一的代理类
    private static final AtomicLong nextUniqueNumber = new AtomicLong();

    @Override
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

        Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        // 遍历接口数组,做一些判断
        for (Class<?> intf : interfaces) {
            // 1. 接口是否可以由该ClassLoader加载 
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
            // 2. 是否确实是个接口
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            // 3. 接口是否重复,这里用到了IdentityHashMap的特性:
            // 判断key是否重复使用的是引用相等性,即key1 == key2
            // 因此如果put()的返回值不为空,则说明interfaceSet中存在相同key引用,即接口
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }

        String proxyPkg = null;     // 代理类的包名
        // 代理类的访问标志,默认是public final的
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

        // 非public的接口,则其代理类与接口同一个包名,
        // 确保所有非public的接口都在同一个包名下
        for (Class<?> intf : interfaces) {
            int flags = intf.getModifiers();
            if (!Modifier.isPublic(flags)) {
                // 非public的,访问标志则为final
                accessFlags = Modifier.FINAL;
                // 获取非public接口的包名
                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)) {
                    // 如果非public的接口包名不同,抛出异常
                    throw new IllegalArgumentException(
                        "non-public interfaces from different packages");
                }
            }
        }
        
        if (proxyPkg == null) {
            // 如果没有非public接口,则使用默认包名
            // Java默认使用“com.sun.proxy.”,Android默认为空
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }
        // 生成代理类的序号
        long num = nextUniqueNumber.getAndIncrement();
        // 生成代理类的全限定名:包名+前缀+序号,例如:com.sun.proxy.$Proxy0
        String proxyName = proxyPkg + proxyClassNamePrefix + num;
        // 调用ProxyGenerator.generateProxyClass生成字节码
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);

        try {
            // 根据二进制文件生成相应的Class实例
            return Proxy.defineClass0(loader, proxyName, proxyClassFile, 0, 
                proxyClassFile.length);
        } catch (ClassFormatError e) {
            throw new IllegalArgumentException(e.toString());
        }
    }
}

ProxyClassFactory首先遍历接口数组,一一进行校验,主要针对以下三个方面,如果校验失败,则抛出异常:

  1. 接口是否能由ClassLoader加载;
  2. 接口是否确实是个接口;
  3. 接口是否重复

接着,生成代理类的全限定名,其结构为:包名+前缀+序号

  • 包名
    • 当接口均为public时,包名默认为com.sun.proxy.,访问标志为public final
    • 当有接口为non-public时,代理类的包名与该接口的包名相同,访问标志为final。注意,所有的non-public接口需在同一个包下面,否则抛出异常
  • 前缀:默认为$Proxy
  • 序号:由原子类生成,确保代理类的唯一性

确定了全限定名与访问标志之后,调用ProxyGenerator.generateProxyClass生成字节码文件,最后再根据字节码生成Class实例。

ProxyGenerator

最后,我们来看下,ProxyGenerator是怎么生成字节码文件的。

public static byte[] generateProxyClass(final String name,
                                        Class<?>[] interfaces,
                                        int accessFlags)
{
    // 创建ProxyGenerator实例对象
    ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
    // 调用generateClassFile生成字节码
    final byte[] classFile = gen.generateClassFile();
    // 省略保存字节码文件的步骤
    ...
    return classFile;
}

ProxyGenerator的静态方法generateProxyClass()就是初始化一个ProxyGenerator实例,然后调用它的generateClassFile()生成字节码,看下generateClassFile()怎么生成字节码的。
private byte[] generateClassFile() {
    // Step 1: 将所有方法封装为ProxyMethod对象
    
    // 首先封装Object的三个方法,Java所有类都继承自Object
    addProxyMethod(hashCodeMethod, Object.class);
    addProxyMethod(equalsMethod, Object.class);
    addProxyMethod(toStringMethod, Object.class);
    
    // 接着封装所有接口的方法
    for (Class<?> intf : interfaces) {
        for (Method m : intf.getMethods()) {
            addProxyMethod(m, intf);
        }
    }
    
    // 对于具有相同签名的代理方法,检验方法的返回值是否兼容
    for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
        checkReturnTypes(sigmethods);
    }
    
    // Step 2: 将所有字段和方法封装成FiledInfo对象和MethodInfo对象
    try {
        // 2.1: 生成带参构造器,并添加到methods集合中
        methods.add(generateConstructor());
        
        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            for (ProxyMethod pm : sigmethods) {
                // 2.2: 为代理方法添加静态字段,例如:private static Method m1;
                fields.add(new FieldInfo(pm.methodFieldName, 
                            "Ljava/lang/reflect/Method;",
                            ACC_PRIVATE | ACC_STATIC));

                //2.3: 为代理方法生成代码并添加到methods中
                methods.add(pm.generateMethod());
            }
        }
        // 2.4: 添加代理类的静态字段初始化方法
        methods.add(generateStaticInitializer());
    } catch (IOException e) {
        throw new InternalError("unexpected I/O Exception", e);
    }
    // 检查方法数、字段数,不允许超过65535
    if (methods.size() > 65535) {
        throw new IllegalArgumentException("method limit exceeded");
    }
    if (fields.size() > 65535) {
        throw new IllegalArgumentException("field limit exceeded");
    }

    // Step 3: 写入最终的class文件
    
    // 验证常量池中有代理类的全限定名
    cp.getClass(dotToSplash(className));
    // 验证常量池中有父类的全限定名,父类名为:"java/lang/reflect/Proxy"
    cp.getClass(superclassName);
    // 验证常量池中存在各个接口的全限定名
    for (Class<?> intf: interfaces) {
        cp.getClass(dotToSplash(intf.getName()));
    }
    
    // 接下来要开始写入文件了,将常量池设为只读
    cp.setReadOnly();
    
    ByteArrayOutputStream bout = new ByteArrayOutputStream();
    DataOutputStream dout = new DataOutputStream(bout);

    try {
        // 写入类文件的各个部分的数据
        // 1. 写入魔数, u4
        dout.writeInt(0xCAFEBABE);
        // 2. 写入次版本号, u2
        dout.writeShort(CLASSFILE_MINOR_VERSION);
        // 3. 写入主版本号,u2
        dout.writeShort(CLASSFILE_MAJOR_VERSION);
        // 4. 写入常量池
        cp.write(dout);
        // 5. 写入访问标志,u2
        dout.writeShort(accessFlags);
        // 6. 写入代理类索引,u2
        dout.writeShort(cp.getClass(dotToSlash(className)));
        // 7. 写入父类索引,u2
        dout.writeShort(cp.getClass(superclassName));
        // 8. 写入接口数,u2
        dout.writeShort(interfaces.length);
        // 9. 写入接口索引,u2 * 接口数
        for (Class<?> intf : interfaces) {
            dout.writeShort(cp.getClass(
                dotToSlash(intf.getName())));
        }
        // 10. 写入字段数,u2
        dout.writeShort(fields.size());
        // 11. 写入字段集合
        for (FieldInfo f : fields) {
            f.write(dout);
        }
        // 12. 写入方法数,u2
        dout.writeShort(methods.size());
        // 13. 写入方法集合
        for (MethodInfo m : methods) {
            m.write(dout);
        }
        // 14. 写入属性数,代理类class文件没有属性,故为0
        dout.writeShort(0);
    } catch (IOException e) {
        throw new InternalError("unexpected I/O Exception", e)
    }
    // 转换成二进制数组输出
    return bout.toByteArray();
}

可以看到generateClassFile()方法是按照Class文件结构进行动态拼接的,生成Class文件主要分为3步:

  1. 收集所有要生成的代理方法,将其包装成ProxyMethod对象,并添加到map集合中
  2. 收集所有要为Class文件生成的字段信息和方法信息
  3. 开始组装Class文件

我们知道一个类的核心部分就是它的字段和方法,因此我们重点来看下第二步,看看它为代理类生成了哪些字段和方法。在第二步中,按顺序做了下面四件事:

  1. 为代理类生成一个带参构造器,传入InvocationHandler实例的引用并调用父类的带参构造器
  2. 遍历代理方法Map集合,为每个代理方法生成对应的Method类型静态域,并将其添加到fields集合中
  3. 遍历代理方法Map集合,为每个代理方法声称对应的MethodInfo对象,并将其添加到methods集合中
  4. 为代理类生成静态初始方法,该静态初始方法主要是将每个代理方法的引用赋值给对应的静态字段

通过以上分析,我们大致可以知道动态代理最终会为我们生成如下结构的代理类:

public class Proxy0 extends Proxy implements Image {
    // 第一步,生成构造器
    public Proxy0(InvocationHandler h) {
        super(h);
    }
    
    // 第二步,生成静态字段
    private static Method m1;    // hashCode方法
    private static Method m2;    // equals方法
    private static Method m3;    // toString方法
    private static Method m4;    // display方法
    
    // 第三步,生成代理方法
    @Override
    public int hashCode() {
        try {
            return (int) h.invoke(this, m1, null);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public boolean equals(Object obj) {
        try {
            Object[] args = new Object[] {obj};
            return (boolean) h.invoke(this, m2, args2);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public String toString() {
        try {
            return (String) h.invoke(this, m3, null);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public void display() {
        try {
            h.invoke(this, m4, null);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    // 第四步,生成静态初始化方法
    static {
        try {
            Class c1 = Class.forName(Object.class.getName());
            Class c2 = Class.forName(Image.class.getName());
            m1 = c1.getMethod("hashCode", null);
            m2 = c1.getMethod("equals", new Class[]{Object.class});
            m3 = c1.getMethod("toString", null);
            m4 = c2.getMethod("display", null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

至此,经过层层分析,深入探究JDK源码,我们还原了动态生成代理类的真面目,一些疑问也得到了很好的解答:

  1. 代理类默认继承Proxy类,因为Java中只支持单继承,因此JDK动态代理只能实现接口
  2. 代理方法都会去调用InvocationHandlerinvoke()方法,因此我们需要重写InvocationHandler.invoke()方法
  3. 调用invoke()方法时会传入代理实例本身、目标方法和目标方法参数,解释了invoke()方法的参数是怎么来的。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 转自https://blog.csdn.net/briblue/article/details/73928350前...
    扎Zn了老Fe阅读 305评论 1 0
  • Java动态代理 引言 最近在看AOP代码,其中利用到了Java动态代理机制来实现AOP织入。所以好好地把Java...
    草捏子阅读 1,527评论 0 18
  • 相关概念1.1 代理  在某些情况下,我们不希望或是不能直接访问对象 A,而是通过访问一个中介对象 B,由 B 去...
    天空在微笑阅读 430评论 0 0
  • 前言 本文是我在学习代理模式时的一篇笔记,除了对代理模式、静态和动态代理的概念和实现进行了描述外,还有关于动态代理...
  • 今晚的月光很明朗 空气里氤氲着芳香 你喜欢的凉爽 屋子里炉火也旺 咖啡也烫 你为何不回家 流浪啊 流浪 我爱月光的...
    深夜狂想曲阅读 213评论 0 1