Java中的动态代理以及Proxy类的偷瞄
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中的动态代理以及Proxy类的偷瞄相关的知识,希望对你有一定的参考价值。
动态代理机制
所谓动态代理,即通过代理类Proxy的代理,接口和实现类之间可以不直接发生联系,而可以在运行期(Runtime)实现动态关联。
Java动态代理类位于Java.lang.reflect包下,主要涉及到两个类。
(1)接口InvocationHandler:该接口中仅定义了一个方法。
Object invoke(Object obj, Method method, Object[] args);
在实际使用时,第一个参数obj一般指代理类,method是被代理的方法,args为该方法的参数数组。
(2)proxy:该类即为动态代理类,作用类实现了InvocationHandler接口的代理类,其中主要方法有:
protected Proxy(InvocationHandler h):构造方法,用于给内部的h赋值。
static Class getProxyClass(ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用。
动态代理应用
Java 1.3中引入的动态代理类可以为已知接口的实现动态地创建包装器(wrapper)类。
与动态代理对应的是非动态代理,先来看看非动态代理的实现。
1. 定义接口和实现类并直接调用
接口的定义
1 package com.tuhooo.proxy.demo; 2 3 public interface Hello { 4 5 public void sayHello(); 6 7 }
接口的实现类
1 package com.tuhooo.proxy.demo; 2 3 public class HelloEarth implements Hello { 4 5 @Override 6 public void sayHello() { 7 System.out.println("Hello earth!"); 8 } 9 10 }
通过实现接口增强
1 package com.tuhooo.proxy.demo; 2 3 public class HelloEarthWrapper1 implements Hello { 4 5 private Hello helloImpl; 6 7 public HelloEarthWrapper1(Hello helloImpl) { 8 this.helloImpl = helloImpl; 9 } 10 11 @Override 12 public void sayHello() { 13 System.out.println("在sayHello之前执行了这条语句......"); 14 helloImpl.sayHello(); 15 16 } 17 }
通过继承对类进行增强
1 package com.tuhooo.proxy.demo; 2 3 public class HelloEarthWrapper2 extends HelloEarth { 4 5 private Hello helloImpl; 6 7 public HelloEarthWrapper2(Hello helloImpl) { 8 this.helloImpl = helloImpl; 9 } 10 11 @Override 12 public void sayHello() { 13 System.out.println("在sayHello之前执行了这条语句......"); 14 helloImpl.sayHello(); 15 } 16 }
实现InvocationHandler接口
1 package com.tuhooo.proxy.demo; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 6 public class HelloHandler implements InvocationHandler { 7 8 private Object proxyed; // 被代理的类 9 10 // 被代理的对象 11 public HelloHandler(Object obj) { 12 13 this.proxyed = obj; 14 } 15 16 @Override 17 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 18 19 Object result; 20 System.out.println("在sayHello()之前可以执行的语句"); 21 // 调用原始对象的方法 22 result = method.invoke(this.proxyed, args); 23 System.out.println("在调用sayHello()之后"); 24 return result; 25 } 26 }
测试代理类
1 package com.tuhooo.proxy.demo; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Proxy; 5 import org.junit.Test; 6 7 public class TestProxy { 8 9 @Test 10 public void test1() { 11 Hello helloImpl = new HelloEarth(); 12 Hello helloWrapper = new HelloEarthWrapper2(helloImpl); 13 helloWrapper.sayHello(); 14 } 15 16 @Test 17 public void test2() { 18 // 被代理类的实例 19 Hello hello = new HelloEarth(); 20 // InvocationHandler接口,传入被代理的类 21 22 InvocationHandler handler = new HelloHandler(hello); 23 24 // 生成代理类 25 // 1. 给我被代理类的类加载器加载被代理的类,为啥要类加载器呢 26 // 2. 给我被代理类的接口信息 27 // 3. 给我如何增强接口中的方法信息 28 Hello proxy = (Hello) Proxy.newProxyInstance(hello.getClass() 29 .getClassLoader(), hello.getClass().getInterfaces(), handler); 30 // 调用代理的方法 31 proxy.sayHello(); 32 } 33 }
2. 使用包装类进行包装
包装类中有一个被包装的类的引用作为属性,和被包装类实现相同的接口,然后在实现接口的方法中调用和被包装类同名的接口中的方法实现方法的增强。这种解决方案的缺点就是当接口中的方法过多的时候就需要为接口中的每个方法都写一遍,即时不需要增强的方法也需要实现。
另一种做法是继承被包装的类,然后在包装类中重写需要增强的方法,但是缺点是只有在被包装类出现的地方才能使用被包装类,而实现接口的方法是在接口出现的地方都可以使用包装类。继承了被包装类之后的类,就是去了接口的好处,在包装类作为引用类型的地方必须的是被包装类及其子类,而不是接口出现。
3. 使用动态代理
使用动态代理则可以不需要实现接口中所有的方法,又可以获得接口的好处。动态代理要求要包装的对象必须实现一个接口,该接口定义了准备在包装器中使用的所有方法,这一限制是鼓励面向接口的编程,而不是限制编程,根据经验,每个类至少应该实现一个接口。良好的接口用法不仅使动态代理成为可能,还有利于程序的模块化。
创建动态代理类
调用动态代理类
主要的步骤有:
首先根据被代理对象创建一个代理类
创建动态代理对象的proxy1,第一个参数为被代理类的类加载器,第二个参数为该类的接口,第三个对象为代理对象的实例。
通过调用代理对象中增强的方法
上述过程就是告诉Proxy类用一个指定的类加载器来动态创建一个对象,该对象要实现指定的接口,并用提供的InvocationHandler来代替传统的方法主体。在实现InvocationHandler接口的类中invoke()方法中,完全不存在对Hello接口的引用,在上述的例子中,以构造方法的传参的形式,为InvocationHandler的实现类提供了被代理接口的实例。代理接口实例上的任何方法调用最终都由InvocationHandler的实例委托给代理接口实例,这是最常见的设计。但是,InvocationHandler实例不一定非要委托给被代理的接口的另一个实例。事实上,InvocationHandler可以自行提供方法主体,完全不必委托给被代理类来实现。如果被代理接口发生变化,InvocationHandler中的实例仍然是可用的。
动态代理可以实现许多AOP方面的功能:安全、事务、日志、过滤、编码、解码等功能,而且纯粹是热插拔的。AOP的好处是可以对类的实例统一实现拦截操作,通常应用在日志、权限、缓存、连接池中等。
基于动态代理的字节码库
了解动态代理的原理之后,我们完全可以自己实现这样一个动态代理,只要生成该类的class文件的内存映像即可。有很多这样的工具。
BCEL:
SERP:
ASM:Java字节码汇编语言。ASM是轻量级的Java字节码处理框架,可以动态生成二进制格式的stub类或其他代理类,或者在类被Java虚拟机装载之前动态修改类。ASM设计的目的就是在运行时使用,因此要尽量体积小速度快。
cglib:Code Generation Library的缩写,依赖与ASM库。Spring和Hibernate选择了同样的cglib包。Hibernate主要利用cglib生成pojo的子类并用override get方法来实现lazy loading机制,Spring则是利用cglib来实现动态代理。JDK的动态代理必须得实现接口才行。
使用CGLib进行动态代理
待增强的类
1 package com.tuhooo.cglib.demo; 2 3 public class HelloWorld{ 4 public void savePerson() { 5 System.out.println("hello, world!"); 6 } 7 }
增强类
1 package com.tuhooo.cglib.demo; 2 3 import java.lang.reflect.Method; 4 import net.sf.cglib.proxy.Enhancer; 5 import net.sf.cglib.proxy.MethodInterceptor; 6 import net.sf.cglib.proxy.MethodProxy; 7 8 public class HelloWorldInterceptor implements MethodInterceptor{ 9 10 // 被增强的类 11 private Object target; 12 // 如何增强方法的信息 13 private Strength strength; 14 15 public HelloWorldInterceptor(Object target, Strength strength) { 16 super(); 17 this.target = target; 18 this.strength = strength; 19 } 20 21 /** 22 * 用来产生代理对象 23 */ 24 public Object createProxy(){ 25 // 增强类 26 Enhancer enhancer = new Enhancer(); 27 // 设置增强类的回调方法 28 enhancer.setCallback(this); 29 //设置代理类的父类 30 enhancer.setSuperclass(HelloWorld.class); 31 return enhancer.create(); 32 } 33 34 // 这里相当于InvocationHandler接口中的invoke方法 35 public Object intercept(Object arg0, Method method, Object[] arg2, 36 MethodProxy arg3) throws Throwable { 37 this.strength.begin(); 38 method.invoke(target, arg2); 39 this.strength.end(); 40 return null; 41 } 42 }
增强的方法
1 package com.tuhooo.cglib.demo; 2 3 // 待增强的方法 4 public class Strength { 5 // 前增强 6 public void begin(){ 7 System.out.println("begin......"); 8 } 9 // 后增强 10 public void end(){ 11 System.out.println("end......."); 12 } 13 }
测试增强
1 package com.tuhooo.cglib.demo; 2 3 import org.junit.Test; 4 5 /** 6 * 使用cglib产生的代理类,其代理类是目标类的子类 7 */ 8 public class TestHello { 9 @Test 10 public void testPersonDaoProxy(){ 11 Object target = new HelloWorld(); 12 Strength strength = new Strength(); 13 HelloWorldInterceptor interceptor = new HelloWorldInterceptor(target, strength); 14 HelloWorld helloWorldProxy = (HelloWorld)interceptor.createProxy(); 15 helloWorldProxy.savePerson(); 16 } 17 }
InvocationHandler源码
1 package java.lang.reflect; 2 3 /** 4 * 每个代理类实例都有一个与之关联的invocation句柄。 5 * 当代理类的实例调用一个方法的时候, 6 * 该方法的invocation被编码并且被分发到它对应的invocation句柄 7 */ 8 public interface InvocationHandler { 9 10 public Object invoke(Object proxy, Method method, Object[] args) 11 throws Throwable; 12 }
Proxy的源码
1 package java.lang.reflect; 2 3 import java.lang.ref.Reference; 4 import java.lang.ref.WeakReference; 5 import java.security.AccessController; 6 import java.security.Permission; 7 import java.security.PrivilegedAction; 8 import java.util.Arrays; 9 import java.util.Collections; 10 import java.util.HashMap; 11 import java.util.HashSet; 12 import java.util.Map; 13 import java.util.Set; 14 import java.util.List; 15 import java.util.WeakHashMap; 16 import sun.misc.ProxyGenerator; 17 import sun.reflect.CallerSensitive; 18 import sun.reflect.Reflection; 19 import sun.reflect.misc.ReflectUtil; 20 import sun.security.util.SecurityConstants; 21 22 /** 23 * Proxy类提供了用于创建代理对象和实例的静态方法, 24 并且它也是所有通过这些静态方法创建出的代理类的父类。 25 * 26 * 从某个接口创建代理对象的方法如下: 27 * 28 * InvocationHandler handler = new MyInvocationHandler(...); 29 * Class proxyClass = Proxy.getProxyClass( 30 * Foo.class.getClassLoader(), new Class[] { Foo.class }); 31 * Foo f = (Foo) proxyClass. 32 * getConstructor(new Class[] { InvocationHandler.class }). 33 * newInstance(new Object[] { handler }); 34 * 或者有更简单的方法 35 * Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), 36 * new Class[] { Foo.class }, handler); 37 * 38 * 一个动态代理类(以下简称为代理类)是该类被创建时在运行时动态 39 * 实现一系列接口的类,有如下的特性。 40 * 41 * 动态代理接口是被动态类实现的接口。 42 * 代理实例是代理类一个一个实例。 43 * 44 * 每个代理实例都和一个实现了InvocationHandler接口的invocation handler对象关联。 45 * 通过代理接口进行的代理类实例的方法调用将会被分发给invocation handler实例,同时传递的 46 * 参数有代理实例,确定被调用方法的Method对象和Object数组类型的参数。 47 * 48 * 49 * containing the arguments. The invocation handler processes the 50 * encoded method invocation as appropriate and the result that it 51 * returns will be returned as the result of the method invocation on 52 * the proxy instance. 53 * 54 * <p>A proxy class has the following properties: 55 * 56 * <ul> 57 * <li>Proxy classes are public, final, and not abstract. 58 * 59 * <li>The unqualified name of a proxy class is unspecified. The space 60 * of class names that begin with the string {@code "$Proxy"} 61 * should be, however, reserved for proxy classes. 62 * 63 * <li>A proxy class extends {@code java.lang.reflect.Proxy}. 64 * 65 * <li>A proxy class implements exactly the interfaces specified at its 66 * creation, in the same order. 67 * 68 * <li>If a proxy class implements a non-public interface, then it will 69 * be defined in the same package as that interface. Otherwise, the 70 * package of a proxy class is also unspecified. Note that package 71 * sealing will not prevent a proxy class from being successfully defined 72 * in a particular package at runtime, and neither will classes already 73 * defined by the same class loader and the same package with particular 74 * signers. 75 * 76 * <li>Since a proxy class implements all of the interfaces specified at 77 * its creation, invoking {@code getInterfaces} on its 78 * {@code Class} object will return an array containing the same 79 * list of interfaces (in the order specified at its creation), invoking 80 * {@code getMethods} on its {@code Class} object will return 81 * an array of {@code Method} objects that include all of the 82 * methods in those interfaces, and invoking {@code getMethod} will 83 * find methods in the proxy interfaces as would be expected. 84 * 85 * <li>The {@link Proxy#isProxyClass Proxy.isProxyClass} method will 86 * return true if it is passed a proxy class-- a class returned by 87 * {@code Proxy.getProxyClass} or the class of an object returned by 88 * {@code Proxy.newProxyInstance}-- and false otherwise. 89 * 90 * <li>The {@code java.security.ProtectionDomain} of a proxy class 91 * is the same as that of system classes loaded by the bootstrap class 92 * loader, such as {@code java.lang.Object}, because the code for a 93 * proxy class is generated by trusted system code. This protection 94 * domain will typically be granted 95 * {@code java.security.AllPermission}. 96 * 97 * <li>Each proxy class has one public constructor that takes one argument, 98 * an implementation of the interface {@link InvocationHandler}, to set 99 * the invocation handler for a proxy instance. Rather than having to use 100 * the reflection API to access the public constructor, a proxy instance 101 * can be also be created by calling the {@link Proxy#newProxyInstance 102 * Proxy.newProxyInstance} method, which combines the actions of calling 103 * {@link Proxy#getProxyClass Proxy.getProxyClass} with invoking the 104 * constructor with an invocation handler. 105 * </ul> 106 * 107 * <p>A proxy instance has the following properties: 108 * 109 * <ul> 110 * <li>Given a proxy instance {@code proxy} and one of the 111 * interfaces implemented by its proxy class {@code Foo}, the 112 * following expression will return true: 113 * <pre> 114 * {@code proxy instanceof Foo} 115 * </pre> 116 * and the following cast operation will succeed (rather than throwing 117 * a {@code ClassCastException}): 118 * <pre> 119 * {@code (Foo) proxy} 120 * </pre> 121 * 122 * <li>Each proxy instance has an associated invocation handler, the one 123 * that was passed to its constructor. The static 124 * {@link Proxy#getInvocationHandler Proxy.getInvocationHandler} method 125 * will return the invocation handler associated with the proxy instance 126 * passed as its argument. 127 * 128 * <li>An interface method invocation on a proxy instance will be 129 * encoded and dispatched to the invocation handler‘s {@link 130 * InvocationHandler#invoke invoke} method as described in the 131 * documentation for that method. 132 * 133 * <li>An invocation of the {@code hashCode}, 134 * {@code equals}, or {@code toString} methods declared in 135 * {@code java.lang.Object} on a proxy instance will be encoded and 136 * dispatched to the invocation handler‘s {@code invoke} method in 137 * the same manner as interface method invocations are encoded and 138 * dispatched, as described above. The declaring class of the 139 * {@code Method} object passed to {@code invoke} will be 140 * {@code java.lang.Object}. Other public methods of a proxy 141 * instance inherited from {@code java.lang.Object} are not 142 * overridden by a proxy class, so invocations of those methods behave 143 * like they do for instances of {@code java.lang.Object}. 144 * </ul> 145 * 146 * <h3>Methods Duplicated in Multiple Proxy Interfaces</h3> 147 * 148 * <p>When two or more interfaces of a proxy class contain a method with 149 * the same name and parameter signature, the order of the proxy class‘s 150 * interfaces becomes significant. When such a <i>duplicate method</i> 151 * is invoked on a proxy instance, the {@code Method} object passed 152 * to the invocation handler will not necessarily be the one whose 153 * declaring class is assignable from the reference type of the interface 154 * that the proxy‘s method was invoked through. This limitation exists 155 * because the corresponding method implementation in the generated proxy 156 * class cannot determine which interface it was invoked through. 157 * Therefore, when a duplicate method is invoked on a proxy instance, 158 * the {@code Method} object for the method in the foremost interface 159 * that contains the method (either directly or inherited through a 160 * superinterface) in the proxy class‘s list of interfaces is passed to 161 * the invocation handler‘s {@code invoke} method, regardless of the 162 * reference type through which the method invocation occurred. 163 * 164 * <p>If a proxy interface contains a method with the same name and 165 * parameter signature as the {@code hashCode}, {@code equals}, 166 * or {@code toString} methods of {@code java.lang.Object}, 167 * when such a method is invoked on a proxy instance, the 168 * {@code Method} object passed to the invocation handler will have 169 * {@code java.lang.Object} as its declaring class. In other words, 170 * the public, non-final methods of {@code java.lang.Object} 171 * logically precede all of the proxy interfaces for the determination of 172 * which {@code Method} object to pass to the invocation handler. 173 * 174 * <p>Note also that when a duplicate method is dispatched to an 175 * invocation handler, the {@code invoke} method may only throw 176 * checked exception types that are assignable to one of the exception 177 * types in the {@code throws} clause of the method in <i>all</i> of 178 * the proxy interfaces that it can be invoked through. If the 179 * {@code invoke} method throws a checked exception that is not 180 * assignable to any of the exception types declared by the method in one 181 * of the proxy interfaces that it can be invoked through, then an 182 * unchecked {@code UndeclaredThrowableException} will be thrown by 183 * the invocation on the proxy instance. This restriction means that not 184 * all of the exception types returned by invoking 185 * {@code getExceptionTypes} on the {@code Method} object 186 * passed to the {@code invoke} method can necessarily be thrown 187 * successfully by the {@code invoke} method. 188 * 189 * @author Peter Jones 190 * @see InvocationHandler 191 * @since 1.3 192 */ 193 194 // 代理对象是可以序列化的 195 public class Proxy implements java.io.Serializable { 196 197 private static final long serialVersionUID = -2222568056686623797L; 198 199 /** 为所有代理类的类名添加前缀,难怪hibernate中很多类都有$Proxy */ 200 private final static String proxyClassNamePrefix = "$Proxy"; 201 202 /** 代理类构造方法的参数类型 */ 203 private final static Class[] constructorParams = 204 { InvocationHandler.class }; 205 206 /** 将代理类的类加载器放在Proxy的maps结构中缓存起来 */ 207 /** 为啥这里用了WeakHashMap() */ 208 private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache 209 = new WeakHashMap<>(); 210 211 /** 用于标记正在生成的代理类 */ 212 private static Object pendingGenerationMarker = new Object(); 213 214 /** 下一个用于生成唯一代理类名字的数字 */ 215 private static long nextUniqueNumber = 0; 216 private static Object nextUniqueNumberLock = new Object(); 217 218 /** 所有代理类的集合,用于isProxyClass方法的实现 */ 219 private static Map<Class<?>, Void> proxyClasses = 220 Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>()); 221 222 /** 223 * 代理实例的invocationHandler对象,原来放在这儿啊. 224 * @serial 225 */ 226 protected InvocationHandler h; 227 228 /** 229 * 禁止实例化 230 */ 231 private Proxy() { 232 } 233 234 /** 235 * 从一个子类中创建代理类的实例(最典型的是一个动态代理类) 236 * 构造方法的参数是用来指定invocation handler 237 * 这个方法还是受保护的呢 238 */ 239 protected Proxy(InvocationHandler h) { 240 doNewInstanceCheck(); 241 this.h = h; 242 } 243 244 /** 245 * 为什么不注释这个方法? 246 * 类名大致意思是代理访问助手? 247 * 这还是个静态的内部类 248 */ 249 private static class ProxyAccessHelper { 250 // The permission is implementation specific. 251 // 权限是指定实现的。 252 static final Permission PROXY_PERMISSION = 253 new ReflectPermission("proxyConstructorNewInstance"); 254 // These system properties are defined to provide a short-term 255 // workaround if customers need to disable the new security checks. 256 // 这些系统配置被定义用来提供短期的配置,如果用户需要禁止新的安全检查。 257 static final boolean allowNewInstance; 258 static final boolean allowNullLoader; 259 static { 260 allowNewInstance = getBooleanProperty("sun.reflect.proxy.allowsNewInstance"); 261 allowNullLoader = getBooleanProperty("sun.reflect.proxy.allowsNullLoader"); 262 } 263 264 // 方法参数还可以这样修饰final? 265 private static boolean getBooleanProperty(final String key) { 266 String s = AccessController.doPrivileged(new PrivilegedAction<String>() { 267 public String run() { 268 return System.getProperty(key); 269 } 270 }); 271 return Boolean.valueOf(s); 272 } 273 274 // 需要进行创建新实例的检查么 275 static boolean needsNewInstanceCheck(Class<?> proxyClass) { 276 if (!Proxy.isProxyClass(proxyClass) || allowNewInstance) { 277 return false; 278 } 279 280 if (ReflectUtil.isNonPublicProxyClass(proxyClass)) { 281 for (Class<?> intf : proxyClass.getInterfaces()) { 282 if (!Modifier.isPublic(intf.getModifiers())) { 283 return true; 284 } 285 } 286 } 287 return false; 288 } 289 } 290 291 /* 292 * Access check on a proxy class that implements any non-public interface. 293 * 294 * @throws SecurityException if a security manager exists, and 295 * the caller does not have the permission. 296 */ 297 private void doNewInstanceCheck() { 298 SecurityManager sm = System.getSecurityManager(); 299 Class<?> proxyClass = this.getClass(); 300 if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(proxyClass)) { 301 try { 302 sm.checkPermission(ProxyAccessHelper.PROXY_PERMISSION); 303 } catch (SecurityException e) { 304 throw new SecurityException("Not allowed to construct a Proxy " 305 + "instance that implements a non-public interface", e); 306 } 307 } 308 } 309 310 /** 311 * 这个方法是重头了 312 * 从给定的类加载器和接口数组返回动态代理类的Class对象。 313 * Returns the {@code java.lang.Class} object for a proxy class 314 * given a class loader and an array of interfaces. 315 * 代理将会通过指定的类加载器所定义而且将会实现所提供的所有的接口。 316 * 317 * 如果已经有实现了相同接口的代理类存在,那么将会直接放回已经存在的代理类; 318 * 否则,将会动态地创建和由类加载器定义的代理类被返回。 319 320 * 对于传递给Proxy.getProxyClass方法的参数有若干限制。 321 * 322 * 在数组中所有的类类型必须是接口,不是其他的类类型或者原始类型。 323 * 接口数组中的接口必须两两不同 324 * 所有的接口类型都可以通过指定的类加载器按名字进行加载。 325 * 也就是对于数组中的每个接口i如下判断必须为true 326 * Class.forName(i.getName(), false, cl) == i 327 * 328 * 所有非公开的接口都必须在相同的包里面;不然的话,代理类不可能实现所有的接口, 329 * 不论它是在哪个包里面定义的。 330 * 331 * 对于指定接口的成员方法的集合的中的方法必须有相同的签名。 332 * 333 * 如果任何方法的返回类型是原始类型或者为空,那么所有的的方法都必须有相同的返回类型。 334 * 335 * 不然,其中一个方法必须可以对其他方法的返回类型是可赋值的。 336 * 337 * 生成的代理类不能超过虚拟机对其中的类的限制。例如,虚拟机会限制一个类的接口个数不超过65535; 338 * 因此传进去的接口的数组的长度不能超过65535 339 * 340 * 如果以上约束有违反的话,将会抛出IllegalArgumentException异常。 341 * 如果有接口是null的话,就会报NullPointerException异常。 342 * 343 * 注意到特定的代理接口的顺序是有意义的:对于创建具有相同代理接口的不同顺序的请求生成的代理类是不同的两个 344 * 代理类。(卧槽,这个也有影响,不读下源码一脸懵逼) 345 */ 346 @CallerSensitive 347 public static Class<?> getProxyClass(ClassLoader loader, 348 Class<?>... interfaces) 349 throws IllegalArgumentException 350 { 351 // 安全管理 352 SecurityManager sm = System.getSecurityManager(); 353 if (sm != null) { 354 checkProxyAccess(Reflection.getCallerClass(), loader, interfaces); 355 } 356 357 // 返回代理对象 358 return getProxyClass0(loader, interfaces); 359 } 360 361 /* 362 * Check permissions required to create a Proxy class. 363 * 364 * To define a proxy class, it performs the access checks as in 365 * Class.forName (VM will invoke ClassLoader.checkPackageAccess): 366 * 1. "getClassLoader" permission check if loader == null 367 * 2. checkPackageAccess on the interfaces it implements 368 * 369 * To get a constructor and new instance of a proxy class, it performs 370 * the package access check on the interfaces it implements 371 * as in Class.getConstructor. 372 * 373 * If an interface is non-public, the proxy class must be defined by 374 * the defining loader of the interface. If the caller‘s class loader 375 * is not the same as the defining loader of the interface, the VM 376 * will throw IllegalAccessError when the generated proxy class is 377 * being defined via the defineClass0 method. 378 */ 379 private static void checkProxyAccess(Class<?> caller, 380 ClassLoader loader, 381 Class<?>... interfaces) 382 { 383 SecurityManager sm = System.getSecurityManager(); 384 if (sm != null) { 385 ClassLoader ccl = caller.getClassLoader(); 386 if (loader == null && ccl != null) { 387 if (!ProxyAccessHelper.allowNullLoader) { 388 sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); 389 } 390 } 391 ReflectUtil.checkProxyPackageAccess(ccl, interfaces); 392 } 393 } 394 395 /** 396 * Generate a proxy class. Must call the checkProxyAccess method 397 * to perform permission checks before calling this. 398 */ 399 private static Class<?> getProxyClass0(ClassLoader loader, 400 Class<?>... interfaces) { 401 402 // 判断接口的个数 403 if (interfaces.length > 65535) { 404 throw new IllegalArgumentException("interface limit exceeded"); 405 } 406 407 // 代理类 408 Class<?> proxyClass = null; 409 410 /* 接口名称的集合用来作为代理类的缓存 */ 411 String[] interfaceNames = new String[interfaces.length]; 412 413 // 检查接口是否有重复的 414 Set<Class<?>> interfaceSet = new HashSet<>(); 415 416 for (int i = 0; i < interfaces.length; i++) { 417 /* 418 * 验证类加载器对接口名称的解析是同一个对象 419 * 因为不同类加载加载的类是不同的 420 */ 421 422 // 获取接口的名字 423 String interfaceName = interfaces[i].getName(); 424 Class<?> interfaceClass = null; 425 try { 426 // 拿到接口的类类型 427 interfaceClass = Class.forName(interfaceName, false, loader); 428 } catch (ClassNotFoundException e) { 429 } 430 431 // 两个类类型之间的比较 432 if (interfaceClass != interfaces[i]) { 433 throw new IllegalArgumentException( 434 interfaces[i] + " is not visible from class loader"); 435 } 436 437 /* 438 * Verify that the Class object actually represents an 439 * interface. 440 */ 441 if (!interfaceClass.isInterface()) { 442 throw new IllegalArgumentException( 443 interfaceClass.getName() + " is not an interface"); 444 } 445 446 /* 447 * Verify that this interface is not a duplicate. 448 */ 449 if (interfaceSet.contains(interfaceClass)) { 450 throw new IllegalArgumentException( 451 "repeated interface: " + interfaceClass.getName()); 452 } 453 454 // 将接口加入到集合中 455 interfaceSet.add(interfaceClass); 456 457 interfaceNames[i] = interfaceName; 458 } 459 460 /* 461 * 使用字符串代表代理接口作为代理类缓存的主键(而不是它们的类对象) 462 * 已经足够了因为我们要求代理接口必须能根据给定的类加载器通过名字被解析, 463 * 而且还有一个好处是,使用字符串代表类弥补了对类的隐式弱引用。 464 */ 465 466 // 将数组转为字符串 467 List<String> key = Arrays.asList(interfaceNames); 468 469 /* 470 * 根据类加载器找到或者创建代理类的缓存. 471 */ 472 Map<List<String>, Object> cache; 473 synchronized (loaderToCache) { 474 cache = loaderToCache.get(loader); 475 if (cache == null) { 476 cache = new HashMap<>(); 477 loaderToCache.put(loader, cache); 478 } 479 /* 480 * 这个映射在这个方法执行时一直有效,不用过多的同步, 481 * 因为仅仅会在类加载器不可达的时候被移除。 482 */ 483 } 484 485 /* 486 * Look up the list of interfaces in the proxy class cache using 487 * the key. This lookup will result in one of three possible 488 * kinds of values: 489 * null, if there is currently no proxy class for the list of 490 * interfaces in the class loader, 491 * the pendingGenerationMarker object, if a proxy class for the 492 * list of interfaces is currently being generated, 493 * or a weak reference to a Class object, if a proxy class for 494 * the list of interfaces has already been generated. 495 */ 496 synchronized (cache) { 497 /* 498 * Note that we need not worry about reaping the cache for 499 * entries with cleared weak references because if a proxy class 500 * has been garbage collected, its class loader will have been 501 * garbage collected as well, so the entire cache will be reaped 502 * from the loaderToCache map. 503 */ 504 do { 505 Object value = cache.get(key); 506 if (value instanceof Reference) { 507 proxyClass = (Class<?>) ((Reference) value).get(); 508 } 509 if (proxyClass != null) { 510 // proxy class already generated: return it 511 return proxyClass; 512 } else if (value == pendingGenerationMarker) { 513 // proxy class being generated: wait for it 514 try { 515 cache.wait(); 516 } catch (InterruptedException e) { 517 /* 518 * The class generation that we are waiting for should 519 * take a small, bounded time, so we can safely ignore 520 * thread interrupts here. 521 */ 522 } 523 continue; 524 } else { 525 /* 526 * No proxy class for this list of interfaces has been 527 * generated or is being generated, so we will go and 528 * generate it now. Mark it as pending generation. 529 */ 530 cache.put(key, pendingGenerationMarker); 531 break; 532 } 533 } while (true); 534 } 535 536 try { 537 String proxyPkg = null; // package to define proxy class in 538 539 /* 540 * Record the package of a non-public proxy interface so that the 541 * proxy class will be defined in the same package. Verify that 542 * all non-public proxy interfaces are in the same package. 543 */ 544 for (int i = 0; i < interfaces.length; i++) { 545 int flags = interfaces[i].getModifiers(); 546 if (!Modifier.isPublic(flags)) { 547 String name = interfaces[i].getName(); 548 int n = name.lastIndexOf(‘.‘); 549 String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); 550 if (proxyPkg == null) { 551 proxyPkg = pkg; 552 } else if (!pkg.equals(proxyPkg)) { 553 throw new IllegalArgumentException( 554 "non-public interfaces from different packages"); 555 } 556 } 557 } 558 559 if (proxyPkg == null) { 560 // if no non-public proxy interfaces, use com.sun.proxy package 561 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; 562 } 563 564 { 565 /* 566 * Choose a name for the proxy class to generate. 567 */ 568 long num; 569 synchronized (nextUniqueNumberLock) { 570 num = nextUniqueNumber++; 571 } 572 String proxyName = proxyPkg + proxyClassNamePrefix + num; 573 /* 574 * Verify that the class loader hasn‘t already 575 * defined a class with the chosen name. 576 */ 577 578 /* 579 * 生成指定的代理类,可以看出这里的是二进制类型 580 * ProxyGenerator的源码看不到了 581 */ 582 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( 583 proxyName, interfaces); 584 try { 585 // defineClass0是一个本地方法 586 proxyClass = defineClass0(loader, proxyName, 587 proxyClassFile, 0, proxyClassFile.length); 588 } catch (ClassFormatError e) { 589 /* 590 * A ClassFormatError here means that (barring bugs in the 591 * proxy class generation code) there was some other 592 * invalid aspect of the arguments supplied to the proxy 593 * class creation (such as virtual machine limitations 594 * exceeded). 595 */ 596 throw new IllegalArgumentException(e.toString()); 597 } 598 } 599 // 将其加入到所有生成的代理类的集合中,用于判断是否是代理类. 600 proxyClasses.put(proxyClass, null); 601 602 } finally { 603 /* 604 * We must clean up the "pending generation" state of the proxy 605 * class cache entry somehow. If a proxy class was successfully 606 * generated, store it in the cache (with a weak reference); 607 * otherwise, remove the reserved entry. In all cases, notify 608 * all waiters on reserved entries in this cache. 609 */ 610 synchronized (cache) { 611 if (proxyClass != null) { 612 cache.put(key, new WeakReference<Class<?>>(proxyClass)); 613 } else { 614 cache.remove(key); 615 } 616 cache.notifyAll(); 617 } 618 } 619 620 // 返回代理类 621 return proxyClass; 622 } 623 624 @CallerSensitive 625 public static Object newProxyInstance(ClassLoader loader, 626 Class<?>[] interfaces, 627 InvocationHandler h) 628 throws IllegalArgumentException 629 { 630 if (h == null) { 631 throw new NullPointerException(); 632 } 633 634 // 安全检查 635 final SecurityManager sm = System.getSecurityManager(); 636 if (sm != null) { 637 checkProxyAccess(Reflection.getCallerClass(), loader, interfaces); 638 } 639 640 /* 641 * 查找或者生成指定的代理类 642 */ 643 Class<?> cl = getProxyClass0(loader, interfaces); 644 645 /* 646 * 调用指定了invocation handler的构造方法。 647 */ 648 try { 649 final Constructor<?> cons = cl.getConstructor(constructorParams); 650 final InvocationHandler ih = h; 651 if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { 652 // create proxy instance with doPrivilege as the proxy class may 653 // implement non-public interfaces that requires a special permission 654 return AccessController.doPrivileged(new PrivilegedAction<Object>() { 655 public Object run() { 656 return newInstance(cons, ih); 657 } 658 }); 659 } else { 660 // 直接返回了代理对象 661 return newInstance(cons, ih); 662 } 663 } catch (NoSuchMethodException e) { 664 throw new InternalError(e.toString()); 665 } 666 } 667 668 private static Object newInstance(Constructor<?> cons, InvocationHandler h) { 669 try { 670 return cons.newInstance(new Object[] {h} ); 671 } catch (IllegalAccessException | InstantiationException e) { 672 throw new InternalError(e.toString()); 673 } catch (InvocationTargetException e) { 674 Throwable t = e.getCause(); 675 if (t instanceof RuntimeException) { 676 throw (RuntimeException) t; 677 } else { 678 throw new InternalError(t.toString()); 679 } 680 } 681 } 682 683 /** 684 * Returns true if and only if the specified class was dynamically 685 * generated to be a proxy class using the {@code getProxyClass} 686 * method or the {@code newProxyInstance} method. 687 * 688 * <p>The reliability of this method is important for the ability 689 * to use it to make security decisions, so its implementation should 690 * not just test if the class in question extends {@code Proxy}. 691 * 692 * @param cl the class to test 693 * @return {@code true} if the class is a proxy class and 694 * {@code false} otherwise 695 * @throws NullPointerException if {@code cl} is {@code null} 696 */ 697 public static boolean isProxyClass(Class<?> cl) { 698 if (cl == null) { 699 throw new NullPointerException(); 700 } 701 702 return proxyClasses.containsKey(cl); 703 } 704 705 /** 706 * Returns the invocation handler for the specified proxy instance. 707 * 708 * @param proxy the proxy instance to return the invocation handler for 709 * @return the invocation handler for the proxy instance 710 * @throws IllegalArgumentException if the argument is not a 711 * proxy instance 712 */ 713 @CallerSensitive 714 public static InvocationHandler getInvocationHandler(Object proxy) 715 throws IllegalArgumentException 716 { 717 /* 718 * Verify that the object is actually a proxy instance. 719 */ 720 if (!isProxyClass(proxy.getClass())) { 721 throw new IllegalArgumentException("not a proxy instance"); 722 } 723 724 final Proxy p = (Proxy) proxy; 725 final InvocationHandler ih = p.h; 726 if (System.getSecurityManager() != null) { 727 Class<?> ihClass = ih.getClass(); 728 Class<?> caller = Reflection.getCallerClass(); 729 if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(), 730 ihClass.getClassLoader())) 731 { 732 ReflectUtil.checkPackageAccess(ihClass); 733 } 734 } 735 736 return ih; 737 } 738 739 private static native Class defineClass0(ClassLoader loader, String name, 740 byte[] b, int off, int len); 741 }
总结Proxy:
以上是关于Java中的动态代理以及Proxy类的偷瞄的主要内容,如果未能解决你的问题,请参考以下文章
Java动态代理Proxy.newProxyInstance源码到底干了什么?2021-8-31