动态代理原理

动态代理是什么?

动态代理是在程序运行的过程中动态生成代理类的字节码文件,然后把字节码文件加载到方法区,生成一个具体的class对象,然后反射拿到构造方法进行实例化,获取代理类

JDK动态代理实现

首先需要一个接口

public interface MyCalculator {
    void sout();
}

然后写一个普通类实现接口

public class Calculator implements MyCalculator {
    @Override
    public void sout() {
        System.out.println("普通计算器!");
    }
}

接着写一个获取代理类的方法

public class CalculatorProxy {

    public static MyCalculator getProcy( Calculator calculator) {
        //获取类加载器
        ClassLoader classLoader = calculator.getClass().getClassLoader();
		
		//获取实现的接口
        Class<?>[] interfaces = calculator.getClass().getInterfaces();
		//包裹原来方法的增强
        InvocationHandler h = new InvocationHandler() {
            @Override
            //invoke方法是对原来的方法包裹并且可以在前后进行增强
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                System.out.println("前面加强");
                //执行原本的方法
                result = method.invoke(calculator, args);
                System.out.println("后面加强");
                return result;
            }
        };
        Object o = Proxy.newProxyInstance(classLoader, interfaces, h);

        return (MyCalculator) o;

    }

}

进行测试

    public static void main(String[] args) {
        MyCalculator procy = CalculatorProxy.getProcy(new Calculator());
        procy.sout();
    }

在这里插入图片描述

JDK动态代理源码解析——代理类怎么生成的?

		//关键点 获取代理对象
        Object o = Proxy.newProxyInstance(classLoader, interfaces, h);

点进newProxyInstance方法

//获取代理类对象
Class<?> cl = getProxyClass0(loader, intfs);
    private static Class<?> getProxyClass0(ClassLoader loader,
       
                                           Class<?>... interfaces) {
        //代理的类接口最多实现65535个                                   
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        //proxyClassCache.get(loader, interfaces);这行代码中,其实主要就是调用了WeakCache 的get方法
		// WeakCache是一个二级缓存实现。其存储方式主要使用ConcurrentMap,从WeakCache缓存获取的时候,需要两个参数,class loader 和 interface 数组
        return proxyClassCache.get(loader, interfaces);
    }

get方法本质是从二级缓存里获取代理对象

//缓存的底层实现, key为一级缓存, value为二级缓存。 为了支持null, map的key类型设置为Object
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>>  map = new ConcurrentHashMap<>();
  • 一级缓存 key:classLoader ( value:二级缓存 key:(classLoader 和 接口 通过规则生成二级缓存的key) value:Supplier)
    二级缓存 key:(classLoader 和 接口 通过规则生成二级缓存的key) value:supplier有可能是一个Factory,或者是一个CacheValue[生成代理对象之后将其包装成CacheValue放入二级缓存的value中并且将没有包装的value直接返回。而因为将生成的结果放入二级缓存当中,如果下次获取或者其他线程来获取的时候就可以直接得到一个CacheValue实例,再通过get就能获取到代理对象。]

  • 一级缓存的key是弱引用,二级缓存是强引用。其中key是根据入参直接传入的,二级缓存的key和value都是是根据一级缓存的key和value通过各自的工厂方法(subKeyFactory和valueFactory)计算得到的。当弱引用被clear后,entries会被以惰性(lazily)方式被删除(在调用get方法后,第二行代码)。
    作用:使用弱引用做一级key,二级缓存中cachevalue也是弱引用,解决内存泄露问题

get方法源码解析

