`
- 浏览:
53128 次
- 性别:
- 来自:
南京
-
学习AOP时遇到关于InvocationHandler接口的问题
动态代理是很多框架和技术的基础, spring 的AOP实现就是基于动态代理实现的。了解动态代理的机制对于理解AOP的底层实现是很有帮助的。
查看doc文档就可以知道,在java.lang.reflect包中有一个叫Proxy的类。下面是doc文档对Proxy类的说明:
"A dynamic proxy class (simply referred to as a proxy class below) is a class that implements a list of interfaces specified at runtime when the class is created, with behavior as described below. A proxy interface is such an interface that is implemented by a proxy class. A proxy instance is an instance of a proxy class. Each proxy instance has an associated invocation handler object, which implements the interface InvocationHandler."
Proxy类的设计用到代理模式的设计思想,Proxy类对象实现了代理目标的所有接口,并代替目标对象进行实际的操作。但这种替代不是一种简单的替代,这样没有任何意义,代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截。所以,Proxy应该包括一个方法拦截器,来指示当拦截到方法调用时作何种处理。InvocationHandler就是拦截器的接口。
InvocationHandler接口也是在java.lang.reflec
Object invoke(Object proxy, Method method, Object[] args)
这个接口有三个参数,其中第二和第三个参数都比较好理解,一个是被拦截的方法,一个是该方法的参数列表。关键是第一个参数。按照doc文档的解析,
proxy - the proxy instance that the method was invoked on
也就是说,proxy应该是一个代理实例,但为什么要传入这个参数呢?
带着这个问题,自己编了个小程序作了一点试验。
///////////////////////////////////////
public interface IAnimal {
void info();
}
////////////////////////////////////
public class Dog implements IAnimal
{
public void info() {
System.out.println("I am a dog!");
}
}
///////////////////////////////////////
import java.lang.reflect.*;
public class ProxyTest {
public static void main(String[] args) throws InterruptedException {
final IAnimal animal = new Dog();
Object proxyObj =Proxy.newProxyInstance(
animal.getClass().getClassLoader(),
animal.getClass().getInterfaces(),
new InvocationHandler()
{
public Object invoke(Object proxy, Method method, Object[] args)
{
try {
System.out.println("被拦截的方法:" + method.getName());
return method.invoke(animal, args);
}
catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
});
if(proxyObj instanceof IAnimal)
{
System.out.println("the proxyObj is an animal!");
}
else
{
System.out.println("the proxyObj isn't an animal!");
}
if(proxyObj instanceof Dog)
{
System.out.println("the proxyObj is a dog!");
}
else
{
System.out.println("the proxyObj isn't a dog!");
}
IAnimal animalProxy = (IAnimal)proxyObj;
animalProxy.info();
animalProxy.hashCode();
System.out.println(animalProxy.getClass().getName().toString());
}
}
程序执行的结果如下:
the proxyObj is an animal!
the proxyObj isn't a dog!
被拦截的方法:info
I am a dog!
被拦截的方法:hashCode
$Proxy0
从结果可以看出以下几点:
1. proxyObj 是一个实现了目标对象接口的对象,而不同于目标对象。也就是说,这种代理机制是面向接口,而不是面向类的。
2. info方法(在接口中)被成功拦截了,hashCode方法也成功被拦截了,但意外的是,getClass方法(继承自Object 类的方法)并没有被拦截!!
3. 应用调试还可以看出Invocation接口中invoke方法的传入的proxy参数确实就是代理对象实例proxyObj
为何getClass()没有被拦截?proxy参数又有何用呢?
先不管,做一个试验看看。既然这个proxy参数就是代理实例对象,它理所当然和proxyObj是一样的,可以调用info等方法。于是我们可以在invoke方法中加上如下一条语句:
((IAnimal)proxy).info();
结果是:
the proxyObj is an animal!
the proxyObj isn't a dog!
被拦截的方法:info
被拦截的方法:info
.......
被拦截的方法:info
被拦截的方法:info
然后就是栈溢出
结果是很明显的,在invoke方法中调用proxy中的方法会再一次引发invoke方法,这就陷入了死循环,最终结果当然是栈溢出的。
可以在invoke方法中调用proxy.getClass(), 程序可以正常运行。但如果调用hashCode()方法同样会导致栈溢出。
通过上面的试验,可以得出一些初步结论,invoke 接口中的proxy参数不能用于调用所实现接口的方法。奇怪的是hashCode()和getClass()方法都是从Object中继承下来的方法,为什么一个可以另一个不可以呢?带首疑问到doc文档看一下Object中这两个方法,发现getClass()是定义为final的,而hashCode()不是。难道是这个原因,于是找到一个非final方法,如equals试了一下,真的又会导致栈溢出;找另一个final方法如wait(),试了一下,invoke又不拦截了。final 难道就是关键之处?
还有一个问题就是proxy有什么用?既然proxy可以调用getClass()方法,我们就可以得到proxy的Class类象,从而可以获得关于proxy代理实例的所有类信息,如方法列表,Annotation等,这就为我们提供的一个分析proxy的有力工具,如通过分析Annotation分析方法的声明式事务需求。我想传入proxy参数应该是这样一个用意吧。
分享到:
Global site tag (gtag.js) - Google Analytics
相关推荐
AOP 动态代理(Proxy invocationHandler),Spring的动态代理的介绍
内容概要:springboot+拦截器+aop+自定义注解+本地线程实现统一接口日志记录,记录下接口所在模块、接口描述、接口请求参数、接口返回参数、接口请求时间以及接口耗时用于接口优化,接口记录参数以及操作人防止使用...
基于Spring boot + maven,以注解+AOP方式实现的java后端项目接口参数校验框架。迄今为止使用最简单、最容易理解的参数校验方案。博客地址:https://blog.csdn.net/weixin_42686388/article/details/104009771
aop学习
Spring_AOP_学习小结 Spring_AOP_学习小结 Spring_AOP_学习小结 Spring_AOP_学习小结
spring aop spring aop spring aop spring aop spring aop spring aop spring aop spring aop spring aop
使用动态代理实现AOP需要有四个角色:被代理的类,被代理类的接口,织入器,和InvocationHandler,而织入器使用接口反射机制生成一个代理类,然后在这个代理类中织入代码。被代理的类是AOP里所说的目标,...
如今互联网项目都采用HTTP接口形式进行开发。无论是Web调用还是智能设备APP调用,只要约定好参数...今天我们就来聊一聊JSR 303和AOP的结合。详细文章请参阅:http://blog.csdn.net/chaijunkun/article/details/44854071
Spring IOC AOP学习示例代码,包含Spring常用操作示例和所有所需jar文件。参考博客:http://blog.csdn.net/daijin888888/article/details/51735291
供学习AOP的人参考,DEMO在VS 2008下调试通过。
简单AOP操作面板学习的介绍
NULL 博文链接:https://jinnianshilongnian.iteye.com/blog/1889545
1. 首先完成一个接口的创建(里面有真正的想要实现的方法): public interface UserDAO{ public void add (); //想要实现的方法 } 2. 写一个真实的主题类实现...3. 写一个动态的代理类实现InvocationHandler接口:
spring-aop-1.1.1.jar spring-aop-1.2.6.jar spring-aop-1.2.9.jar spring-aop-2.0.2.jar spring-aop-2.0.6.jar spring-aop-2.0.7.jar spring-aop-2.0.8.jar spring-aop-2.0.jar spring-aop-2.5.1.jar spring-aop-...
spring 配置aop时,必须的jar包:aopalliance-1.0.jar aspectjweaver.jar
Spring Boot 使用 AOP 和 Redis 实现接口限流是一种高效且实用的方法,用于控制对特定接口的访问频率。以下是实现这个功能的基本步骤: 引入依赖:首先,在 Spring Boot 项目中引入 Redis 和 AOP 的相关依赖。这...
NULL 博文链接:https://microjava.iteye.com/blog/525796
开发工具 aopalliance-1.0开发工具 aopalliance-1.0开发工具 aopalliance-1.0开发工具 aopalliance-1.0开发工具 aopalliance-1.0开发工具 aopalliance-1.0开发工具 aopalliance-1.0开发工具 aopalliance-1.0开发工具...
个人整理的关于AOP概念
AOP似乎一时之间成了潮流。Java初学者不禁要发出感慨,OOP还没有学通呢,又来AOP。本文不是要在理论上具体阐述何为AOP, 为何要进行AOP . 要详细了解学习AOP可以到它老家http://aosd.net去瞧瞧。这里只是意图通过一个...