https://i-blog.csdnimg.cn/blog_migrate/9cf950b161ffd6e53521492968bd213c.png
JDK动态代理(proxy)可以在运行时创建一个实现一组给定接口的新类。但是略有限制,即被代理的类必须实现某个接口,否则无法使用JDK自带的动态代理,因此,如果不满足条件,就只能使用另一种更加灵活,功能更加强大的动态代理技术—— CGLIB。Spring里会自动在JDK的代理和CGLIB之间切换,同时我们也可以强制Spring使用CGLIB。
闲话少说,下面先用实例介绍使用方式,接着从proxy类源码角度分析实现过程:
1.JDK动态代理实例
(1)动态代理首先提供一个调度处理器接口(Invocationhandler),该接口实例封装了我们要代理的对象实例的数据。
- public class TranceHander implements InvocationHandler{
- private Object tObject;
- public TranceHander(Object t){
- tObject=t;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- // TODO Auto-generated method stub
- System.out.println("被代理对象:"+tObject);
- System.out.println("方法:"+method.getName());
- return method.invoke(tObject,args);//方法反射
- }
- }
(2)使用Proxy类的newProxyInstance方法创建代理对象或getProxyClass方法获得代理类Class。
测试mian方法中,写了一个代理类,从1-1000的整数数组中使用二分查找随机查找一个整数,实现对象比较和toString接口代理
- public class ProxyTest {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- Object[] elements=new Object[1000];
- for(int i=0;i<elements.length;i++){
- Integer value=i+1;//被代理对象
- InvocationHandler handler=new TranceHander(value);//构建调度处理器
- Object proxy=Proxy.newProxyInstance(null, new Class[]{Comparable.class}, handler);
- elements[i]=proxy;
- }
- Integer key=new Random().nextInt(1000)+1;
- int res=Arrays.binarySearch(elements, key);
- if(res>=0){
- System.out.println(elements[res].toString());
- }
- System.out.println("查找Key:"+key);
- }
- }
运行结果:
2.JDK源码实现原理分析
到这里我们发现,我们使用代理对象调用接口时候均调用TranceHander对象的invoker方法,使用Method的类的反射机制执行被代理对象的实现接口方法。
到这里我们得思考:
1. 这个代理对象是由谁且怎么生成的?
2. invoke方法是怎么调用的?
3. invoke和add方法有什么对应关系?
4. 生成的代理对象是什么样子的?
2. invoke方法是怎么调用的?
3. invoke和add方法有什么对应关系?
4. 生成的代理对象是什么样子的?
下面我引入了OPEN JDK源码
点击打开链接 ,查看了一下午,终于看明白了
。
(1)代理对象生成:
- Object proxy=Proxy.newProxyInstance(null, new Class[]{Comparable.class}, handler);
(2)查看Proxy类的newProxyInstance方法
从生成对象方法中,我们看到三个关键的地方:
- Class<?> cl = getProxyClass0(loader, interfaces);//得到代理类
- final Constructor<?> cons = cl.getConstructor(constructorParams);
- newInstance(cons, ih);//将InvocationHandler h传入代理对象中
- public static Object newProxyInstance(ClassLoader loader,
- Class<?>[] interfaces,
- InvocationHandler h)
- throws IllegalArgumentException
- {
- if (h == null) {
- throw new NullPointerException();
- }
- final SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
- }
- /*
- * Look up or generate the designated proxy class.
- */
- Class<?> cl = getProxyClass0(loader, interfaces);
- /*
- * Invoke its constructor with the designated invocation handler.
- */
- try {
- final Constructor<?> cons = cl.getConstructor(constructorParams);
- final InvocationHandler ih = h;
- if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
- // create proxy instance with doPrivilege as the proxy class may
- // implement non-public interfaces that requires a special permission
- 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());
- }
- }
(3)生成Proxy类,查看 getProxyClass0方法,
这个方法有点长,抽取关键的地方:
1.先抽取被代理类实现的接口Interface,检测是否符合要求
- /* collect interface names to use as key for proxy class cache */
- String[] interfaceNames = new String[interfaces.length];
- // for detecting duplicates
- Set<Class<?>> interfaceSet = new HashSet<>();
- for (int i = 0; i < interfaces.length; i++) {
- /*
- * Verify that the class loader resolves the name of this
- * interface to the same Class object.
- */
- 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");
- }
- /*
- * Verify that the Class object actually represents an
- * interface.
- */
- if (!interfaceClass.isInterface()) {
- throw new IllegalArgumentException(
- interfaceClass.getName() + " is not an interface");
- }
- /*
- * Verify that this interface is not a duplicate.
- */
- if (interfaceSet.contains(interfaceClass)) {
- throw new IllegalArgumentException(
- "repeated interface: " + interfaceClass.getName());
- }
- interfaceSet.add(interfaceClass);
- interfaceNames[i] = interfaceName;
- }
2.查看代理对象缓存中是否有我们要创建的代理类,如果有,直接获取;没有则创建
没有则创建,先定义代理类的包名,如果所有的接口都是公共的,则默认将创建的类放在String PROXY_PACKAGE = "com.sun.proxy";中,若果存在
非public接口则所有的非public接口必须在同一包下,代理类也放在此包下,否则系统抛异常。
- <span style="white-space:pre"> </span> 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");
- }
- }
- }
- if (proxyPkg == null) {
- // if no non-public proxy interfaces, use com.sun.proxy package
- proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
- }
接下来创建类,和将字节码写入类文件中:
- byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);
- proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
- return proxyClass;
(4)创建代理实例,将
接着看class文件的反编译源码吧,看大家都明白了吧,直接用的Method的反射机制实现的代理,equal、hascode、toString方法是所有类都有的,compareTo则是被代理对象的实现接口:
- newInstance(cons, ih);//将InvocationHandler h传入代理对象中
看完源码,我还是有点困惑,直到我看到生成的代理类文件源码后,豁然开朗。其实主要一步就在于生成类
的过程,类文件中建立被代理对象接口的invoke调用,话不多说看源码(我直接在测试用例调用的ProxyGenerator.generateProxyClass( proxyName, interfaces)接口生成的)
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- Integer value=1;
- InvocationHandler handler=new TranceHander(value);//构建调度处理器
- Object proxy=Proxy.newProxyInstance(null, Integer.class.getInterfaces(), handler);
- String path = "D://aaa.class";
- byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",Integer.class.getInterfaces());
- FileOutputStream out = null;
- try {
- out = new FileOutputStream(path);
- out.write(classFile);
- out.flush();
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- try {
- out.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
接着看class文件的反编译源码吧,看大家都明白了吧,直接用的Method的反射机制实现的代理,equal、hascode、toString方法是所有类都有的,compareTo则是被代理对象的实现接口:
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.lang.reflect.UndeclaredThrowableException;
- public final class $Proxy0 extends Proxy
- implements Comparable
- {
- private static Method m3;
- private static Method m1;
- private static Method m0;
- private static Method m2;
- public $Proxy0(InvocationHandler paramInvocationHandler)
- throws
- {
- super(paramInvocationHandler);
- }
- public final int compareTo(Object paramObject)
- throws
- {
- try
- {
- return ((Integer)this.h.invoke(this, m3, new Object[] { paramObject })).intValue();
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
- public final boolean equals(Object paramObject)
- throws
- {
- try
- {
- return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
- public final int hashCode()
- throws
- {
- try
- {
- return ((Integer)this.h.invoke(this, m0, null)).intValue();
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
- public final String toString()
- throws
- {
- try
- {
- return (String)this.h.invoke(this, m2, null);
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
- static
- {
- try
- {
- m3 = Class.forName("java.lang.Comparable").getMethod("compareTo", new Class[] { Class.forName("java.lang.Object") });
- m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
- m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
- m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
- return;
- }
- catch (NoSuchMethodException localNoSuchMethodException)
- {
- throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
- }
- catch (ClassNotFoundException localClassNotFoundException)
- {
- throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
- }
- }
- }