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中的动态代理实现数据库连接池
2002 年 12 月 05 日 作者通过使用JAVA中的动态代理实现数据库连接池,使使用者可以以普通的jdbc连接的使用习惯来使用连接池. 数据库连接池在编写应用服务是经常需要用到的模块,太过频繁的 ...
- java中的动态代理机制
java中的动态代理机制 在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Class),这一个类和接口是实现 ...
- 十分钟理解Java中的动态代理
十分钟理解 Java 中的动态代理 一.概述 1. 什么是代理 我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品.关于微商代理,首先我们从他们那里买东西时通常不知道 ...
- 深度剖析java中JDK动态代理机制
https://www.jb51.net/article/110342.htm 本篇文章主要介绍了深度剖析java中JDK动态代理机制 ,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定 ...
- 一文读懂Java中的动态代理
从代理模式说起 回顾前文: 设计模式系列之代理模式(Proxy Pattern) 要读懂动态代理,应从代理模式说起.而实现代理模式,常见有下面两种实现: (1) 代理类关联目标对象,实现目标对象实现的 ...
- 浅谈代理模式与java中的动态代理
代理模式的定义: 代理模式是一个使用律非常高的模式,定义如下: 为其他对象提供一种代理,以控制对这个对象的访问. 类图: 简单的静态代理: public interface IRunner{ //这是 ...
- java中的动态代理Proxy
动态代理是java语言的一个神奇的地方,不是很好理解,下面来看看关键的地方. InvocationHandler 是一个接口,官方文档解释说,每个代理的实例都有一个与之关联的 InvocationHa ...
- java中的动态代理
1.动态代理的定义:为其他对象提供一个代理以控制对这个对象的访问 2.通过api看下proxy生成代理类的2中写法: 创建某一接口 Foo 的代理: InvocationHandler handler ...
- 代理模式与java中的动态代理
前言 代理模式又分为静态代理与动态代理,其中动态代理是Java各大框架中运用的最为广泛的一种模式之一,下面就用简单的例子来说明静态代理与动态代理. 场景 李雷是一个唱片公司的大老板,很忙, ...
随机推荐
- Android UI Material Design
Material Design 中文版: http://wiki.jikexueyuan.com/project/material-design/ Material Design开发文章系列1:App ...
- JavaScript 的闭包用于什么场景
本文翻译自 MDN ( Mozilla Developer Network ): 原文地址:MDN 译文地址:shixinzhang 的博客 读完本文你将了解到: 词法作用域 闭包 闭包实战场景之回调 ...
- 几种常用的json序列化和反序列化工具介绍
一.前言 Json序列化和反序列化工作中会时常用到,也是目前数据交互的常用格式,Rest风格的接口加上json格式的数据交互,真的是天作之合. 目前Json字符与Json对象的相互转换方式有很多,接下 ...
- android多线程-AsyncTask之工作原理深入解析(下)
关联文章: Android 多线程之HandlerThread 完全详解 Android 多线程之IntentService 完全详解 android多线程-AsyncTask之工作原理深入解析(上) ...
- [Linux] ubuntu 格式化u盘
$sudo fdisks -l 基本功,格式化命令,以格式化 /dev/sdb4 分区为例:$ sudo umount /dev/sdb4 # 必须先卸载该分区 # 格式化为 FAT 分区$ s ...
- Mac的SourceTree安装时如何注册?
目前安装SourceTree的过程中是必须注册的,但是注册又是国外的,因此需要FQ.但是未注册成功,是没有办法设置SourceTree的代理的,虽然不知道SourceTree有没有设置代理的功能,解决 ...
- php漏洞挖掘书籍
PHP是一种被广泛使用的脚本语言,尤其适合web开发.具有跨平台,容易学习,功能强大等特点,据统计全世界超过34%的网站有php的应用,包括Yahoo.sina.163.sohu等大型门户网站.而且很 ...
- spring+mybatis项目启动报错Initializing Spring root WebApplicationContext
这个问题很怪异,各种各样的情况都会导致这个问题的出现,主要是由于sping加载读取配置文件的时候出了问题.我在处理mybatis的时候出现了这个问题,后来排查发现,在mybatis的配置文件中如果有大 ...
- 【Bootstrap 多级菜单】
参考资料: Bootstrap-submenu:http://www.html580.com/11848/demo Bootstrap-submenu:https://vsn4ik.github.io ...
- java 压缩和解压zip包
网上有关压缩和解压zip包的博文一大堆,我随便找了一个.看了看,依照自己的须要改动了一下,与各位分享一下,希望各位大神指正: package com.wangpeng.utill; import ja ...