论坛首页 Java企业应用论坛

JDK的动态代理是如何实现的?

浏览 13700 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-04-14  
用Spring大概有3个月,一直都是张口AOP,闭口AOP,但是Spring的AOP具体是怎么实现的,现在也不是十分清楚.

只知道可以用JDK的动态代理,基于接口,或者是cglib方式字节码增强.目前对JDK的动态代理方式不是很理解,固拿出来探讨下,我知道这应该是个新手贴,但还是请大家给点意见.如果真正理解了动态代理,那么对基于Spring AOP出现的各种现象就比较容易理解了.

先贴上一段代码.

package com.jamesby.proxy;
public interface CarCompany {

	public void makeCar(); 
	public void abc();
}

package com.jamesby.proxy;

public class CarCompanyImpl implements CarCompany {
	public void makeCar() {
		System.out.println("Company A make a car!");
		abc();
	}
	public void abc()
	{
		System.out.println("abc is invoked!");
	}
}

package com.jamesby.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class CarCompanyHandler implements InvocationHandler  {
	CarCompany com;
	public CarCompanyHandler(CarCompany com) {
		this.com = com;
	}
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
			System.out.println("time1:" + System.currentTimeMillis());
			method.invoke(this.com, new Class[] {});
			System.out.println("time2:" + System.currentTimeMillis());
		return null;
	}
}

package com.jamesby.proxy;

import java.lang.reflect.Proxy;

public class Test {
	public static void main(String[] arg) {
		CarCompanyImpl a = new CarCompanyImpl();
		CarCompanyHandler handler = new CarCompanyHandler(a);
		// 产生一个新的代理类
		CarCompany com = (CarCompany) Proxy.newProxyInstance(Test.class
				.getClassLoader(), new Class[] { CarCompany.class }, handler);
		com.makeCar();
	}
}



从以上代码可以看出在调用com.makeCar()的时候动态代理会生效,而在makeCar()方法中调用abc()的时候并不会进行动态代理.

猜测到底JDK是如何实现动态代理的?
1.基于JDK的反射机制,通过JDK调用handler的method.invoke(this.com, new Class[] {});实现对最终方法的调用,前后各有代码也有点Decorator的味道!

2.也是我比较模糊的一点
		CarCompany com = (CarCompany) Proxy.newProxyInstance(Test.class
				.getClassLoader(), new Class[] { CarCompany.class }, handler);
		com.makeCar();


也就是动态代理JDK到底是如何实现的?
JDK的动态代理并没有生成具体的类,她只是在运行中起动态调用目标类的作用,那么代码
CarCompany com = (CarCompany) Proxy.newProxyInstance(Test.class
				.getClassLoader(), new Class[] { CarCompany.class }, handler);

究竟做了什么,是将CarCompany注册到JDK的某个地方,当调用com.makeCar()的时候JDK到注册的地方去寻找,发现有注册就进行动态代理?



   发表时间:2007-04-14  
先贴段JDK的代码,大家帮助分析下:
package java.lang.reflect;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import sun.misc.ProxyGenerator;

public class Proxy implements java.io.Serializable {

    private static final long serialVersionUID = -2222568056686623797L;
    private final static String proxyClassNamePrefix = "$Proxy";
    private final static Class[] constructorParams ={ InvocationHandler.class };
    private static Map loaderToCache = new WeakHashMap();
    private static Object pendingGenerationMarker = new Object();
    private static long nextUniqueNumber = 0;
    private static Object nextUniqueNumberLock = new Object();
    private static Map proxyClasses =Collections.synchronizedMap(new WeakHashMap());
    protected InvocationHandler h;

    private Proxy() {
    }

    protected Proxy(InvocationHandler h) {
	this.h = h;
    }

