辽宁省住房和建设厅官方网站,台州企业建站系统,陇南市建设局网站公示,仁茂网络seo简介动态代理#xff0c;通俗点说就是#xff1a;无需声明式的创建java代理类#xff0c;而是在运行过程中生成虚拟的代理类#xff0c;被ClassLoader加载。 从而避免了静态代理那样需要声明大量的代理类。上面的简介中提到了两个关键的名词#xff1a;“静态…简介动态代理通俗点说就是无需声明式的创建java代理类而是在运行过程中生成虚拟的代理类被ClassLoader加载。 从而避免了静态代理那样需要声明大量的代理类。上面的简介中提到了两个关键的名词“静态代理”和“动态代理”我们先来看来下两个问题首先什么是代理呢它可以看作是对最终调用目标的一个封装可以通过操作代理对象来调用目标类这样就可以实现调用者和目标对象的解耦合“静态代理”和“动态代理”又有什么区别呢这是代理模式的两种类型Java中JDK从1.3版本开始支持动态代理主要是通过java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler这两个类配合使用来实现的而所谓的“静态”和“动态”其实也就是代理类的字节码文件创建的时间不同“静态代理”是在程序运行前就知道了哪些对象需要创建代理对象“动态代理”则是在程序运行中动态的判断哪些对象需要创建代理对象Java中主要有两种动态代理的实现方式JDK和CGLIB他们最主要的区别简单来讲就是 JDK动态代理针对实现了接口的类生成代理必须实现接口 CGLIB动态代理是针对类实现代理无需实现接口动态代理这个东西吧。。。个人的感觉是不自己走一遍流程看别人讲再多遍总是不踏实本系列就是一套保姆级的Debug教程接下来会通过两个示例跟我一起手把手地捋下JDK动态代理和CGLIB动态代理创建代理对象的基本流程JDK这个案例我们一共需要创建4个文件创建测试文件IService接口文件public interface IService {void A();String B(int i);
}MyService实现类被代理类public class MyService implements IService {Overridepublic void A() {System.out.println(This is A().);}Overridepublic String B(int i) {return This is B(). i;}
}MyServiceProxy代理创建类public class MyServiceProxy {public static IService getProxy(final IService iService) {/** 获取类加载器 */ClassLoader classLoader iService.getClass().getClassLoader();/** 获取接口集合 */Class?[] interfaces iService.getClass().getInterfaces();InvocationHandler invocationHandler new InvocationHandler() {Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object invoke method.invoke(iService, args);return invoke;}};Object proxyInstance Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);return (IService) proxyInstance;}
}Test测试启动类public class Test {public static void main(String[] args) {System.getProperties().put(sun.misc.ProxyGenerator.saveGeneratedFiles, true);IService proxy MyServiceProxy.getProxy(new MyService());System.out.println(proxy.getClass());String b proxy.B(1);System.out.println(b);}
}Debug启动在下图箭头指向位置处打上断点然后Debug启动点击step into来到需要创建代理的接口类中点击step into进入类MyServiceProxy这个类中先获取类加载器再获取接口们的集合最后获取到handler后3个参数一起传入newProxyInstance()方法中用来创建我们的代理类newProxyInstance()我们进入newProxyInstance()方法中看下方法说明Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.简单翻一下返回一个指定接口的代理类的实例该代理类将方法调用分派给指定的调用处理程序。一开始先判断传入的InvocationHandler是否为空接着获取接口、获取安全管理器然后是非常重要的一步生成字节码文件getProxyClass0()getProxyClass0()这个函数很复杂我们进来瞅瞅(ω)第一步先判断了下接口个数对需要代理的对象实现的接口数量做了一个限制不能超过65535个第二步通过proxyClassCache的get()方法获取代理类并返回通过缓冲区查询数值这里有两种情况如果实现给定接口的给定加载器定义的代理类存在这将简单地返回缓存的副本否则它将通过ProxyClassFactory创建代理类点击进入会执行到WeakCache中的get()方法上图中可以看到proxyClassCache这个变量实际就是一个WeakCache对象get()进入get()方法先判断传入的参数是否为空然后移执行expungeStaleEntries()移除之前的内容尝试从map中获取key值并经获取到的key值们转换成ConcurrentMapObject, SupplierV的类型首先我们看下这个map是何方神圣这是一个map里面套着一个map而且key的类型使用Object以便存储key值为null的对象既然是尝试获取就会出现两种情况获取到key未获取到key如果未获取到会多执行一步putIfAbsent()方法其余基本一致putIfAbsent()在这个方法中如果指定的键还没有与一个值相关联或被映射为null则将其与给定的值相关联并返回null否则就会返回当前值其实就是如果获取到的valuesMap为null就把key为nullvalue为valuesMap的对象放入map中如果oldValuesMap获取到值了就会给valuesMap赋个值但是目前并没有获取到相关联的值如下图执行完putIfAbsent()方法后map的值增加了一个但是valuesMap仍为空点击step over继续执行下面的逻辑出现了一个叫做subKeyFactory的变量点击下进入到定义该变量的位置就在这个类一开始这是一个BiFunctionK, P, ?类型的变量BiFunction这是一个函数式接口代表叻一个接受两个参数并产生一个结果的函数apply()subKeyFactory在WeakCache对象初始化的时候进行了赋值this.subKeyFactory Objects.requireNonNull(subKeyFactory);所以subKeyFactory是有值的调用叻他的apply()方法由于我们的接口只有一个所以会返回return new Key1(interfaces[0])此时已经获取到了接口对象并从valuesMap中检索该子键所存储的可能的SupplierV由于valuesMap是空所以valuesMap.get(subKey)并未获取到值接着创建一个Factory对象并赋值为null由于从valuesMap.get(subKey)并未获取到值所以不会进入supplier ! null的判断逻辑代码块直接进入下面创建一个Factory对象这个Factory对象实现了SupplierV实现了值的懒同步构建并将其放置到了缓存中把刚新建好的Factory赋值给变量supplier由于while(true)是个死循环因此会再次进入循环中此时由于刚刚的赋值变量supplier已经不是null叻于是会调用supplier的get()方法get()apply()在这个方法中会生成我们代理对象的字节码文件generateProxyClass()方法执行完成后其实就生成了我们当前代理对象的字节码文件apply()方法详解可以参考代理对象的字节码文件生成详解 方法执行完成后返回到get()方法再返回到上一层的get()方法此时value也拿到叻值接着通过return value跳出当前循环♻️执行完getProxyClass0()方法回到newProxyInstance()此时我们已经生成了代理类的字节码文件也获取到了代理类并赋值给了变量cl由于sm为null跳过checkNewProxyPermission()直接开始获取构造器验证访问修饰符通过调用获取到的构造器的newInstance()方法获取实例对象并返回可以看到此时获取到的对象是com.aqin.custom.proxy.jdk.MyService6d86b085返回到getProxy()获取到了代理对象继续把测试启动类执行完JDK介绍完啦我们开始CGLIBCGLIB创建测试文件MyService被代理类public class MyServic{public void A() {System.out.println(This is A().);}public String B(int i) {return This is B(). i;}
}MyCglib拦截器package com.aqin.custom.proxy.cglib;import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;/*** Description* Author aqin1012 AQin.* Date 12/28/22 5:36 PM* Version 1.0*/
public class MyCglib implements MethodInterceptor {Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {Object o1 methodProxy.invokeSuper(o, objects);return o1;}
}需要注意的是MethodInterceptor和MethodProxy都要选org.springframework.cglib.proxy包下的Test测试启动类package com.aqin.custom.proxy.cglib;import org.springframework.cglib.core.DebuggingClassWriter;
import org.springframework.cglib.proxy.Enhancer;/*** Description* Author aqin1012 AQin.* Date 12/28/22 5:29 PM* Version 1.0*/
public class Test {public static void main(String[] args) {/** 动态代理创建的字节码文件存储到本地 */System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, cglib_proxy);/** 通过cg11b动态代理获取代理对象的过程创建调用的对象 */Enhancer enhancer new Enhancer();/** 设置enhancer对象的父类 */enhancer.setSuperclass(MyService.class);/** 设置enhancer的回调对象 */enhancer.setCallback(new MyCglib());/** 创建代理对象 */MyService myService (MyService) enhancer.create();/** 通过代理对象调用目标方法 */System.out.println(myService.B(999));System.out.println(myService.getClass());}
}Debug启动按照下图在箭头指向的位置打上一个断点Debug的方式启动step into进入会跳转到下图的位置继续向下执行就会进入到Enhancer类中这个类是生成动态子类以实现方法拦截的JDK 1.3中引入它并不需要被代理类一定实现了某个接口这是与JDK动态代理的主要区别动态生成的子类会覆盖超类的非最终方法并回调到用户定义的拦截器实现的回调函数。继续执行到private static final EnhancerKey KEY_FACTORY (EnhancerKey) KeyFactory.create(EnhancerKey.class, KeyFactory.HASH_ASM_TYPE, null);这行这句的作用是使用key工厂创建出对应class的代理类后面的KeyFactory_HASH_ASM_TYPE即代理类中创建HashCode方法的策略而且在此处出现了两个对象KeyFactory和EnhancerKeyKeyFactory这个对象是生成处理多值键的类多个键组合在一起的用于Maps和Sets等集合中而Enhancer是一个内部接口由于ClassLoader的问题只能是public的创建EnhancerKey我们进入KeyFactory的create()方法中开始EnhancerKey对象的创建再次进入return处的create()方法方法先创建了一个最简易的代理类生成器只会生成HashCode()、equals()、toString()、newInstance()方法然后设置接口类型添加定制器设置类加载器最后调用gen.create()生成enhancerKey的代理类于是我们再次进入create()方法这个create()方法中的第一步设置了该生成器生成代理对象类的名字前缀第二步调用了super的create()方法终于咱们的创建要开始叻(∇)/create()获取到当前生成器的类加载器loader定义一个MapClassLoader,ClassLoaderData类型的变量cache并把CACHE赋值给cache通过cache.get(loader)获取ClassLoaderData类型的结果data不过此时cache里并没有值CACHE是当前类在一开始初始化的一个MapClassLoader, ClassLoaderData类型的空集合的变量所以此时cache也为空所以继续执行会开始执行下图中条件代码块中的逻辑即新建一个ClassLoaderData(loader)赋值给刚刚未获取到值的变量data此时点击step into我们会来到ClassLoaderData类中的变量GET_KEY初始化的位置这个GET_KEY是一个Function类型的变量可以看出Function类型是一个函数式接口提供一个apply()方法传入一个对象返回一个对象public Object apply(AbstractClassGenerator gen) {return gen.key;
}在此处的作用就是传入一个AbstractClassGenerator类型的变量gen返回gen.keykey的定义位置见下图继续执行我们会进入ClassLoaderData的构造方法中在这里为上面定义的变量赋值new ClassLoaderData()第一步先判断类加载器不能为空为空则抛出IllegalArgumentException的异常接着设置类加载器用的是弱引用类型WeakReference即在下次垃圾回收时就会进行回收引用的强软弱虚引用的强弱关系依次递减强引用关系存在即不会被垃圾回收机制回收内存溢出抛异常也不会回收软引用SoftReference内存不够会被回收回收后仍然OOM才会抛异常弱引用WeakReference无论内存够不够垃圾收集器一工作就会被回收虚引用PhantomReference约等于没有引用关系甚至无法通过虚引用获取到对象实例只是会在被回收后发送一条系统通知然后新建了一个回调函数这个回调函数的作用是当缓存中没获取到值时会调用传入默认生成器AbstractclassGenerator的生成代理类并返回最后新建一个LoadingCacheAbstractClassGenerator, Object, Object类型的缓存类赋值给变量generatedClasses这就是一个类型的变量在类的一开始定义的而LoadingCache中有两个参数一个是GET_KEY就是刚刚解释的那个会返回gen.key的函数式接口还有一个是load而这个load也是一个function类型的函数式接口返回的是一个gen.wrapCachedClass(klass)对象其实也就是一个Class类型的对象即此缓存对象中包含的两个具体的function对象其实就是具体的业务逻辑处理过程执行完构造函数返回赋值给data并放入缓存中此时我们的ClassLoaderData类型的变量data中包含了两个变量一个key值和一个Class类型的对象而这个对象被放入了CACHE中因此此时CACHE中有了一个键值对设置一个key值紧接着通过刚创建的data中调用get方法并将当前生成器以及是否使用缓存的标识系统参数System.getProperty(cglib.useCache,true)传入进去返回的是生成好的代理类的class信息get()进入get()方法中如果不使用缓存则直接调用生成器的命令不过一般都是默认使用缓存的即getUserCache()返回的值为true于是会将生成器作为参数传入到generatedClasses的get()方法中可以看到此处的keyMapper就是在前面步骤中初始化好的ClassLoaderData返回的值就是gen.key由于map为空所以v值为null于是进入到this.createEntry(key, cacheKey, v)方法中由于v值为null所以会进入else的代码块新建一个FutureTask对象为task赋值而FutureTask对象中是一个lambda表达式调用一个call()方法返回LoadingCache.this.loader.apply()不知道大家对LoadingCache还有没有印象之前初始化ClassLoaderData对象时给变量generatedClasses赋的值就是一个LoadingCache对象当时里面传入的两个参数一个是GET_KEY其实就是gen.key一个是load此处调用的LoadingCache.this.loader就是指的这个对象因此调用LoadingCache.this.loader的apply()方法实际就是调用load这个lambda表达式中的apply()方法所以在执行到task.run()时会调用执行call()方法点击step into进入run()方法就会发现代码又执行到了刚才赋值的位置再次点击step into就会跳转到LoadingCache.this.loader.apply()方法中在此处调用执行的generate()方法就是实际生成字节码文件的方法先将当前的代理类生成器存入变量CURRENT中CURRENT是一个ThreadLocal类型的变量如下图然后从传入的ClassLoaderData中获取类加载器classLoader判断其是否为空为空则抛出IllegalStateException异常不为空则继续执行后面的逻辑在获取到类加载器后我们还需要获取到代理类的名字于是generateClassName()方法开始生成代理类的名字getClassName()方法中是具体的生成规则这点要比JDK生成代理对象名称要复杂JDK就是获取包名然后加“$Proxy”0、1、2、3……可以看下这次最终生成的代理类的名字有了名字后回到generate()方法中将它缓存进传入的变量data中有了类加载器有了名字接下来就开始生成字节码了进入generateClass()方法中在这里为字节码文件写入方法先创建了一个ClassEmitter对象尝试获取被代理类的newInstance()方法如果没有会报异常由此可知如果想用Generator代理类生成器newInstance()方法必不可少此处我们代理的Enchaer、EnhancerKey、newInstance方法返回值为Object接着找到newInstance()方法的所有参数类型放入集合parameterTypes中当做成员变量接下来开始具体的写入操作先通过begin_class()创建类开始写入类头版本号访问权限类名等通用信息接着调用EmitUtils.null_constructor()写入无参构造方法调用EmitUtils.factory_method()写入newInstance()方法随后调用ce.begin_method()开始构造有参构造方法有参构造中调用父类构造方法即super.构造方法()找到传入的定制器例如一开始传入的hashCode()方法定制器遍历成员变量即newInstance()方法的所有参数将这些参数全部声明写入到类中设置每个成员变量的值this.xxxxxx设置返回值至此完成有参构造及成员变量的写入最后还有一些Object的固定方法需要写入包括hashcode()、equals()、toString()最后执行到ce.end_class()类写入结束至此类信息收集完成并全部写入ClassEmitter类型的变量ce中方法结束返回字节码通过类加载器加载到内存返回到apply()方法中获取generatedClasses的get()方法的返回值后解包装并返回此时的cachedValue就有值了返回给我们的obj在get()方法执行结束后会判断获取到Object对象是否为Class的实例对象由于Class类型的对象我们是没有办法直接使用的所以需要通过调用firstInstance()方法进行实例化如果为Class则实例化并返回我们需要的代理类,如果不是则说明是实体则直接执行另一个方法返回实体。create()执行结束后一路返回继续执行完后面的静态代码块就会回到我们的测试启动类紧接着对superclass和callback两个属性进行赋值