JDK动态代理的一些认识

          通过学习静态代理,知道如果接口增加了方法,那么委托类和代理类都必须实现,同时在代理类中还要对新增方法做处理,比如日志记录等,很繁琐。而JDK的动态代理就应运而生了,代理类不需要程序员手动预先写好,而是有Proxy类来帮你生成(运行期生成)。先直接看例子。
package proxy;

public interface SingInterface {
	public void sing();
}


package proxy;

public interface DanceInterface {
	public void dance();
}


package proxy;

public class Person implements SingInterface, DanceInterface {

	@Override
	public void dance() {
		System.out.println("I'm dancing......");
	}

	@Override
	public void sing() {
		System.out.println("I'm singging......");
	}

}


package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 给目标类(被代理类,委托类)的方法执行前和执行后做些日志
 *
 */
public class LogInvocationHandler implements InvocationHandler{

	private Object targer;//目标类
	public LogInvocationHandler(Object target){
		this.targer=target;
	}
	/**
	 * 取得有Proxy产生的代理类
	 */
	public Object getProxy(){
		return Proxy.newProxyInstance(
				/*取得当前线程使用的加载器*/Thread.currentThread().getContextClassLoader(),
				/*目标类都实现了哪些接口,告诉Proxy,Proxy生成的代理类也将会同样实现*/this.targer.getClass().getInterfaces(),
				/*将自身传给Proxy,Proxy生成的代理类将会用它调用下面的invoke方法*/this);
	}
	@Override
	/**
	 * proxy就是Proxy类生成的代理类
	 * 初步学习JDK的动态代理,这里最容不明白,到底是谁调用了它
	 */
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println(method.getName()+"准备执行......");
		Object result=method.invoke(this.targer, args);//执行的是目标类的方法
		System.out.println(method.getName()+"执行结束......");
		return result;
	}

}



package proxy;
/**
 *测试类 
 *给Person的sing和dance方法执行前和执行后做些日志
 */
public class Client {

	public static void main(String[] args){
		Person person=new Person();
		//取得将对person进行加日志的代理类
		Object proxy=new LogInvocationHandler(person).getProxy();
		//对sing方法加日志
		((SingInterface)proxy).sing();
		//对dance方法加日志
		((DanceInterface)proxy).dance();//可知proxy一定同时实现了SingInterface和DanceInterface接口
		
	}
	
	
}

运行结果

sing准备执行......
I'm singging......
sing执行结束......
dance准备执行......
I'm dancing......
dance执行结束......

 

下面来看看Proxy.newInstance方法

public static Object newProxyInstance(ClassLoader loader,
			Class<?>[] interfaces, InvocationHandler h)
			throws IllegalArgumentException {
		if (h == null) {
			throw new NullPointerException();
		}

		/*
		 * Look up or generate the designated proxy class.
		 * 取得生成的代理类的Class类
		 */
		Class<?> cl = getProxyClass0(loader, interfaces); // stack walk magic:
															// do not refactor

		/*
		 * Invoke its constructor with the designated invocation handler.
		 */
		try {
			//取得代理类参数类型为InvocationHandler.class的构造方法
			final Constructor<?> cons = cl.getConstructor(constructorParams);
			final InvocationHandler ih = h;
			SecurityManager sm = System.getSecurityManager();
			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() {
								//把实现了InvocationHandler接口的对象给代理类
								//看上面的例子中LogInvocationHandler类中getProxy方法
								//里面获取代理类时,第三个参数是不是this(就是指logInvocationHandler)
								return newInstance(cons, ih);
							}
						});
			} else {
				return newInstance(cons, ih);
			}
		} catch (NoSuchMethodException e) {
			throw new InternalError(e.toString());
		}
	}

      看了上面的源码,得知重点转移到getProxyClass0(loader, interfaces); 这个方法里面了,进去看了 下,源码挺多的,重要的是后面那部分。

/*
	 * Generate the specified proxy class.
	 */
	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());
	}
    }
    // add to set of all generated proxy classes, for isProxyClass
    proxyClasses.put(proxyClass, null);

} finally {
    /*
     * We must clean up the "pending generation" state of the proxy
     * class cache entry somehow.  If a proxy class was successfully
     * generated, store it in the cache (with a weak reference);
     * otherwise, remove the reserved entry.  In all cases, notify
     * all waiters on reserved entries in this cache.
     */
    synchronized (cache) {
	if (proxyClass != null) {
	    cache.put(key, new WeakReference(proxyClass));
	} else {
	    cache.remove(key);
	}
	cache.notifyAll();
    }
}
return proxyClass;
}

     再继续跟踪ProxyGenerator.generateProxyClass(    proxyName, interfaces);


public static byte[] generateProxyClass(String paramString, Class[] paramArrayOfClass)
  {
    ProxyGenerator localProxyGenerator = new ProxyGenerator(paramString, paramArrayOfClass);
    byte[] arrayOfByte = localProxyGenerator.generateClassFile();
    if (saveGeneratedFiles)
      AccessController.doPrivileged(new PrivilegedAction(paramString, arrayOfByte)
      {
        public Object run()
        {
          try
          {
            FileOutputStream localFileOutputStream = new FileOutputStream(ProxyGenerator.access$000(this.val$name) + ".class");
            localFileOutputStream.write(this.val$classFile);
            localFileOutputStream.close();
            return null;
          }
          catch (IOException localIOException)
          {
          }
          throw new InternalError("I/O exception saving generated file: " + localIOException);
        }
      });
    return arrayOfByte;
  }


      到这就不再跟踪了,有兴趣的同学,可以自己去看源码吧。

      现在来看看Proxy给我们生成的代理类是怎样的。

package proxy;
import java.io.FileOutputStream;
import java.io.IOException;
import sun.misc.ProxyGenerator;
/**
 * 这个类来源http://rejoy.iteye.com/blog/1627405
 *
 */
public class ProxyGeneratorUtils {

	/**
	 * 把代理类的字节码写到硬盘上
	 * @param path 保存路径
	 */
	public static void writeProxyClassToHardDisk(String path) {
		
		// 获取代理类的字节码
		byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11", Person.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();
			}
		}
	}
	
	public static void main(String[] args){
		ProxyGeneratorUtils.writeProxyClassToHardDisk("E:/$Proxy11.class");  
	}
}



    用反编译工具来看看生成出来的$Proxy11.class

 

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.DanceInterface;
import proxy.SingInterface;

public final class $Proxy11 extends Proxy
  implements SingInterface, DanceInterface
{
  private static Method m1;
  private static Method m4;
  private static Method m3;
  private static Method m0;
  private static Method m2;

  public $Proxy11(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }
  /**
   *由于 任何类都是Object的子类,所以Person类也拥有Object的方法
   *所以这里也进行了处理
   */
  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final void dance()
    throws 
  {
    try
    {
     //看到LogInvocationHandler中的invoke()方法是在这被调用的	
      this.h.invoke(this, m4, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final void sing()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m4 = Class.forName("proxy.DanceInterface").getMethod("dance", new Class[0]);
      m3 = Class.forName("proxy.SingInterface").getMethod("sing", new Class[0]);
      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());
  }
}


    可以看到代理类实现了和目标类一样的接口,同时代理类调用了InvocationHandler中的invoke()方法,然后InvocationHandler中的invoke()方法调用目标类中的真正方法。

 


 



 
 
 
 
 
 
 
 
 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值