动态代理

最近在看AOP源码,先熟悉下JDK动态代理、Cglib、ASM相关的一些东西。


一.静态代理的缺点:

     想想如果我们要用静态代理的方式去实现B中内的method(),我们应该如何做?

     肯定是先将方法抽象到一个接口,代理类和B都去实现此接口,然后在代理类中有B的对象,在掉代理类的method时候,内部方法实际掉的B的method~~~~

     那么问题来了,如果我有对很多个类都实现代理模式,那我岂不是都需要按照如上步骤进行操作?想想就麻烦。。

      因此动态代理便被提了出来。


二.动态代理中的接口和类

     在动态代理中有两个很重要的类/接口,分别是InvocationHandler接口和Proxy类。看下他们的定义吧.

     InvocationHandler:接口

InvocationHandler 是代理实例的调用处理程序 实现的接口。 

每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。

    在此接口中,只有一个方法:

Object invoke(Object proxy, Method method, Object[] args) 
          在代理实例上处理方法调用并返回结果。 

 此方法的参数定义:

proxy - 在其上调用方法的代理实例,指代我们所代理的那个真实对象
method - 对应于在代理实例上调用的接口方法的 Method 实例。指代的是我们所要调用真实对象的某个方法的Method对象
args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。指代的是调用真实对象某个方法时接受的参数。

    Proxy类:
    在这个类中,常用的是newProxyInstance()方法

  

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
                               throws IllegalArgumentException返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。此方法相当于: 
     Proxy.getProxyClass(loader, interfaces).
         getConstructor(new Class[] { InvocationHandler.class }).
         newInstance(new Object[] { handler });
 Proxy.newProxyInstance 抛出 IllegalArgumentException,原因与 Proxy.getProxyClass 相同。

方法参数:

loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序 

返回:

一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口 

三.动态代理的实现

public interface Buy {
	public void buy(String goods); 
	public void get(String goods);
}

public class BuyApple implements Buy{

	public void buy(String goods) {
		// TODO Auto-generated method stub
		System.out.println(" i want to buy "+goods);
	}

	public void eat(String goods) {
		// TODO Auto-generated method stub
		System.out.println(" i want to eat "+goods);
	}

	public void get(String goods) {
		// TODO Auto-generated method stub
		System.out.println(" i get "+goods);
	}

}

public class ProxyHandler implements InvocationHandler {
	
	//要代理的真实对象
	private Object proxyHandler;
	
	public ProxyHandler(Object proxy){
		this.proxyHandler=proxy;
	}
	/*
	 * 如果在InvocationHander中实现buy()方法,并且InvocationHander中调用,这就是一个静态代理。
	 * 如果接口中新增eat(),那么InvocationHander中要新增eat(),多麻烦
	 * 在这里好好仔细品味一下静态代理的缺点。
	 * 如果我们讲方法用一种统一的形式调用,就很方便了。这就是动态代理。
	 */

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		//
		System.out.println(" proxy " + proxy.getClass().getName());
		//要被调用的方法名称
		System.out.println(" method "+ method.getName());
		//要被调用的方法的参数
		System.out.println(" args "+ args[0].getClass().getName());
		// 方法名.invoke(具体的类,参数);
		Object obj = method.invoke(proxy, args);
		return obj;
	}

}


public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stb
		BuyApple buyApple = new BuyApple();
		InvocationHandler ih = new ProxyHandler(buyApple);
		/**
		 * 这里返回的是代理类的指定调用处理程序的代理实例.
		 * 分析下这一块的含义。
		 * 1.再次之前我们创建了一个具体的BuyApple实例,我们现在的目的是想通过一个代理类,来调用buyApple的方法
		 * 	  在根据此方法的名称,我们可以知道这个函数返回的是一个新的代理,用来替代buyApple调用方法。那么,代理类
		 *   如何获得?
		 * 2. newProxyInstance()方法返回:一个指定接口的代理类实例。该接口可以将方法调用指派到指定的调用处理程序。
		 *    按照此概念,这里返回的是Buy接口的代理类实例。<所以在下面的输出buy.getClass().getName()中显示的是$Proxy>
		 * 	     这个代理类有些什么特性呢?
		 *    第三个参数InvoataionHandler的意义就是:将此代理类关联到这个InvocationHandler上。在调用代理类的方法是,会将
		 *    此参数作为参数传递到invoke函数里面。
		 * 3. 为什么返回的代理类实例是Buy类型?
		 *    原因就是在newProxyInstance这个方法的第二个参数上,我们给这个代理对象提供了一组什么接口,那么我这个代理对象
		 *    就会实现了这组接口,这个时候我们当然可以将这个代理对象强制类型转化为这组接口中的任意一个,因为这里的接口
		 *    是Buy类型,所以就可以将其转化为Buy类型了。	
		 * 4.有人会问为什么要穿类的加载器(classloader),因为JVM规定,不同的加载器加载到jvm的对象不是同一个对象,这也是JVM双亲
		 *   委派模型产生的原因。
		 * */
		Buy buy = (Buy) Proxy.newProxyInstance(buyApple.getClass().getClassLoader(),
				buyApple.getClass().getInterfaces(),ih);
		//因此这里输出buy的getClass().getName()不是Buy!!!!
		System.out.println(" buy "+buy.getClass().getName());
		//和上面进行对比
		Buy buy1 = new BuyApple();
		System.out.println(" buy1 "+buy1.getClass().getName());
	
		buy.buy("banana");
		
	}

}



写完demo,自己仍有不少疑问:


1.为什么JDK的动态代理只能调用 接口中定义的函数?(而cglib却可以实现?)

2.打印出来的代理类的名字为什么是$Proxy1


四.动态代理的深度解析

先看下主要的源码:

Proxy.newProxyInstance

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

	/*
	 *创建代理类类
	 */
	Class cl = getProxyClass(loader, interfaces);

	/*
	 * Invoke its constructor with the designated invocation handler.
	 */
	try {
	    Constructor cons = cl.getConstructor(constructorParams);
	    return (Object) cons.newInstance(new Object[] { h });
	} catch (NoSuchMethodException e) {
	    throw new InternalError(e.toString());
	} catch (IllegalAccessException e) {
	    throw new InternalError(e.toString());
	} catch (InstantiationException e) {
	    throw new InternalError(e.toString());
	} catch (InvocationTargetException e) {
	    throw new InternalError(e.toString());
	}
    }

这个方法我们可以知道他的作用是:获取代理类,并且根据他的构造函数创建一个实例并且返回。看下如何创建代理类的:

getProxyClass

    public static Class<?> getProxyClass(ClassLoader loader, 
                                         Class<?>... interfaces)
	throws IllegalArgumentException
    {    //接口<span style="font-size:12px;">类数组的长度小于65535,如果大于就会内存溢出</span>
	if (interfaces.length > 65535) {
	    throw new IllegalArgumentException("interface limit exceeded");
	}

	Class proxyClass = null;

	String[] interfaceNames = new String[interfaces.length];

	Set interfaceSet = new HashSet();	// for detecting duplicates  为了防止重复,将所有产生的接口类存入一个set里面

	for (int i = 0; i < interfaces.length; i++) {
	 
	    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");
	    }

	
	    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;
	}

	
	Object key = Arrays.asList(interfaceNames);

	/*
	 * Find or create the proxy class cache for the class loader. 这里为类加载器找到或者创建代理类缓存
	 */
	Map cache;
	synchronized (loaderToCache) {
	    cache = (Map) loaderToCache.get(loader);
	    if (cache == null) {
		cache = new HashMap();
		loaderToCache.put(loader, cache);
	    }
	    /*
	     * This mapping will remain valid for the duration of this
	     * method, without further synchronization, because the mapping
	     * will only be removed if the class loader becomes unreachable.
	     */
	}

	
	
	synchronized (cache) {
	   
	    do {
		Object value = cache.get(key);
		if (value instanceof Reference) {
		    proxyClass = (Class) ((Reference) value).get();
		}
		if (proxyClass != null) {
		    // proxy class already generated: return it
		    return proxyClass;
		} else if (value == pendingGenerationMarker) {
		    // proxy class being generated: wait for it
		    try {
			cache.wait();
		    } catch (InterruptedException e) {
			/*
			 * The class generation that we are waiting for should
			 * take a small, bounded time, so we can safely ignore
			 * thread interrupts here.
			 */
		    }
		    continue;
		} else {
		    /*
		     * No proxy class for this list of interfaces has been
		     * generated or is being generated, so we will go and
		     * generate it now.  Mark it as pending generation.
		     */
		    cache.put(key, pendingGenerationMarker);
		    break;
		}
	    } while (true);
	}
      <span style="font-size:12px;">  //这句代码是给新生成的代理类截取接口,JDK是这样设计的:如果接口为public,则生成到顶包底下,如果为默认修饰符,也就是修饰符为空,则会生成
       //  到接口所定义的包下</span>

	try {
	    String proxyPkg = null;	// package to define proxy class in

	    /*
	     * Record the package of a non-public proxy interface so that the
	     * proxy class will be defined in the same package.  Verify that
	     * all non-public proxy interfaces are in the same package.
	     */
	    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,
		proxyPkg = "";		// use the unnamed package
	    }

	    {
		/*
		 *  代理类起名字$Proxy+数子
		 */
		long num;
		synchronized (nextUniqueNumberLock) {
		    num = nextUniqueNumber++;
		}
		String proxyName = proxyPkg + proxyClassNamePrefix + num;
		/*
		 * 检查类加载器是否已经用被选择的名字定义
		 */

		/*
		 *  这是根据字节码生成代理类
		 */
		byte[] proxyClassFile =	ProxyGenerator.generateProxyClass(
		    proxyName, interfaces);
		try {
		    proxyClass = defineClass0(loader, proxyName,
			proxyClassFile, 0, proxyClassFile.length);
		} catch (ClassFormatError e) {
		  
		    throw new IllegalArgumentException(e.toString());
		}
	    }
	    // add to set of all generated proxy classes, for isProxyClass
	    proxyClasses.put(proxyClass, null);

	} finally {
	    synchronized (cache) {
		if (proxyClass != null) {
		    cache.put(key, new WeakReference(proxyClass));
		} else {
		    cache.remove(key);
		}
		cache.notifyAll();
	    }
	}
	return proxyClass;
    }

在这里用了JNI方法,无法看到最底层的实现,不过查了下资料,产生的代理类的格式:

public class $Proxy1 extends Proxy implements 传入的接口{

}

尤其注意:这个类已经继承的一个类Proxy,i因此他无法再继承别的类。所以代理可以获得的函数只能是接口里面的函数!



五.动态代理的缺点

   1.只能处理实现的接口内的方法。

   2.效率低,开销大。


六.总结

一个典型的动态代理创建对象过程可分为以下四个步骤:
1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(...);
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});
3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))


参考blog:http://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值