public V get(K key, P parameter) {
    //这里要求实现的接口不能为空
    Objects.requireNonNull(parameter);
    //清除过期的缓存(依靠弱引用机制),WeakCache的所有方法调用都会先进行这一步处理
    expungeStaleEntries();
    //将ClassLoader包装成CacheKey, 作为一级缓存的key
    Object cacheKey = CacheKey.valueOf(key, refQueue);
    //获取得到二级缓存
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    //如果根据ClassLoader没有获取到对应的值
    if (valuesMap == null) {
        //以CAS方式放入, 如果不存在则放入,否则返回原先的值
        ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, 
                valuesMap = new ConcurrentHashMap<>());
        //如果oldValuesMap有值, 说明放入失败
        if (oldValuesMap != null) {
            valuesMap = oldValuesMap;
        }
    }
    //根据代理类实现的接口数组来生成二级缓存key, 分为key0, key1, key2, keyx
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    //这里通过subKey获取到二级缓存的值
    Supplier<V> supplier = valuesMap.get(subKey);
    Factory factory = null;
    //这个循环提供了轮询机制, 如果条件为假就继续重试直到条件为真为止
    while (true) {
        //如果通过subKey取出来的值不为空
        if (supplier != null) {
            //在这里supplier可能是一个Factory也可能会是一个CacheValue
            //在这里不作判断, 而是在Supplier实现类的get方法里面进行验证
            V value = supplier.get();
            if (value != null) {
                return value;
            }
        }
        if (factory == null) {
            //新建一个Factory实例作为subKey对应的值
            factory = new Factory(key, parameter, subKey, valuesMap);
        }
        if (supplier == null) {
            //到这里表明subKey没有对应的值, 就将factory作为subKey的值放入
            supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                //到这里表明成功将factory放入缓存
                supplier = factory;
            }
            //否则, 可能期间有其他线程修改了值, 那么就不再继续给subKey赋值, 而是取出来直接用
        } else {
            //期间可能其他线程修改了值, 那么就将原先的值替换
            if (valuesMap.replace(subKey, supplier, factory)) {
                //成功将factory替换成新的值
                supplier = factory;
            } else {
                //替换失败, 继续使用原先的值
                supplier = valuesMap.get(subKey);
            }
        }
    }
}

get方法里做了什么?

  • 首先清除过期的缓存(依靠弱引用机制),防止内存泄露
  • 将ClassLoader包装成CacheKey, 作为一级缓存的key
  • map.get(cacheKey)获得二级缓存,如果为null则以CAS方式放入(不存在则放入),否则返回存在的值
  • 根据class loader 和 interface 数组按照一定的规则生成二级缓存subKey
  • valuesMap.get(subKey)获得二级缓存的value
  • 从缓存中获取value,当缓存获取不到值得时候,首先会实例化一个Factory 放入二级缓存的value中,等再次获取或者其他线程同时获取的时候,就可以得到一个Factory实例
  • 然后执行 V value = supplier.get(); 其中supplier就是二级缓存的value,它有可能是一个Factory,也可能是一个CacheValue
  • 如果二级缓存的value是Factory实例,通过其的get方法可以生成代理对象返回(Factory这个内部工厂类,可以看到它的get方法是使用synchronized关键字进行了同步),生成代理对象之后将其包装成CacheValue放入二级缓存的value中并且将没有包装的value直接返回。因为将生成的结果放入二级缓存当中,所以下次获取或者其他线程来获取的时候就可以直接得到一个CacheValue实例,再通过get就能获取到代理对象
//获取代理类对象构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
//通过代理类的构造器反射出代理对象
 return cons.newInstance(new Object[]{h});

JDK代理类方法如何调用的被代理类方法?

