`
jamesby
  • 浏览: 383440 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

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

阅读更多
用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到注册的地方去寻找,发现有注册就进行动态代理?



分享到:
评论
10 楼 shaucle 2007-04-15  
yimlin 写道
正解!

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

哦,这个说了。。。
9 楼 shaucle 2007-04-15  
看看jdk动态代理反编译后的<? extends Proxy>
如果有兴趣的话再看看
cglib(asm), javasist
8 楼 jamesby 2007-04-14  
janh 写道
应该只有一个动态代理,多个拦截器是由spring自己进行维护的,它有专门处理来根据通知类型和Ordered排序值调用不同的拦截器。
我也是这样一来理解的,在看Spring AOP的代码!
7 楼 janh 2007-04-14  
应该只有一个动态代理,多个拦截器是由spring自己进行维护的,它有专门处理来根据通知类型和Ordered排序值调用不同的拦截器。
6 楼 jamesby 2007-04-14  
还有一个问题,那就是如果为一个ServiceImpl配置多个拦截器的时候到底生成多少个动态代理类的问题!

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

5 楼 jamesby 2007-04-14  
这样就可以很好的理解为什么service调用service本身的方法AOP起不到拦截作用了.

因为Spring 将Service 注入到Controller 或者Action的时候注入的是动态代理类,而service调用service自己的方法这时候的对象句柄this实际上是自己实现ServiceImpl类.因此AOP拦截不到.
4 楼 jamesby 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吗?
3 楼 yimlin 2007-04-14  
正解!

http://www.blogjava.net/AndersLin/archive/2006/06/11/51997.html
2 楼 jamesby 2007-04-14  
Class cl = getProxyClass(loader, interfaces);
生成实现接口的类,并自动设置一个ContractorMethodName(InvocationHandler h)的构造函数?

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

就是InvocationHandler 的invoke是如何被调用的?是对代理接口的每个声明的方法生成类似如下代码?
makeCar()
{
   handler.invoke(......);
}
1 楼 jamesby 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);
}

相关推荐

    浅谈JDK动态代理与CGLIB代理去区别

    JDK动态代理的局限性在于,它只能为实现了至少一个接口的类创建代理,因为代理对象必须继承自这些接口。这种方式适用于那些基于接口进行编程的设计,比如Spring AOP中的Advisor和Advice。 相反,CGLIB(Code ...

    JDK动态代理_JDK动态代理

    ### JDK动态代理详解 #### 一、引言 在软件工程中,代理模式是一种常见的设计模式,它通过为一个对象提供一个替代品或占位符来控制对这个对象的访问。这种模式通常用于添加额外的功能(例如日志记录、事务管理等)...

    关于jdk动态代理的源码剖析

    - **只能代理实现了接口的类**:这是JDK动态代理的一个限制,如果目标对象没有实现任何接口,则无法使用JDK动态代理。 - **性能开销**:虽然这种开销通常很小,但在高并发场景下可能会成为瓶颈。 通过上述分析,...

    Spring框架中JDK动态代理和cglib动态代理

    如果我们想使用 JDK 动态代理,必须提供一个接口,并且将其实现类交给 JDK 动态代理来生成代理类。 CGLIB 动态代理是基于类的代理, 它可以代理类和接口。CGLIB 动态代理使用 ASM 字节码工具来生成代理类。CGLIB ...

    模拟JDK动态代理内部实现

    在本文中,我们将深入探讨如何模拟JDK的动态代理内部实现。 首先,我们需要了解JDK动态代理的基础知识。Java中的动态代理通过`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口实现。`Proxy...

    jdk动态代理技术详解

    JDK 动态代理技术是 Java 语言自身对动态代理的支持,提供了一种灵活和高效的方式来实现动态代理。通过使用 InvocationHandler 接口和 Proxy 类,可以轻松地实现动态代理,提高软件的灵活性和扩展性。

    JDK动态代理源码

    在Java编程领域,JDK动态代理是一个非常重要的概念,它允许我们在运行时动态地创建一个实现了特定接口的代理对象,以此来拦截并扩展原有对象的行为。动态代理在很多场景下都有应用,比如AOP(面向切面编程)、事件...

    JDK动态代理简单示例

    除了基本的代理实现,JDK动态代理还可以结合其他设计模式,如工厂模式,创建更加复杂的代理对象。此外,Spring框架中的AOP功能也是基于JDK动态代理或CGLIB实现的,它允许开发者定义切面,对满足特定条件的方法进行...

    JDK动态代理 spring aop 的原理

    在Java编程领域,JDK动态代理是实现动态创建代理对象的一种技术,它是Java标准库提供的一种强大工具。Spring AOP(面向切面编程)则是一种流行的应用框架,它利用动态代理来实现对业务代码的切面增强,如日志、事务...

    AOP之JDK动态代理和CGLib动态代理

    Spring框架是AOP实现的一个典范,它提供了两种主要的动态代理方式:JDK动态代理和CGLib动态代理。 **JDK动态代理**: JDK动态代理基于Java的反射API实现,适用于接口代理。当目标对象实现了至少一个接口时,Spring...

    java-jdk动态代理的实现demo

    Java JDK 动态代理是一种强大的特性,它允许我们在运行时创建代理类,这些代理类可以扩展或修饰已存在的接口实现。动态代理在很多场景下非常有用,比如日志记录、性能监控、事务管理等,这些功能可以在不修改原始...

    jdk动态代理和CGlib动态代理

    JDK动态代理和CGlib动态代理是Java中实现这一目标的两种主要方式。 ### JDK动态代理 JDK动态代理基于Java的接口实现。如果一个类实现了至少一个接口,我们就可以为这个类创建一个动态代理。动态代理通过`java.lang....

    jdk动态代理 + 拦截器实现小例

    在这个“jdk动态代理 + 拦截器实现小例”中,我们将探讨如何利用Java的InvocationHandler接口和Proxy类来实现拦截器模式,以实现灵活的代码扩展和日志记录、性能监控等需求。 首先,让我们理解什么是动态代理。在...

    java代理机制 JDK动态代理和cglib代理 详解

    本文将深入探讨两种主要的Java代理实现:JDK动态代理和CGLIB代理。 一、JDK动态代理 JDK动态代理基于接口实现,它要求被代理的类必须实现至少一个接口。在运行时,Java会动态地创建一个新的类,这个类实现了与原始...

    代理模式,JDK动态代理,SpringAOP来龙去脉

    在Java中,我们可以使用JDK的动态代理或者Spring AOP来实现代理模式。 JDK动态代理主要依赖于`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口。Proxy类是生成代理对象的工厂,而...

    JDK动态代理和CGLIB代理

    这种代理方式适用于目标对象实现了特定接口的情况,因为JDK动态代理只能代理实现了接口的类。 以下是一个简单的JDK动态代理示例: ```java interface MyService { void doSomething(); } class MyServiceImpl ...

    spring jdk动态代理

    Spring AOP允许我们通过代理来实现横切关注点,如日志、事务管理等,而JDK动态代理则是Spring AOP实现的一种方式。本文将深入探讨Spring如何利用JDK动态代理技术来实现这一功能,并通过实例解析其底层实现。 首先,...

    Jdk动态代理和cglib动态代理原理

    - **JDK代理**适用于目标类实现有接口的情况,更符合面向接口的设计原则,且无需引入额外的库。 - **CGLIB代理**适用于目标类没有接口或者不希望修改原有接口的情况,其性能通常优于JDK代理,因为它是基于字节码生成...

    java jdk 动态代理 演示demo

    Java JDK 动态代理是一种强大的特性,它允许我们在运行时创建代理对象,这些代理对象可以扩展或增强已存在的接口实现。动态代理在处理AOP(面向切面编程)场景、事件监听、性能监控等方面有着广泛的应用。下面我们将...

Global site tag (gtag.js) - Google Analytics