JDK进阶(7):动态代理

理解JDK动态代理

  1. Java 动态代理机制,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。
  2. 代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架。
  3. 在JDK的API中,InvocationHandler 就是这一组接口,而Proxy负责动态生成代理类

Proxy类 与 InvocationHandler 接口

Proxy类
  1. Proxy:是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象
  2. Proxy 的静态方法:
// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
public static InvocationHandler getInvocationHandler(Object proxy)

// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)

// 方法 3:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

// 方法 4:该方法用于判断指定类对象是否是一个动态代理类
public static boolean isProxyClass(Class<?> cl)
InvocationHandler 接口
  1. 这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问
  2. 方法
// 该方法负责集中处理动态代理类上的所有方法调用。
// 第一个参数是代理类实例
// 第二个参数是被调用的方法对象
// 第三个方法是调用参数。
// 调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
Object invoke(Object proxy, Method method, Object[] args)
使用 Java 动态代理
  1. 步骤如下
  • 通过实现 InvocationHandler 接口创建自己的调用处理器
  • 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类
  • 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型
  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入
  1. 动态代理对象创建过程实现如下
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(..); 
 
// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); 
 
// 通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); 
 
// 通过构造函数对象创建动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
  1. 实际使用中,会使用newProxyInstance() 代替 getProxyClass 方法
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(..); 
 
// 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler );
  1. 示例:实现一个 ArrayList 的动态代理
static void test() {
    // 使用 Guava的Lists工具定义一个集合
    ArrayList<String> target = Lists.newArrayList();

    // 通过 Proxy.newProxyInstance 方法直接创建动态代理类实例
    // InvocationHandler,使用内部匿名类实现
    Collection collectionProxy = (Collection) Proxy.newProxyInstance(Collection.class.getClassLoader(), new Class[]{Collection.class}, new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("proxy.getClass().getName(): " + proxy.getClass().getName());
            System.out.println("method.getName(): " + method.getName());

            if (Objects.isNull(args)) {
                System.out.println("args: " + Arrays.toString(args));
            }
            Object object = method.invoke(target, args);
            return object;
        }
    });

    collectionProxy.add("你好,代理");

}

输出:
proxy.getClass().getName(): com.sun.proxy.$Proxy0
method.getName(): add
  1. Guava 实现,更加简单
static void test2() {
    // 使用 Guava的Lists工具定义一个集合
    ArrayList<Object> list = Lists.newArrayList();
    
    // 使用 Guava 的 Reflection 工具
    Collection collection = Reflection.newProxy(Collection.class, (proxy, method, args) -> {
        System.out.println("*********** Before invoke *****************");
        
        Object object = method.invoke(list, args);
        
        System.out.println("*********** After invoke *****************");
        return object;
    });

    collection.add("Guava Proxy");
}

深入理解动态代理(JDK8)

动态代理实现源码分析(Proxy源码)
  1. 先看几个变量
// 代理类构造函数的参数
private static final Class<?>[] constructorParams = { InvocationHandler.class };
    
// 存放代理类Class的缓存
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
    proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

// KeyFactory 根据接口的数量,映射一个最佳的key生成函数,其中表示接口的类对象被弱引用;也就是key对象被弱引用继承自 WeakReference(key0、key1、key2、keyX),保存接口密钥(hash值)
private static final class KeyFactory
    implements BiFunction<ClassLoader, Class<?>[], Object>{}

// 代理类实例关联的调用处理器
protected InvocationHandler h;
  1. 分析 newProxyInstance 方法
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        ...

        /*
         * 获得与制定类装载器和一组接口相关的代理类类型对象
         * getProxyClass() 方法内部,也是调用 getProxyClass0() 方法
         */
        Class<?> cl = getProxyClass0(loader, intfs);


        // 通过反射获取构造函数对象并生成代理类实例
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        
        ...
        
        // 反射实例化
        return cons.newInstance(new Object[]{h});
    }
  1. 最核心的 getProxyClass0 方法
// 生成代理类,调用前必须进行 checkProxyAccess 权限检查,所以newProxyInstance进行了权限检查
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
    //实现接口的最大数量 < 65535 
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }
    
    // 如果缓存中有,就直接返回,否则会生成
    return proxyClassCache.get(loader, interfaces);
}
  1. roxyClassCache.get 方法