    //关键是这个方法,正在看.
    public static Class<?> getProxyClass(ClassLoader loader, 
                                         Class<?>... interfaces)
	throws IllegalArgumentException
    {
	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
	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");
	    }
	    if (interfaceSet.contains(interfaceClass)) {
		throw new IllegalArgumentException(
		    "repeated interface: " + interfaceClass.getName());
	    }
	    interfaceSet.add(interfaceClass);
	    interfaceNames[i] = interfaceName;
	}
	Object key = Arrays.asList(interfaceNames);
	Map cache;
	synchronized (loaderToCache) {
	    cache = (Map) loaderToCache.get(loader);
	    if (cache == null) {
		cache = new HashMap();
		loaderToCache.put(loader, cache);
	    }
	}
	synchronized (cache) {
	    do {
		Object value = cache.get(key);
		if (value instanceof Reference) {
		    proxyClass = (Class) ((Reference) value).get();
		}
		if (proxyClass != null) {
		    return proxyClass;
		} else if (value == pendingGenerationMarker) {
		    try {
			cache.wait();
		    } catch (InterruptedException e) {
		    }
		    continue;
		} else {
		    cache.put(key, pendingGenerationMarker);
		    break;
		}
	    } while (true);
	}
	try {
	    String proxyPkg = null;	// package to define proxy class in
	    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
	    }
	    {
		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());
		}
	    }
	    proxyClasses.put(proxyClass, null);
	} finally {
	    synchronized (cache) {
		if (proxyClass != null) {
		    cache.put(key, new WeakReference(proxyClass));
		} else {
		    cache.remove(key);
		}
		cache.notifyAll();
	    }
	}
	return proxyClass;
    }

    //这个就是我的例子中调用的方法.
    public static Object newProxyInstance(ClassLoader loader,
					  Class<?>[] interfaces,
					  InvocationHandler h)
	throws IllegalArgumentException
    {
	if (h == null) {
	    throw new NullPointerException();
	}
	Class cl = getProxyClass(loader, interfaces);
	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());
	}
    }


    public static boolean isProxyClass(Class<?> cl) {
	if (cl == null) {
	    throw new NullPointerException();
	}

	return proxyClasses.containsKey(cl);
    }


    public static InvocationHandler getInvocationHandler(Object proxy)
	throws IllegalArgumentException
    {

	if (!isProxyClass(proxy.getClass())) {
	    throw new IllegalArgumentException("not a proxy instance");
	}
	Proxy p = (Proxy) proxy;
	return p.h;
    }


    private static native Class defineClass0(ClassLoader loader, String name,
					     byte[] b, int off, int len);
}
0 请登录后投票
   发表时间:2007-04-14  
Class cl = getProxyClass(loader, interfaces);
生成实现接口的类,并自动设置一个ContractorMethodName(InvocationHandler h)的构造函数?

如果这样就还有一个问题也是最关键的问题搞不懂.

就是InvocationHandler 的invoke是如何被调用的?是对代理接口的每个声明的方法生成类似如下代码?
makeCar()
{
   handler.invoke(......);
}
0 请登录后投票
   发表时间:2007-04-14  
正解!

http://www.blogjava.net/AndersLin/archive/2006/06/11/51997.html
0 请登录后投票
   发表时间:2007-04-14  
yimlin 写道
正解!

http://www.blogjava.net/AndersLin/archive/2006/06/11/51997.html


多谢楼上,看来我的猜测是对的!

这样来说,对于Spring AOP来说,比如事务拦截的时候,只拦截某些方法,实际上Spring 应该是在类似如下代码做文章!

public class CarCompanyHandler implements InvocationHandler  {   
    CarCompany com;   
    public CarCompanyHandler(CarCompany com) {   
        this.com = com;   
    }   
    public Object invoke(Object proxy, Method method, Object[] args)   
            throws Throwable {   
            //此处做文章,如果是需要事务拦截的,则此处添加事务代,否则什么也不做.
            method.invoke(this.com, new Class[] {});   
        return null;   
    }   
} 

如果以上说法成立,那么实际上对于已经被动态代理的接口来讲,无论方法是否匹配,都是通过java的反射机制来对目标类实现调用的.

也就是说一个interface有10个方法声明,但是我只对其中一个方法配置事务属性,结果是对该接口的所有方法调用均是通过发射机制完成的,当然对其中配置事务属性的方法进行事务拦截.

PS:楼上给的blog地址是你自己的blog吗?
0 请登录后投票
   发表时间:2007-04-14  
这样就可以很好的理解为什么service调用service本身的方法AOP起不到拦截作用了.

因为Spring 将Service 注入到Controller 或者Action的时候注入的是动态代理类,而service调用service自己的方法这时候的对象句柄this实际上是自己实现ServiceImpl类.因此AOP拦截不到.
0 请登录后投票
   发表时间:2007-04-14  
还有一个问题,那就是如果为一个ServiceImpl配置多个拦截器的时候到底生成多少个动态代理类的问题!

一个动态代理类?
如果一个那多个拦截器如何按照次序拦截?

0 请登录后投票
   发表时间:2007-04-14  
应该只有一个动态代理,多个拦截器是由spring自己进行维护的,它有专门处理来根据通知类型和Ordered排序值调用不同的拦截器。
0 请登录后投票
   发表时间:2007-04-14  
janh 写道
应该只有一个动态代理,多个拦截器是由spring自己进行维护的,它有专门处理来根据通知类型和Ordered排序值调用不同的拦截器。
我也是这样一来理解的,在看Spring AOP的代码!
0 请登录后投票
   发表时间:2007-04-15  
看看jdk动态代理反编译后的<? extends Proxy>
如果有兴趣的话再看看
cglib(asm), javasist
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics