JavaBase-Java反射机制
反射的应用:动态代理
动态代理概述
- 代理设计模式的原理:
- 使用一个代理将对象包装起来,然后用该代理对象代替原始对象。任何对原始对象的调用都要通过代理对象操作。代理对象决定是否以及何时将方法调用转到原始对象上
- 静态代理:
- 特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样导致程序开发中必然产生过多的代理类。理想的方式是,通过一个代理类完成完全的代理功能。
- 动态代理是指客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
- 动态代理适用场合:
- 调试
- 远程方法调用
- 动态代理相比于静态代理的有点:
- 抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,可以更加灵活和统一的处理众多的方法
动态代理的例子
静态代理
-
步骤
- 创建一个被接口类ClothFactory,定义所需要代理的方法
- 创建被代理类MyClothFactory实现接口ClothFactory
- 创建被代理类ProxyClothFactory实现接口ClothFactory
- 在代理类中,添加代理类MyClothFactory的属性 ,作为后续从代理类控制何时调用被代理类
- 在代理类中,添加给代理类属性赋值的方法
- 在代理类的代理方法中,通过被代理类的属性调用被代理类的方法
-
代码
-
package com.jl.java.base.proxy; /** * 静态代理 * 特点:代理类和被代理类在编译期间,就确定下来了 * @author jiangl * @version 1.0 * @date 2021/5/7 15:14 */ /** * 1.创建一个接口,使得代理类和被代理类都实现这个接口,创建一个被接口类ClothFactory,定义所需要代理的方法 */ interface ClothFactory{ void produceCloth(); } /** * 2.创建被代理类MyClothFactory实现接口ClothFactory */ class MyClothFactory implements ClothFactory{ @Override public void produceCloth() { System.out.println("生产衣服"); } } /** * 3.创建被代理类ProxyClothFactory实现接口ClothFactory */ class ProxyClothFactory implements ClothFactory{ /** * 4.在代理类中,添加代理类MyClothFactory的属性 ,作为后续从代理类控制何时调用被代理类 */ private ClothFactory myClothFactory; /** * 5.在代理类中,添加给代理类属性赋值的方法 * @param myClothFactory */ public ProxyClothFactory(MyClothFactory myClothFactory) { this.myClothFactory = myClothFactory; } /** * 6.在代理类的代理方法中,通过被代理类的属性调用被代理类的方法 */ @Override public void produceCloth() { System.out.println("获取原料"); myClothFactory.produceCloth(); System.out.println("完成"); } } public class StaticProxyTest { public static void main(String[] args) { ClothFactory proxyClothFactory = new ProxyClothFactory(new MyClothFactory()); proxyClothFactory.produceCloth(); } }
-
-
静态代理缺点
- 在编译期,就确定了代理类和被代理类,如果大量使用会导致系统中存在很多代理类
动态代理(JDK动态代理)
-
创建一个被接口类Human,定义所需要代理的方法,代理类和被代理类都要实现这个方法
-
创建一个被代理类Student实现Human接口,并重写接口中的方法
-
通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法,生成代理类
- ClassLoader loader:被代理类的类加载器,用于通过类加载器动态创建一个代理类
- Class<?>[] interfaces:被代理类所实现的所有接口数组,因为被代理类可能实现多个接口所以是数组。而代理类需要通过实现这些接口来,并重写接口中的方法来代理被代理类。
- InvocationHandler h:回调方法对象,代理类通过回调方法对象中的 invoke方法来实现对被代理类的调用和功能的增强(可以在invoke(Object proxy, Method method, Object[] args)中编写所要对被代理类功能增强的代码逻辑)
- Object proxy:这是代理类的对象
- Method method:这是所要代理的方法
- Object[] args:所要代理方法的入参数组
- 在InvocationHandler h类中需要添加一个被代理类的属性(为了通用性该属性的类型应为Object),用于在invoke()方法中,通过Method.invoke方法来调用被代理类的所需要代理的方法
-
最后先创建一个被代理类对象
- 通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法获取到代理类的对象,该对象可以赋值给Human proxy(向上转型,类的多态性)
- Human proxy = Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
- 通过调用代理类对象proxy.eat()调用被代理类的方法,实现代理模式
- 通过Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法获取到代理类的对象,该对象可以赋值给Human proxy(向上转型,类的多态性)
-
代码
-
package com.jl.java.base.proxy; import org.junit.Test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author jiangl * @version 1.0 * @date 2021/5/7 15:19 */ interface Human{ String getBelief(); void eat(String food); } /** * 被代理类 */ class Student implements Human{ @Override public String getBelief() { System.out.println("好好学习,天天向上"); return "学习"; } @Override public void eat(String food) { System.out.println("吃有营养的东西"); } } /** * 要想实现动态代理,需要解决的问题? * 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象 * 问题二:当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法 */ class ProxyFactory{ /** * 调用此方法,返回一个代理类的对象,解决问题一 * @param obj 被代理类的对象 * @return 返回代理类的对象 */ public static Object getProxyInstance(Object obj){ MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler); } } class MyInvocationHandler implements InvocationHandler{ /** * 需要使用被代理类的对象进行赋值 */ private Object obj; public void bind(Object obj){ this.obj = obj; } /** * 当通过代理类的对象,调用方法a时,就会自动的调用如下的方法 * 将被代理类要执行的方法a的功能就声明在invoke()中 * @param proxy 代理类的对象 * @param method 代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法 * @param args 方法的入参 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("---代理---"); //obj:被代理类对象 Object returnValue = method.invoke(obj, args); //上述方法的返回值,就作为当前类的invoke()的返回值 return returnValue; } } public class DynamicProxyTest { @Test public void test(){ Object proxyInstance1 = ProxyFactory.getProxyInstance(new Student()); System.out.println(proxyInstance1.getClass()); Class<?>[] interfaces = proxyInstance1.getClass().getInterfaces(); for(Class inter : interfaces){ System.out.println(inter); } Human proxyInstance = (Human) ProxyFactory.getProxyInstance(new Student()); proxyInstance.getBelief(); proxyInstance.eat("苹果"); } }
-
源码解析(JDK动态代理)
-
前提:
-
通过Proxy.nexProxyInstance()方法创建的类,实际是实现了被代理类接口,并且继承与Proxy类的如$Proxy0匿名类
-
代码
-
@Test public void test(){ Object proxyInstance1 = ProxyFactory.getProxyInstance(new Student()); System.out.println(proxyInstance1.getClass()); System.out.println(proxyInstance1.getClass().getSuperclass()); Class<?>[] interfaces = proxyInstance1.getClass().getInterfaces(); for(Class inter : interfaces){ System.out.println(inter); } }
-
结果
- class com.jl.java.base.proxy.$Proxy5
class java.lang.reflect.Proxy
interface com.jl.java.base.proxy.Human
- class com.jl.java.base.proxy.$Proxy5
-
-
源码:
-
生成代理类源码newProxyInstance()方法
-
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //判断回调方法对象是否为空 if (h == null) { throw new NullPointerException(); } /** * 查找或生成指定的代理类。 */ Class<?> cl = getProxyClass0(loader, interfaces); /* * 通过代理类的构造器,反射生成代理类,并将需要调用的方法写入代理类 */ try { final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; SecurityManager sm = System.getSecurityManager(); if (sm != null && java.lang.reflect.Proxy.ProxyAccessHelper.needsNewInstanceCheck(cl)) { return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }
-
-
在newProxyInstance()方法中,通过getProxyClass0(loader,interfaces)方法生成匿名代理类
-
通过Java安全器检测
-
//Java安全器检测 SecurityManager sm = System.getSecurityManager(); if (sm != null) { final int CALLER_FRAME = 3; // 0: Reflection, 1: getProxyClass0 2: Proxy 3: caller final Class<?> caller = Reflection.getCallerClass(CALLER_FRAME); final ClassLoader ccl = caller.getClassLoader(); checkProxyLoader(ccl, loader); ReflectUtil.checkProxyPackageAccess(ccl, interfaces); }
-
-
判断代理类的实现接口是否超过了int最大值,如果超过则抛出异常IllegalArgumentException
-
//判断被代理类的接口是否超过int最大值 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); }
-
-
创建一个代理类的变量Class proxyClass用于返回
-
//代理类 变量 Class proxyClass = null;
-
-
获取被代理类的所有接口(接口数组),将这些接口名作为代理类缓存的key。用于再次获取时不需要二次创建,优化性能
-
//获取所有的接口名称用于作为代理类缓存的关键字 String[] interfaceNames = new String[interfaces.length]; //创建一个被代理类接口的HashSet用于去重 Set interfaceSet = new HashSet(); //循环遍历入参代理类的所有接口 for (int i = 0; i < interfaces.length; i++) { //检查通过类加载加载的接口实际类,是否和传入的接口类相同 String interfaceName = interfaces[i].getName(); Class interfaceClass = null; try { interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] + " is not visible from class loader"); } //校验加载得到的接口类是否真实为接口 if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } //判断是否有重复接口 if (interfaceSet.contains(interfaceClass)) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); interfaceNames[i] = interfaceName; }
-
-
将接口数组转换成List,作为key
-
//将接口名字符数组转换成List<String> /** * 使用代理接口的字符串表示作为代理类缓存中的键(而不是它们的类对象)就足够了, * 因为我们要求代理接口通过提供的类加载器按名称进行解析,它的优点是,使用类的字符串表示会导致对类的隐式弱引用。 */ Object key = Arrays.asList(interfaceNames);
-
-
根据类加载器在缓存loaderToCache map中查询,这个类加载下的所有代理类的Map
-
WeakHashMap<ClassLoader,HashMap<List,Object>>
-
//通过类加载器找到或创建一个代理类缓存 Map cache; synchronized (loaderToCache) { //根据类加载器找到一个缓存Map cache = (Map) loaderToCache.get(loader); //如果类加载器的缓存map不存在,则重新创建 if (cache == null) { cache = new HashMap(); loaderToCache.put(loader, cache); } /** * 此映射将在此方法期间保持有效,无需进一步同步,因为只有在类装入器变得不可访问时才会删除映射。 */ }
-
-
获取的到代理类map中根据,接口数组key查询获取代理类,此处有三种情况
-
根据key查询到代理类,直接返回
-
根据key获取到对象pendingGenerationMarker,表示代理类正在创建中,调用cache.await()方法进入等待
-
根据key获取到null,则表示未创建代理,此时先将key的value赋值成pendingGenerationMarker,防止多次创建,保证线程安全,其他线程来访问则会进入上一个分支,进入等待(而正真的创建代理类,在后续逻辑中)
-
/** * 使用键在代理类缓存中查找接口列表。此查找将导致三种可能的值之一: * null:类加载器中的接口列表当前没有代理类 * the pendingGenerationMarker object: 当前正在生成接口列表的代理类 * a weak reference to a Class object: 接口列表的代理类已经生成。 */ synchronized (cache) { /** * 请注意,我们不必担心在map中清除弱引用条目的缓存, * 因为如果代理类已被垃圾收集,则其类装入器也将被垃圾收集, * 因此这个缓存在GC后从整个代理类加载器缓存Map中清除 */ do { //从缓存中根据接口字符串做的关键字key获取代理类 Object value = cache.get(key); //判断如果获取到的值是Reference类的对象,则调用Reference对象中的get()方法获取,引用的实际对象 if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); } //判断代理类 if (proxyClass != null) { //代理类已创建,返回代理类 return proxyClass; } else if (value == pendingGenerationMarker) { // 代理类正在创建中,进入wait()等待 try { cache.wait(); } catch (InterruptedException e) { /* * The class generation that we are waiting for should * take a small, bounded time, so we can safely ignore * thread interrupts here. */ } continue; } else { /** * 尚未生成或正在生成此接口列表的代理类,因此我们将立即生成它。将其标记为挂起生成。 * 这样做的目的是,多线程情况下,其他线程会看到一个正在创建的标识,进入上一个if条件,进入等待 */ cache.put(key, pendingGenerationMarker); break; } } while (true); }
-
-
创建代理类对象步骤
-
判断被代理类的接口是否都在同一个包名下,校验不通过在抛异常返回
-
/** * 记录非公共代理接口的包,以便在同一个包中定义代理类。 * 验证所有非公共代理接口是否在同一个包中。 */ for (int i = 0; i < interfaces.length; i++) { int flags = interfaces[i].getModifiers(); if (!Modifier.isPublic(flags)) { String name = interfaces[i].getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } }
-
生成一个代理类的名称,规则是 包名+jdk代理类前缀$Proxy+计数
-
//生成一个代理类的名称,规则是 包名+jdk代理类前缀$Proxy+计数 long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } String proxyName = proxyPkg + proxyClassNamePrefix + num;
-
调用 ProxyGenerator.generateProxyClass方法生成代理类文件
-
//生成一个代理类文件 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);
-
通过类加载器,代理类名,代理类文件,生成一个代理类的类对象
-
try { //通过类加载器,代理类名,代理类文件,生成一个代理类的类对象 proxyClass = 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()); }
-
将代理类添加到proxyClasses Map中,用于isProxyClass方法中使用,判断类是否为代理类
-
//将代理类添加到proxyClasses Map中,用于isProxyClass方法中使用,判断类是否为代理类 // add to set of all generated proxy classes, for isProxyClass proxyClasses.put(proxyClass, null);
-
-
最后在fianlly代码块中做最后的处理,将之前设置的缓存key,根据是否正确生成代理做最后赋值
-
如果代理类创建成功,则创建一个弱引用WeakReference把代理类对象引用,把弱引用作为value赋值给接口名数组的key
-
如果代理类创建失败,则删除key
-
最后唤醒所有等待队列中的线程
-
/** * 我们必须以某种方式清除代理类缓存项的“挂起的生成”状态。 * 如果成功生成了代理类,则将其存储在缓存中(使用弱引用);否则,请删除保留项。 * 在所有情况下,都要通知此缓存中保留项的所有等待队列中的线程。 */ synchronized (cache) { if (proxyClass != null) { //代理类创建成功 //根据接口数组 作为key,将代理类封装成一个索引用放入缓存map中 cache.put(key, new WeakReference(proxyClass)); } else { //代理类创建失败,则在缓存中删除key cache.remove(key); } //唤醒其他等待的线程, cache.notifyAll(); }
-
-
源码
-
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { //Java安全器检测 SecurityManager sm = System.getSecurityManager(); if (sm != null) { final int CALLER_FRAME = 3; // 0: Reflection, 1: getProxyClass0 2: Proxy 3: caller final Class<?> caller = Reflection.getCallerClass(CALLER_FRAME); final ClassLoader ccl = caller.getClassLoader(); checkProxyLoader(ccl, loader); ReflectUtil.checkProxyPackageAccess(ccl, interfaces); } //判断被代理类的接口是否超过int最大值 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } //代理类 变量 Class proxyClass = null; //获取所有的接口名称用于作为代理类缓存的关键字 String[] interfaceNames = new String[interfaces.length]; //创建一个被代理类接口的HashSet用于去重 Set interfaceSet = new HashSet(); //循环遍历入参代理类的所有接口 for (int i = 0; i < interfaces.length; i++) { //检查通过类加载加载的接口实际类,是否和传入的接口类相同 String interfaceName = interfaces[i].getName(); Class interfaceClass = null; try { interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] + " is not visible from class loader"); } //校验加载得到的接口类是否真实为接口 if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } //判断是否有重复接口 if (interfaceSet.contains(interfaceClass)) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); interfaceNames[i] = interfaceName; } //将接口名字符数组转换成List<String> /** * 使用代理接口的字符串表示作为代理类缓存中的键(而不是它们的类对象)就足够了, * 因为我们要求代理接口通过提供的类加载器按名称进行解析,它的优点是,使用类的字符串表示会导致对类的隐式弱引用。 */ Object key = Arrays.asList(interfaceNames); //通过类加载器找到或创建一个代理类缓存 Map cache; synchronized (loaderToCache) { //根据类加载器找到一个缓存Map cache = (Map) loaderToCache.get(loader); //如果类加载器的缓存map不存在,则重新创建 if (cache == null) { cache = new HashMap(); loaderToCache.put(loader, cache); } /** * 此映射将在此方法期间保持有效,无需进一步同步,因为只有在类装入器变得不可访问时才会删除映射。 */ } /** * 使用键在代理类缓存中查找接口列表。此查找将导致三种可能的值之一: * null:类加载器中的接口列表当前没有代理类 * the pendingGenerationMarker object: 当前正在生成接口列表的代理类 * a weak reference to a Class object: 接口列表的代理类已经生成。 */ synchronized (cache) { /** * 请注意,我们不必担心在map中清除弱引用条目的缓存, * 因为如果代理类已被垃圾收集,则其类装入器也将被垃圾收集, * 因此这个缓存在GC后从整个代理类加载器缓存Map中清除 */ do { //从缓存中根据接口字符串做的关键字key获取代理类 Object value = cache.get(key); //判断如果获取到的值是Reference类的对象,则调用Reference对象中的get()方法获取,引用的实际对象 if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); } //判断代理类 if (proxyClass != null) { //代理类已创建,返回代理类 return proxyClass; } else if (value == pendingGenerationMarker) { // 代理类正在创建中,进入wait()等待 try { cache.wait(); } catch (InterruptedException e) { /* * The class generation that we are waiting for should * take a small, bounded time, so we can safely ignore * thread interrupts here. */ } continue; } else { /** * 尚未生成或正在生成此接口列表的代理类,因此我们将立即生成它。将其标记为挂起生成。 * 这样做的目的是,多线程情况下,其他线程会看到一个正在创建的标识,进入上一个if条件,进入等待 */ cache.put(key, pendingGenerationMarker); break; } } while (true); } //开始生产代理类 try { String proxyPkg = null; // package to define proxy class in /** * 记录非公共代理接口的包,以便在同一个包中定义代理类。 * 验证所有非公共代理接口是否在同一个包中。 */ for (int i = 0; i < interfaces.length; i++) { int flags = interfaces[i].getModifiers(); if (!Modifier.isPublic(flags)) { String name = interfaces[i].getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } //如果未传入接口或者未找到包名,则使用默认包名com.sun.proxy. if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } { //生成一个代理类的名称,规则是 包名+jdk代理类前缀$Proxy+计数 long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } String proxyName = proxyPkg + proxyClassNamePrefix + num; /** * 校验是否已经加载了代理类,这一步好像是没有逻辑 * Verify that the class loader hasn't already * defined a class with the chosen name. */ //生成一个代理类 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); try { proxyClass = 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()); } } //将代理类添加到proxyClasses Map中,用于isProxyClass方法中使用,判断类是否为代理类 // add to set of all generated proxy classes, for isProxyClass proxyClasses.put(proxyClass, null); } finally { /** * 我们必须以某种方式清除代理类缓存项的“挂起的生成”状态。 * 如果成功生成了代理类,则将其存储在缓存中(使用弱引用);否则,请删除保留项。 * 在所有情况下,都要通知此缓存中保留项的所有等待队列中的线程。 */ synchronized (cache) { if (proxyClass != null) { //代理类创建成功 //根据接口数组 作为key,将代理类封装成一个索引用放入缓存map中 cache.put(key, new WeakReference(proxyClass)); } else { //代理类创建失败,则在缓存中删除key cache.remove(key); } //唤醒其他等待的线程, cache.notifyAll(); } } return proxyClass; }
-
-