//  //key:类加载器;parameter:接口数组
public V get(K key, P parameter) {
   
    Objects.requireNonNull(parameter);
    
    //清除已经被 GC 回收的弱引用
    expungeStaleEntries();

    //CacheKey 弱引用类,refQueue 已经被回收的弱引用队列;构建一个CacheKey
    Object cacheKey = CacheKey.valueOf(key, refQueue);
    
    //map一级缓存,获取 valuesMap 二级缓存
    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;
        }
    }

    // subKeyFactory类型是KeyFactory,apply返回表示接口的key
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    
    //Factory 实现了supplier,我们实际是获取缓存中的Factory,调用其get方法
    Supplier<V> supplier = valuesMap.get(subKey);
    Factory factory = null;
    
    //下面用到了 CAS + 重试 实现的多线程安全的 非阻塞算法(Lock Free)
    while (true) {
        if (supplier != null) {
            // 只需要知道,最终会调用get方法,此supplier可能是缓存中取出来的,也可能是Factory新new出来的
            V value = supplier.get();
            if (value != null) {
                return value;
            }
        }
        // else no supplier in cache
        // or a supplier that returned null (could be a cleared CacheValue
        // or a Factory that wasn't successful in installing the CacheValue)

        // lazily construct a Factory
        if (factory == null) {
            factory = new Factory(key, parameter, subKey, valuesMap);
        }

        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)) {
                // successfully replaced
                // cleared CacheEntry / unsuccessful Factory
                // with our Factory
                supplier = factory;
            } else {
                // retry with current supplier
                supplier = valuesMap.get(subKey);
            }
        }
    }
}
  1. ProxyClassFactory.apply 方法
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

    ...
    
    /*
     * Generate the specified proxy class.
     * 生成类字节码的方法:重点
     */
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags);
    try {
        return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
    } catch (ClassFormatError e) {
            /*
             * A ClassFormatError here means that (barring bugs in the
             * proxy class generation code) there was some other
             * invalid aspect of the arguments supplied to the proxy
             * class creation (such as virtual machine limitations
             * exceeded).
             */
        throw new IllegalArgumentException(e.toString());
    }
}
  1. ProxyGenerator.generateProxyClass 方法生成字节码
public static byte[] generateProxyClass(final String name, Class<?>[] interfaces, int accessFlags) {
    ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
    
    //真正生成字节码的方法
    final byte[] classFile = gen.generateClassFile();
    
    //如果saveGeneratedFiles为true 则生成字节码文件,所以在开始我们要设置这个参数
    //当然,也可以通过返回的bytes自己输出
    if (saveGeneratedFiles) {
        java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() {
                    public Void run() {
                        try {
                            int i = name.lastIndexOf('.');
                            Path path;
                            if (i > 0) {
                                Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
                                Files.createDirectories(dir);
                                path = dir.resolve(name.substring(i+1, name.length()) + ".class");
                            } else {
                                path = Paths.get(name + ".class");
                            }
                            Files.write(path, classFile);
                            return null;
                        } catch (IOException e) {
                            throw new InternalError( "I/O exception saving generated file: " + e);
                        }
                    }
                });
    }
    return classFile;
}
  1. ProxyGenerator.generateClassFile 方法
private byte[] generateClassFile() {
        /* ============================================================
         * Step 1: Assemble ProxyMethod objects for all methods to generate proxy dispatching code for.
         * 步骤1:为所有方法生成代理调度代码,将代理方法对象集合起来。
         */
        //增加 hashcode、equals、toString方法
        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: Assemble FieldInfo and MethodInfo structs for all of fields and methods in the class we are generating.
         * 为类中的方法生成字段信息和方法信息
         */
        try {
            //增加构造方法
            methods.add(generateConstructor());
            for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
                for (ProxyMethod pm : sigmethods) {
                    // add static field for method's Method object
                    fields.add(new FieldInfo(pm.methodFieldName,
                            "Ljava/lang/reflect/Method;",
                            ACC_PRIVATE | ACC_STATIC));
                    // generate code for proxy method and add it
                    methods.add(pm.generateMethod());
                }
            }
            //增加静态初始化信息
            methods.add(generateStaticInitializer());
        } catch (IOException e) {
            throw new InternalError("unexpected I/O Exception", e);
        }

        if (methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        }
        if (fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        }

        /* ============================================================
         * Step 3: Write the final class file.
         * 步骤3:编写最终类文件
         */
        /*
         * Make sure that constant pool indexes are reserved for the following items before starting to write the final class file.
         * 在开始编写最终类文件之前,确保为下面的项目保留常量池索引。
         */
        cp.getClass(dotToSlash(className));
        cp.getClass(superclassName);
        for (Class<?> intf: interfaces) {
            cp.getClass(dotToSlash(intf.getName()));
        }

        /*
         * Disallow new constant pool additions beyond this point, since we are about to write the final constant pool table.
         * 设置只读,在这之前不允许在常量池中增加信息,因为要写常量池表
         */
        cp.setReadOnly();

        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        DataOutputStream dout = new DataOutputStream(bout);

        try {
            // u4 magic;
            dout.writeInt(0xCAFEBABE);
            // u2 次要版本;
            dout.writeShort(CLASSFILE_MINOR_VERSION);
            // u2 主版本
            dout.writeShort(CLASSFILE_MAJOR_VERSION);

            cp.write(dout);             // (write constant pool)

            // u2 访问标识;
            dout.writeShort(accessFlags);
            // u2 本类名;
            dout.writeShort(cp.getClass(dotToSlash(className)));
            // u2 父类名;
            dout.writeShort(cp.getClass(superclassName));
            // u2 接口;
            dout.writeShort(interfaces.length);
            // u2 interfaces[interfaces_count];
            for (Class<?> intf : interfaces) {
                dout.writeShort(cp.getClass(
                        dotToSlash(intf.getName())));
            }
            // u2 字段;
            dout.writeShort(fields.size());
            // field_info fields[fields_count];
            for (FieldInfo f : fields) {
                f.write(dout);
            }
            // u2 方法;
            dout.writeShort(methods.size());
            // method_info methods[methods_count];
            for (MethodInfo m : methods) {
                m.write(dout);
            }
            // u2 类文件属性:对于代理类来说没有类文件属性;
            dout.writeShort(0); // (no ClassFile attributes for proxy classes)

        } catch (IOException e) {
            throw new InternalError("unexpected I/O Exception", e);
        }

        return bout.toByteArray();
    }
