`
chaisencs
  • 浏览: 8343 次
  • 性别: Icon_minigender_1
  • 来自: 长春
社区版块
存档分类
最新评论

jdk动态代理中的问题——调用proxy的toString方法引起的栈溢出

    博客分类:
  • J2SE
阅读更多
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Test {

	public static void main(String[] args) {

		UserManager target = new UserManagerImpl();
		
		UserManager proxy = (UserManager) Proxy.newProxyInstance(target.getClass().getClassLoader(),
				target.getClass().getInterfaces(), new UserManagerProxy(target));
		proxy.addUser();
	}

}


interface UserManager {
	
	public void addUser();
}

class UserManagerImpl implements UserManager {

	@Override
	public void addUser() {
		System.out.println("add user...");
	}
	
} 

class UserManagerProxy implements InvocationHandler {

	private UserManager target;
	
	public UserManagerProxy(UserManager target) {
		this.target = target;
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		
		System.out.println("check privilege " + proxy);
		
		method.invoke(target, args);
		
		return null;
	}
	
}


在执行代理处理类的System.out.println("check privilege " + proxy);时候,出现了java.lang.StackOverflowError错误。原因可以初步定位在proxy的toString方法上。
但是调试后发现proxy属于$Proxy0类,而$Proxy0这个Class是运行时生成的类,网上有一个牛人贴出了$Proxy0的源码:

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 UserManager {
	private static Method m1;
	private static Method m0;
	private static Method m3;
	private static Method m2;

	static {
		try {
			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]);
			m3 = Class.forName("cn.edu.jlu.proxy.UserManager").getMethod("addUser",
					new Class[0]);
			m2 = Class.forName("java.lang.Object").getMethod("toString",
					new Class[0]);
		} catch (NoSuchMethodException nosuchmethodexception) {
			throw new NoSuchMethodError(nosuchmethodexception.getMessage());
		} catch (ClassNotFoundException classnotfoundexception) {
			throw new NoClassDefFoundError(classnotfoundexception.getMessage());
		}
	}

	public $Proxy0(InvocationHandler invocationhandler) {
		super(invocationhandler);
	}

	@Override
	public final boolean equals(Object obj) {
		try {
			return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))
					.booleanValue();
		} catch (Throwable throwable) {
			throw new UndeclaredThrowableException(throwable);
		}
	}

	@Override
	public final int hashCode() {
		try {
			return ((Integer) super.h.invoke(this, m0, null)).intValue();
		} catch (Throwable throwable) {
			throw new UndeclaredThrowableException(throwable);
		}
	}

	@Override
	public final String toString() {
		try {
			return (String) super.h.invoke(this, m2, null);
		} catch (Throwable throwable) {
			throw new UndeclaredThrowableException(throwable);
		}
	}

	@Override
	public void addUser() {
		try {
			super.h.invoke(this, m3, null);
			return;
		} catch (Error e) {
		} catch (Throwable throwable) {
			throw new UndeclaredThrowableException(throwable);
		}

	}
}

可以看到,调用toString方法的时候,调用了h的invoke方法,而h就是InvocationHandler的实例,所以是递归调用,所以就会出现上述所说的java.lang.StackOverflowError错误。

这里顺便说一下简单编写java动态代理的过程:

1.定义一个接口I
2.编写该接口I的实现类Impl
3.编写InvocationHandler接口的实现类H,构造H类对象的时候可以把要代理的对象target传入,target完成实际的动作。在里面的invoke方法里编写自己想实现的逻辑,然后再调用实际要完成的动作就可以。
4.调用Proxy.newProxyInstance方法,传递的三个参数分别是代理类的类加载器(可以用Impl实例的getClass().getClassLoader())
、代理类要实现的接口列表(可以用Impl实例getClass().getInterfaces())、InvocationHandler实现类的实例。

这样就生成了$Proxy0类的对象,由于$Proxy0类实现了I接口,所以可以将对象强制转型成I。


再说一下Proxy.newProxyInstance方法的实际过程:
1.使用传入的InvocationHandler实例参数将Proxy类的h实例初始化,注意,如果传入空对象的话,会抛出空指针错误,即h不能为空。
2.运行时生成代理Class,即$Proxy0
3.利用上面动态生成的$Proxy0类,构造出该类的对象,并返回。















分享到:
评论

相关推荐

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

    通过上述分析,我们可以看出JDK动态代理是一种非常强大且灵活的技术,它不仅可以帮助我们解决实际开发中遇到的问题,还能够提高代码的可维护性和可扩展性。理解其工作原理对于深入学习Java编程有着重要意义。

    JDK动态代理_JDK动态代理

    下面是一个简单的示例,演示了如何使用JDK动态代理为一个接口创建代理对象,并在方法调用前后添加日志记录功能。 ```java /** * 业务接口 */ public interface IHello { void hello(String name); } /** * ...

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

    然而,如果目标类的方法很多,CGLIB可能会更高效,因为它只需创建一次子类,而JDK动态代理每次调用都需要通过InvocationHandler。在内存占用上,两者差别不大,但CGLIB生成的代理类可能略微增加内存负担。 在工具...

    JDK动态代理proxy

    JDK动态代理,全称为Java Dynamic Proxy,是Java标准库提供的一种强大且灵活的机制,允许我们在运行时创建代理类来实现指定的接口。这种机制主要用于实现AOP(面向切面编程)或为已有接口提供额外的功能,如日志、...

    jdk动态代理技术详解

    Proxy 类是 JDK 中的动态代理类,是动态代理的核心类,其作用类似于代理模式中的代理。Proxy 类中包含的全是静态的方法和成员变量,这是一个纯粹的工具类,因此其源代码中含有一个私有的构造器,在某种意义上可以看...

    JDK动态代理源码

    3. **不同类,不同方法的代理**:在描述中提到的“不同类,不同方法的代理”意味着JDK动态代理可以为任何实现了相同接口的类创建代理,而不仅仅是单一类。这得益于`InvocationHandler`的灵活性,它可以处理任何接口...

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

    在 JDK 动态代理中,我们可以使用 InvocationHandler 来处理方法的调用。InvocationHandler 是一个接口,它定义了 invoke 方法,该方法将被代理对象的方法调用转发到 InvocationHandler 的 invoke 方法中。在 invoke...

    JDK动态代理简单示例

    JDK动态代理是Java编程中一个非常重要的特性,它允许我们在运行时创建具有特定接口的代理类实例。这种技术在很多场景下都非常有用,比如在AOP(面向切面编程)中实现方法拦截、日志记录、事务管理等。下面我们将深入...

    JDK动态代理在EJB3(包括WebService)中的应用

    标题 "JDK动态代理在EJB3(包括WebService)中的应用" 暗示了本文将探讨Java开发中的一种重要技术——JDK动态代理,以及它如何在企业级JavaBean (EJB) 3.x版本及其相关的Web服务实现中发挥作用。EJB3是Java EE平台的...

    JDK动态代理 spring aop 的原理

    总的来说,JDK动态代理是Spring AOP实现的基础,它允许我们在运行时动态创建代理对象,实现对方法调用的拦截和增强。Spring AOP则在此基础上提供了更高级的抽象,让我们可以方便地定义和管理切面,从而实现更灵活的...

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

    - `MethodInterceptor`接口类似于JDK动态代理中的`InvocationHandler`,但它的拦截逻辑更为灵活,可以在`intercept()`方法中控制目标方法的调用。 3. **FastClass和MethodProxy** - CGLIB通过`FastClass`和`...

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

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

    jdk动态代理和CGlib动态代理

    在Java编程中,动态代理是一种强大的技术,它允许我们在运行时创建对象的代理,以便在调用实际方法之前或之后执行额外的操作。JDK动态代理和CGlib动态代理是Java中实现这一目标的两种主要方式。 ### JDK动态代理 ...

    模拟JDK动态代理内部实现

    在Java编程中,动态代理是一种强大的技术,它允许我们在运行时创建对象的代理,这些代理对象可以在调用实际方法之前和之后执行额外的操作。这种机制主要用于实现AOP(面向切面编程)和事件监听等功能。在本文中,...

    spring jdk动态代理

    在Java中,动态代理是一种在运行时创建代理类的能力,它允许我们为一组接口创建代理对象,这些代理对象可以拦截并处理方法调用。核心接口是`java.lang.reflect.InvocationHandler`,它定义了处理方法调用的逻辑;另...

    java jdk 动态代理 演示demo

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

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

    - **CGLIB代理**适用于目标类没有接口或者不希望修改原有接口的情况,其性能通常优于JDK代理,因为它是基于字节码生成的子类,而JDK代理需要反射调用接口方法。 在实际开发中,如Spring AOP框架就同时支持JDK和...

    JDK的动态代理总结

    在Java开发中,JDK的动态代理是一种非常重要的技术,它允许我们在运行时创建具有特定接口的代理类实例,从而实现代理对象对目标对象的方法调用进行拦截和增强。这篇博客将对JDK的动态代理进行深入的总结,探讨其原理...

    JDK动态代理和CGLIB代理

    Java动态代理是Java编程中一个重要的特性,它允许我们在运行时创建对象的代理,从而实现对原对象的一些额外操作或扩展功能。JDK动态代理和CGLIB代理是两种常用的实现方式。 首先,我们来看看JDK动态代理。JDK动态...

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

    在Java编程中,JDK动态代理是一种非常实用的技术,它允许我们在运行时创建代理类来增强或拦截原有类的方法调用。在这个“jdk动态代理 + 拦截器实现小例”中,我们将探讨如何利用Java的InvocationHandler接口和Proxy...

Global site tag (gtag.js) - Google Analytics