代理类源码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import JDKproxy.MyCalculator;
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 MyCalculator {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    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 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 sout() throws  {
        try {
		/**
 		*super 就是代表proxy类,类持有一个protected InvocationHandler h;属性
        *这个属性是我们在构造代理类的时候传入的:
        *Object o = Proxy.newProxyInstance(classLoader, interfaces, h);
        *h的invoke方法里就包含了我们的增强逻辑以及原始方法的调用
		*/
            super.h.invoke(this, m3, (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"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            //我们的测试方法
            m3 = Class.forName("JDKproxy.MyCalculator").getMethod("sout");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

  1. 代理类通过static块 m3 = Class.forName(“JDKproxy.MyCalculator”).getMethod(“sout”);拿到原本方法对象
  2. 然后在代理方法 sout()中 super.h.invoke(this, m3, (Object[])null);
  3. super 就是代表proxy类,类持有一个protected InvocationHandler h;属性
  4. 这个属性是我们在构造代理类的时候传入的:
  5. Object o = Proxy.newProxyInstance(classLoader, interfaces, h);
  6. h的invoke方法里就包含了我们的增强逻辑以及原始方法的调用

JDK动态代理为什么必须要基于接口?

通过上面的分析,我们已经知道了代理对象是如何生成的了,那么回到开头的问题,为什么jdk的动态代理一定要基于接口呢?

其实如果不看上面的分析,我们也应该知道,要扩展一个类有常见的两种方式,继承父类或实现接口。这两种方式都允许我们对方法的逻辑进行增强,但现在不是由我们自己来重写方法,而是要想办法让jvm去调用InvocationHandler中的invoke方法,也就是说代理类需要和两个东西关联在一起:

  • 被代理类

  • invocationHandler

而jdk处理这个问题的方式是选择继承父类Proxy,并把InvocationHandler存在父类的对象中:

public class Proxy implements java.io.Serializable {
    protected InvocationHandler h;
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }
    //...
}

通过父类Proxy的构造方法,保存了创建代理对象过程中传进来的InvocationHandler的实例,使用protected修饰保证了它可以在子类中被访问和使用。但是同时,因为java是单继承的,因此在继承了Proxy后,只能通过实现目标接口的方式来实现方法的扩展,达到我们增强目标方法逻辑的目的。

CGLIB动态代理实现

如果不是spring项目首先要引入依赖

    <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.12</version>
        </dependency>

写一个普通类

public class CGCalculator {
    public void run() {
        System.out.println("普通计算器!");
    }
}

写一个方法拦截器

public class MyCglib implements MethodInterceptor {
    /**
     *
     * @param obj 表示增强的对象,即实现这个接口类的一个对象
     * @param method 表示要拦截的方法
     * @param objects 表示被拦截方法的参数
     * @param methodProxy 表示要触发父类的方法对象
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("方法加强前");
        Object o = methodProxy.invokeSuper(obj, objects);
        System.out.println("方法加强后");
        return o;
    }
}

进行测试

public class Test6 {
    public static void main(String[] args) {
        //创建cglib获取代理对象的操作对象
        Enhancer enhancer = new Enhancer();
        //设置enhancer对象的父类
        enhancer.setSuperclass(CGCalculator.class);
        //设置enhancer的回调对象
        enhancer.setCallback(new MyCglib());
        //创建代理对象
        CGCalculator cgCalculator = (CGCalculator)enhancer.create();
        cgCalculator.run();
    }
}

CGLIB动态代理源码解析——代理类怎么生成的?

首先代理类对象是调用enhancer.create()生成的,其中会调用super.create()。
做的事情是:通过cglib的生成策略生成代理类的字节码文件,然后加载到jvm,再通过反射创建代理类对象。

public abstract class AbstractClassGenerator<T> implements ClassGenerator {
    protected Object create(Object key) {
        try {
            ClassLoader loader = this.getClassLoader();
            Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHE;
            AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
            if (data == null) {
                Class var5 = AbstractClassGenerator.class;
                synchronized(AbstractClassGenerator.class) {
                    cache = CACHE;
                    data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
                    if (data == null) {
                        Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> newCache = new WeakHashMap(cache);
                        data = new AbstractClassGenerator.ClassLoaderData(loader);
                        newCache.put(loader, data);
                        CACHE = newCache;
                    }
                }
            }

            this.key = key;
            //1.获取代理类对象
            Object obj = data.get(this, this.getUseCache());
            return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
        }
        ...
    }
    
    public Object get(AbstractClassGenerator gen, boolean useCache) {
        if (!useCache) {
            //2.生成代理类对象
            return gen.generate(this);
        } else {
            Object cachedValue = this.generatedClasses.get(gen);
            return gen.unwrapCachedValue(cachedValue);
        }
    }
    
     protected Class generate(AbstractClassGenerator.ClassLoaderData data) {
        Object save = CURRENT.get();
        CURRENT.set(this);
        try {
            ClassLoader classLoader = data.getClassLoader();
            if (classLoader == null) {
                throw new IllegalStateException("ClassLoader is null while trying to define class " + this.getClassName() + ". It seems that the loader has been expired from a weak reference somehow. Please file an issue at cglib's issue tracker.");
            } else {
                String className;
                synchronized(classLoader) {
                    className = this.generateClassName(data.getUniqueNamePredicate());
                    data.reserveName(className);
                    this.setClassName(className);
                }

                Class gen;
                if (this.attemptLoad) {
                    try {
                        gen = classLoader.loadClass(this.getClassName());
                        Class var25 = gen;
                        return var25;
                    } catch (ClassNotFoundException var20) {
                    }
                }
			//3.生成代理类字节码
                byte[] b = this.strategy.generate(this);
                className = ClassNameReader.getClassName(new ClassReader(b));
                ProtectionDomain protectionDomain = this.getProtectionDomain();
                synchronized(classLoader) {
                    if (protectionDomain == null) {
                        //4.根据字节码生成代理类对象
                        gen = ReflectUtils.defineClass(className, b, classLoader);
                    } else {
                        gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
                    }
                }

                Class var8 = gen;
                return var8;
            }
        }...
    }
}

CGLIB代理类方法如何调用的被代理类方法?

生成完代理类会生成如下三个文件,其中第2个是代理类class,其余两个是代理类的fastclass和被代理类fastclass
在这里插入图片描述
我们先粗略看一下代理类class中的内容,代理类run()方法中,会调用方法拦截器的intercept()方法,方法拦截器会通过某些手段找到代理类的CGLIB$run$0()方法,最后实现调用父类即被代理类Person的run()方法,这个手段在Jdk中是反射,而在Cglib中是一种新的机制-FastClass机制。

public class Person$$EnhancerByCGLIB$$c7ae5d4e extends Person implements Factory {
    private static final Method CGLIB$run$0$Method;
    private static final MethodProxy CGLIB$run$0$Proxy;

    static void CGLIB$STATICHOOK1() {
        Class var0 = Class.forName("com.codezhao.designpattern.proxypattern.cglibproxy.Person$$EnhancerByCGLIB$$c7ae5d4e");
        Class var1;
        CGLIB$run$0$Method = ReflectUtils.findMethods(new String[]{"run", "()V"}, (var1 = Class.forName("com.codezhao.designpattern.proxypattern.cglibproxy.Person")).getDeclaredMethods())[0];
        CGLIB$run$0$Proxy = MethodProxy.create(var1, var0, "()V", "run", "CGLIB$run$0");
    }

    final void CGLIB$run$0() {
        super.run();
    }

    public final void run() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$run$0$Method, CGLIB$emptyArgs, CGLIB$run$0$Proxy);
        } else {
            super.run();
        }
    }
}

FastClass机制

由于反射的效率较低,Cglib采用FastClass机制实现被代理类方法的调用,FastClass机制就是给一个类的方法建立索引,通过索引直接调用方法。

调用某Test对象的run()方法,FastClass机制实现:

/**
 * @author codeZhao
 * @date 2021/2/20  19:46
 * @Description
 */
public class FastClassTest {
    public static void main(String[] args) {
        Test test = new Test();
        Test$FastClass test$FastClass = new Test$FastClass();
        //先获取方法索引
        int index = test$FastClass.getIndex("run()v");
        //通过invoke()传入方法索引和类对象,执行索引对应方法
        test$FastClass.invoke(index, test);
    }
}

class Test {
    public void f() {
        System.out.println("f()");
    }
    public void run() {
        System.out.println("run()");
    }
}

class Test$FastClass {
    public Object invoke(int index, Object o) {
        Test t = (Test) o;
        switch (index) {
            case 1:
                t.f();
                return null;
            case 2:
                t.run();
                return null;
        }
        return null;
    }
    public int getIndex(String signature) {
        switch (signature.hashCode()) {
            case 3078511://"f()v的hashCode"
                return 1;
            case 3108302://"run()v的hashCode"
                return 2;
        }
        return -1;
    }
}

动态调用时,假设要调用某Test对象的run()方法,FastClass机制实现方式是首先通过getIndex()传入方法签名信息,获取方法的索引,然后通过invoke()传入方法索引类对象,执行索引对应方法

反射来实现:

Test test = new Test();
//通过反射拿到方法
Method method = Class.forName("Test").getMethod("run");
//invoke直接传入方法
method.invoke(test);

FastClass机制和反射相比,构建时比较复杂,但运行效率高于反射

代理对象生成后,会产生一个同名的fun方法用于前端调用,该方法内部会将请求传递到拦截对象中。
除此之外,代理对象中还会生产一个CGLIB$fun$1方法,该方法会调用父类(被代理对象)的fun方法。
在使用invokeSuper的过程中,会经拦截器、FastClass走到CGLIB$fun$1这里,
然后在CGLIB$fun$1中请求父类的fun方法。
大致过程如下图所示:

在这里插入图片描述

invokeSuper调用前后的主要流程如下图所示
在这里插入图片描述

  1. 假设前端触发了方法名为fun调用,首先进入代理对象中的同名方法
  2. 然后进入自定义的方法拦截对象MethodInterceptor,图中的this也就是代理对像
  3. 在调用invokeSuper后,cglib内部会通过代理类的FastClass找到要执行的方法,此时传到FastClass中的对象是代理对象,所以在通过FastClass对象后,流程会重新走到代理对象中
  4. ==但此时不会再次调用fun同名方法,而是调用了一个CGLIB$fun$1的方法,该方法会调用父类(也就是被代理类)fun方法,==至此流程结束。

如果将MethodInterceptor中的invokeSuper改为invoke,会怎么样呢?会出现StackOverflowError,同时invoke之前的内容会被反复执行。请看如下流程图

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值