生成的动态代理类分析
  1. 通过分析 Collection 的代理类实现,分析其内部方法实现
  2. 动态代理类构造函数只有一个:$Proxy0(java.lang.reflect.InvocationHandler)
  3. 方法可以分为三类:
  • 继承自 Object基类,如hashCode()、equals(java.lang.Object)、toString()
  • 实现自 Collection 接口,如:add(java.lang.Object)、clear()、isEmpty()、size()
  • 继承自 Proxy 类,如:getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)
  1. 实例代码如下:
Class<?> proxyClass = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);

System.out.println("----------1、查看动态代理类的构造方法 constructors-------------");

Constructor<?>[] constructors = proxyClass.getConstructors();
for (Constructor<?> constructor : constructors) {
    String name = constructor.getName();
    System.out.println("代理类的构造方法的名字:  " + name);


    // 拼参数列表
    StringBuilder tBuilder = new StringBuilder(name);
    tBuilder.append('(');
    // 构造方法的参数
    Class<?>[] clazzParams = constructor.getParameterTypes();
    for (Class<?> clazzParam : clazzParams) {
        // 构造方法的参数
        System.out.println("构造方法参数的类型:  " + clazzParam.getName());
        tBuilder.append(clazzParam.getName()).append(',');
    }

    // 有参数的时候
    if (clazzParams != null && clazzParams.length != 0) {
        // 去掉最后一个逗号
        tBuilder.deleteCharAt(tBuilder.length() - 1);
    }
    tBuilder.append(')');
    System.out.println(tBuilder);
}
/*
 输出:
 代理类的构造方法的名字:  com.sun.proxy.$Proxy0
 构造方法参数的类型:  java.lang.reflect.InvocationHandler
 com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
*/

System.out.println("----------2、动态代理类的方法以及参数-------------");

//利用反射得到所有的方法
Method[] methods = proxyClass.getMethods();
for (Method method : methods) {
    String name = method.getName();
    StringBuilder tBuilder = new StringBuilder(name);
    tBuilder.append('(');
    //得到方法的参数类型
    Class<?>[] clazzParams = method.getParameterTypes();
    for (Class<?> clazzParam : clazzParams) {
        tBuilder.append(clazzParam.getName()).append(',');
    }

    if (clazzParams != null && clazzParams.length != 0) {
        tBuilder.deleteCharAt(tBuilder.length() - 1);
    }
    tBuilder.append(')');
    System.out.println(tBuilder);
}

输出:

----------1、查看动态代理类的构造方法 constructors-------------
代理类的构造方法的名字:  com.sun.proxy.$Proxy0
构造方法参数的类型:  java.lang.reflect.InvocationHandler
com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)

