JAVA 动态代理(proxy)的实现和源码分析

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. 生成的代理对象是什么样子的?
下面我引入了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.查看代理对象缓存中是否有我们要创建的代理类,如果有,直接获取;没有则创建

如果有则  return  proxyClassCache.get(loader, interfaces); 
   没有则创建,先定义代理类的包名,如果所有的接口都是公共的,则默认将创建的类放在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)创建代理实例,将
  • 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());
    }
  }
}



  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值