JDK动态代理

发布时间 2023-12-07 17:23:22作者: FynnWang

JDK动态代理

  • 被代理接口
public interface ClothesFactory {
    void makeClothes();
}
  • 被代理接口实现
public class ClothesFactoryImpl implements ClothesFactory {

    @Override
    public void makeClothes() {
        System.out.println("I make clothes!");
    }
}
  • 获取代理类
public Object getProxyInstance(ClothesFactory factory) {
    //获取被代理类的1.类加载器 2.接口类 3.InvocationHandler实现类的invoke方法。通过反射
    return Proxy.newProxyInstance(factory.getClass().getClassLoader(), factory.getClass().getInterfaces(), new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("fire");
            method.invoke(factory, args);
            return null;
        }
    });
}
  • 测试
@Test
public void proxyTest() {
    ClothesFactoryImpl factory = new ClothesFactoryImpl();
    ClothesFactory proxyInstance = (ClothesFactory) getProxyInstance(factory);
    proxyInstance.makeClothes();
}
// fire
// I make clothes!

小结

  • 直观的感受就是先获取到了实际被代理接口的method,将method作为参数传入到invoke方法,然后在method.invoke前面和后面写逻辑

疑问

  1. Proxy.newProxyInstance是如何返回一个被代理接口的实例?
  2. 代理实现中如何获取到了被代理接口的method

原理

  • java.lang.reflect.Proxy.ProxyClassFactoryjava.lang.reflect.Proxy的内部类
private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
            ......
            /*
            * 通过方法传入代理类的全限定名, 被代理接口, 访问标识 如public
            * 生成一个.class文件, 反编译之后是一个继承自Proxy, 并实现被代理接口的类, 增强方法InvocationHandler做为其中的成员域
            */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                /*
                * native方法, 执行类加载机制, 返回这个java.lang.Class对象, 最后通过newInstance来创建代理对象, InvocationHandler对象作为构造函数的
                * 参数
                */
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
                ......
        }
}
  • 自建测试
@Test
public void defineClass() throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException, IOException 
{
    Method defineClass0 = Proxy.class.getDeclaredMethod("defineClass0", new Class[]{ClassLoader.class, String.class, byte[].class, int.class, int.class});
    Constructor<Proxy> declaredConstructor = Proxy.class.getDeclaredConstructor();
    declaredConstructor.setAccessible(true);
    String proxyName = "com.wangfan.proxy.MyProxy";
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, new Class[]{ClothesFactory.class}, Modifier.PUBLIC);
    /**
    * 将proxyClassFile输出到.class文件
    */
    outPutToFile(proxyClassFile);

    /**
    * JVM类加载, 获取了代理类的java.lang.Class对象, 继承了Proxy, 实现了ClothesFactory接口
    */
    Class proxyClass = (Class) defineClass0.invoke(declaredConstructor.newInstance(), ClothesFactory.class.getClassLoader(), proxyName, proxyClassFile, 0, proxyClassFile.length);
    //System.out.println(JSON.toJSONString(invoke.getInterfaces()));

    /**
    * 通过反射获取构造代理类的构造函数,这个构造函数需要一个InvocationHandler对象作为参数
    */
    ClothesFactoryImpl clothesFactory = new ClothesFactoryImpl();
    Constructor constructor = proxyClass.getConstructor(new Class[]{InvocationHandler.class});
    constructor.setAccessible(true);
    ClothesFactory factory = (ClothesFactory) constructor.newInstance(new Object[]{(InvocationHandler) (proxy, method, args) -> {
        System.out.println("fire");
        method.invoke(clothesFactory, args);
        return null;
    }});

    /**
    * 执行业务方法
    */
    factory.makeShoes();
}
	
/*
* 输出到文件
*/
private void outPutToFile(byte[] proxyClassFile) throws IOException {
    String filepath = "D:\\Code\\Study\\MyProxy.class";
    File file = new File(filepath);
    if (file.exists()) {
        file.delete();
    }
    FileOutputStream fos = new FileOutputStream(file);
    fos.write(proxyClassFile, 0, proxyClassFile.length);
    fos.flush();
    fos.close();
}
  • MyProxy.class反编译
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.wf.proxy;

import com.wf.proxy.jdk.ClothesFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public class MyProxy extends Proxy implements ClothesFactory {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m4;
    private static Method m0;

    public MyProxy(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 makeShoes() 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 void makeClothes() throws  {
        try {
            super.h.invoke(this, m4, (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.wf.proxy.jdk.ClothesFactory").getMethod("makeShoes");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.wf.proxy.jdk.ClothesFactory").getMethod("makeClothes");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

总结

  • JDK动态代理实际上是通过根据代理类全限定名和被代理接口,生成一个byte[]类型的proxyClassFile文件,然后再通过本地方法defineClass0加载类到JVM中,classLoader与被代理接口用的同一个AppClassLoader,然后InvocationHandler对象是作为代理类的成员域,通过构造函数参数传入
  • 当代理类调用被代理接口中的方法的时候,最终调用到的是InvocationHandler对象的invoke方法,方法的参数包含被代理接口的方法