----------2、动态代理类的方法以及参数-------------
add(java.lang.Object)
remove(java.lang.Object)
equals(java.lang.Object)
toString()
hashCode()
clear()
contains(java.lang.Object)
isEmpty()
iterator()
size()
toArray([Ljava.lang.Object;)
toArray()
spliterator()
addAll(java.util.Collection)
stream()
forEach(java.util.function.Consumer)
containsAll(java.util.Collection)
removeAll(java.util.Collection)
removeIf(java.util.function.Predicate)
retainAll(java.util.Collection)
parallelStream()
isProxyClass(java.lang.Class)
newProxyInstance(java.lang.ClassLoader,[Ljava.lang.Class;,java.lang.reflect.InvocationHandler)
getProxyClass(java.lang.ClassLoader,[Ljava.lang.Class;)
getInvocationHandler(java.lang.Object)
wait()
wait(long,int)
wait(long)
getClass()
notify()
notifyAll()

动态代理实现AOP

  1. 目标对象,被代理的对象:Target
public interface Target {

    /**
     * 方法
     */
    void first();
}

class CustomTarget implements Target {

    @Override
    public void first() {
        System.out.println("方法 first 被调用了");
    }

}
  1. 通知:Advice
public interface Advice {

    /**
     * 方法前调用
     *
     * @param method
     */
    void before(Method method);

    /**
     * 方法后调用
     *
     * @param method
     */
    void after(Method method);

    /**
     * 异常
     * @param method
     */
    void afterThrow(Method method);

    /**
     * Finally 处理
     * @param method
     */
    void afterFinally(Method method);
}

class CustomAdvice implements Advice {
    private long beginTime = 0;

    @Override
    public void before(Method method) {
        System.out.println("~~~ 在方法调用之前 ~~~");
        beginTime = System.currentTimeMillis();
    }

    @Override
    public void after(Method method) {
        System.out.println("~~~ 方法已经调用结束啦 ~~~");
        long endTime = System.currentTimeMillis();
        System.out.println(method.getName() + " 方法一共运行的时间为 " + (endTime - beginTime));
    }

    @Override
    public void afterThrow(Method method) {

    }

    @Override
    public void afterFinally(Method method) {

    }
}
  1. 动态代理工具类
public class ProxyUtil {

    /**
     * 得到代理对象
     *
     * @param target
     * @param handle
     * @return
     */
    public static Object getProxy(Object target, InvocationHandler handle) {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), handle);

    }

    /**
     * 得到代理对象
     *
     * @param target
     * @param advice
     * @return
     */
    public static Object getProxy(final Object target, final Advice advice) {

        Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                (proxy1, method, args) -> {
                    advice.before(method);
                    Object object = method.invoke(target, args);
                    advice.after(method);
                    return object;
                });

        return proxy;

    }
}

@Setter
@Getter
class CustomInvocationHandler implements InvocationHandler {

    /**
     * 切面对象
     */
    private Advice advice;
    /**
     * 被代理对象,即目标属性
     */
    private Object target;


    public CustomInvocationHandler(Advice advice, Object target) {
        this.advice = advice;
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        advice.before(method);

        Object obj = method.invoke(target, args);

        advice.after(method);

        return obj;
    }
}
  1. 测试
Target target = new CustomTarget();
Advice advice = new CustomAdvice();
CustomInvocationHandler invocationHandler = new CustomInvocationHandler(advice, target);

Target proxy = (Target) ProxyUtil.getProxy(target, invocationHandler);
proxy.first();

输出:
在什么时候被调用
~~~ 在方法调用之前 ~~~
方法 first 被调用了
~~~ 方法已经调用结束啦 ~~~
first 方法一共运行的时间为 1

简单理解CgLib动态代理

  1. JDK 的 Proxy,无法摆脱仅支持 interface 代理的桎梏,这就需要cglib来实现
  2. cglib最重要EnhancerMethodInterceptor,使用与Proxy类似
  3. 简单看个示例
public static Object getCgProxy(final Object targrt, final Advice advice) {

    //利用cglib 中的Enhancer
    Enhancer enhancer = new Enhancer();

    //把目标类设置为代理的父类(继承目标类,覆盖其所有非final的方法)
    enhancer.setSuperclass(targrt.getClass());

    //设置回调, //实现MethodInterceptor 接口
    enhancer.setCallback(new MethodInterceptor() {

        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            Object object = null;
            try {
                //前置
                advice.before(method);
                object = methodProxy.invoke(targrt, args);
                //后置
                advice.after(method);
            } catch (Exception e) {
                //例外,异常
                advice.afterThrow(method);
            } finally {
                //最终
                advice.afterFinally(method);
            }

            return object;
        }
    });

    return enhancer.create();
}

参考

  1. 源码地址:Java 进阶
  2. java进阶(七):详解JAVA代理
  3. Java 动态代理机制分析及扩展,第 1 部分
  4. java 1.8 动态代理源码分析

Fork me on Gitee

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值