AOP---jdk动态代理的思考
引出问题:动态代理中是谁调用了invoke方法
为了更好的说明情况,我先写一个动态代理类
a.Person类
public interface Person {
public void eating();
}
b.PersonImpl类
public class PersonImpl implements Person{
public void eating() {
System.out.println("我能吃!");
}
}
c.PersonProxy动态代理类
public class PersonProxy implements InvocationHandler{
//传入的对象
private Object target;
//真实对象与代理对象绑定
public Object bind(Object target){
this.target=target;
//this指的是personProxy的一个实例
Object proxy =Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);return proxy;
}
//invocationHandler的默认方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("调用代理前");
Object object =method.invoke(target, args);
System.out.println("调用代理后");
return object;
}
}
d.test类
public class test {
public static void main(String[] args) {
PersonProxy proxy=new PersonProxy();
Person person=(Person)proxy.bind(new PersonImpl());
person.eating();
}
}
e.运行结果
调用代理前
我能吃!
调用代理后
f.从以上代码和结果可以看出,我们并没有显示的调用invoke()方法,但是这个方法确实执行了。下面就整个的过程进行分析一下,
g.我们先想一下,为什么我们在test类中只调用了代理类的bind函数,返回的代理对象就可以调用我们在实现Person类中的eating()方法?并且不是直接调用eating()方法,还在eating()的方法上添加了一些功能(输出调用代理前,调用代理后),这不就是代理的概念吗?不由的让我想起一个介绍代理的例子,火车站相当于一个实体,如果你去火车站买票,那就没啥事,如果你要去火车售票代理点买票,这个时候你就得掏点手续费,火车代理点可以给你提供一些额外的功能。扯的哪去了,说正事,从运行结果上可以看出来,当我们调用person.eating()的时候实际上调用了代理类PersonProxy中的invoke方法,但是是谁调用的呢?可以bind()函数中的newProxyInstance这个方法作为突破口,我们先来看一下Proxy类中newProxyInstance方法的源代码:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
} /*
* Look up or generate the designated proxy class.
*/
Class cl = getProxyClass(loader, interfaces); /*
* Invoke its constructor with the designated invocation handler.
*/
try {
/*
* private final static Class[] constructorParams = { InvocationHandler.class };
* cons即是形参为InvocationHandler类型的构造方法
*/
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());
}
}
从源码中我们可以看到:根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0.$Proxy0类 实现了interfaces的接口,并继承了Proxy类。
h.既然newProxyInstance()返回的对象能调用eating()方法,那我们看看代理实例proxy的方法有那些?
public class PersonProxy implements InvocationHandler{
//传入的对象
private Object target;
//真实对象与代理对象绑定
public Object bind(Object target){
this.target=target;
//this指的是personProxy的一个实例
Object proxy =Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
//查看proxy中的方法
for (Method name : proxy.getClass().getMethods()) {
System.out.println(name);
}
return proxy;
}
//invocationHandler的默认方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("调用代理前");
Object object =method.invoke(target, args);
System.out.println("调用代理后");
return object;
}
}
运行结果:
public final int $Proxy0.hashCode()
public final boolean $Proxy0.equals(java.lang.Object)
public final java.lang.String $Proxy0.toString()
public final void $Proxy0.eating()
public static boolean java.lang.reflect.Proxy.isProxyClass(java.lang.Class)
public static java.lang.Object java.lang.reflect.Proxy.newProxyInstance(java.lang.ClassLoader,java.lang.Class[],java.lang.reflect.InvocationHandler) throws java.lang.IllegalArgumentException
public static java.lang.reflect.InvocationHandler java.lang.reflect.Proxy.getInvocationHandler(java.lang.Object) throws java.lang.IllegalArgumentException
public static java.lang.Class java.lang.reflect.Proxy.getProxyClass(java.lang.ClassLoader,java.lang.Class[]) throws java.lang.IllegalArgumentException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
调用代理前
我能吃!
调用代理后
我们看到了方法中有eating()方法,我们试试看
Object proxy =Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
((Person) proxy).eating();
运行结果:
调用代理前
我能吃!
调用代理后
public final int $Proxy0.hashCode()
public final boolean $Proxy0.equals(java.lang.Object)
public final java.lang.String $Proxy0.toString()
public final void $Proxy0.eating()
public static boolean java.lang.reflect.Proxy.isProxyClass(java.lang.Class)
public static java.lang.Object java.lang.reflect.Proxy.newProxyInstance(java.lang.ClassLoader,java.lang.Class[],java.lang.reflect.InvocationHandler) throws java.lang.IllegalArgumentException
public static java.lang.reflect.InvocationHandler java.lang.reflect.Proxy.getInvocationHandler(java.lang.Object) throws java.lang.IllegalArgumentException
public static java.lang.Class java.lang.reflect.Proxy.getProxyClass(java.lang.ClassLoader,java.lang.Class[]) throws java.lang.IllegalArgumentException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
调用代理前
我能吃!
调用代理后
这就说明了Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this)这句相当于在代理对象中重写了实例中的eating()方法,并且在其中调用了invoke()方法。
i.证明是谁调用了invoke方法
既然可以看到代理对象proxy的所有方法,我们试试代理对象的其他代理实例的方法,从h中可以看到代理方法有:hashcode(),equals(),toString(),eating()
Object proxy =Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
//哈希值
System.out.println(proxy.hashCode());
运行结果:
调用代理前
调用代理后
7486844
调用代理前
我能吃!
调用代理后
Object proxy =Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
//toString
System.out.println(proxy.toString());
运行结果:
调用代理前
调用代理后
com.huhu.aop.PersonImpl@723d7c
调用代理前
我能吃!
调用代理后
j.反编译.class文件
public final class $Proxy0 extends Proxy implements Person {
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("***.RealSubject").getMethod("request",
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());
}
} //static
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);
}
}
public final void eating() {
try {
super.h.invoke(this, m3, null);
return;
} catch (Error e) {
} 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);
}
}
}
反编译的结果证明了我们h中得到的结论,顺便看到了为什么jdk动态代理只能代理接口。
AOP---jdk动态代理的思考的更多相关文章
- AOP jdk动态代理
一: jdk动态代理是Spring AOP默认的代理方法.要求 被代理类要实现接口,只有接口里的方法才能被代理,主要步骤是先创建接口,接口里创建要被代理的方法,然后定义一个实现类实现该接口,接着将被代 ...
- Spring AOP --JDK动态代理方式
我们知道Spring是通过JDK或者CGLib实现动态代理的,今天我们讨论一下JDK实现动态代理的原理. 一.简述 Spring在解析Bean的定义之后会将Bean的定义生成一个BeanDefinit ...
- Spring AOP JDK动态代理与CGLib动态代理区别
静态代理与动态代理 静态代理 代理模式 (1)代理模式是常用设计模式的一种,我们在软件设计时常用的代理一般是指静态代理,也就是在代码中显式指定的代理. (2)静态代理由 业务实现类.业务代理类 两部分 ...
- AOP学习心得&jdk动态代理与cglib比较
什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善.OOP引入 ...
- Spring AOP详解 、 JDK动态代理、CGLib动态代理
AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录.AOP将这些分散在各个业务逻辑中的代码 ...
- jdk动态代理与cglib代理、spring aop代理实现原理
原创声明:本博客来源与本人另一博客[http://blog.csdn.net/liaohaojian/article/details/63683317]原创作品,绝非他处摘取 代理(proxy)的定义 ...
- jdk动态代理与cglib代理、spring aop代理实现原理解析
原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...
- 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析
原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...
- Spring AOP高级——源码实现(3)AopProxy代理对象之JDK动态代理的创建过程
spring-aop-4.3.7.RELEASE 在<Spring AOP高级——源码实现(1)动态代理技术>中介绍了两种动态代理技术,当然在Spring AOP中代理对象的生成也是运用 ...
- 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
Spring AOP详解 . JDK动态代理.CGLib动态代理 原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...
随机推荐
- Net Core中数据库事务隔离详解——以Dapper和Mysql为例
Net Core中数据库事务隔离详解--以Dapper和Mysql为例 事务隔离级别 准备工作 Read uncommitted 读未提交 Read committed 读取提交内容 Repeatab ...
- CROSS JOIN,NATURAL JOIN
CROSS JOIN:笛卡尔积 NATURAL JOIN:
- scrollTop的兼容性
各浏览器下 scrollTop的差异 IE6/7/8: 对于没有doctype声明的页面里可以使用 document.body.scrollTop 来获取 scrollTop高度 : 对于有docty ...
- springboot学习(一)——helloworld
以下内容,如有问题,烦请指出,谢谢 springboot出来也很久了,以前零散地学习了不少,不过很长时间了都没有在实际中使用过了,忘了不少,因此要最近准备抽时间系统的学习积累下springboot,给 ...
- [置顶]
MVC输出缓存(OutputCache参数详解)
1.学习之前你应该知道这些 几乎每个项目都会用到缓存,这是必然的.以前在学校时做的网站基本上的一个标准就是1.搞定增删改查2.页面做的不要太差3.能运行(ps真的有这种情况,答辩验收的时候几个人在讲台 ...
- Linux根目录详解-转自鸟哥的私房菜
转自:http://myhat.blog.51cto.com/391263/107931/ *根目录(/)的意义与内容: 根目录是整个系统最重要的一个目录,因为不但所有的目录都是由根目录衍生出来的 ...
- HTTPS从认识到线上实战全记录
前言 关于HTTPS,基本上你想知道的都在这里了.本文原标题<HTTPS原理与实践>,下图是本文配套PPT的目录截图: [TOC] 原理篇 认识HTTPS 先说一下,本文可能有些地方由于描 ...
- 使用sed,grep 批量修改文件内容
使用sed命令可以进行字符串的批量替换操作,以节省大量的时间及人力: 使用的格式如下: sed -i "s/oldstring/newstring/g" `grep oldstri ...
- 前端之 CSS🤖
css (层叠样式表) : 对html标签的渲染和布局 查找 (选择器) 操作标签 (属性操作) 导入用 link 标签,后面的href就用你的文件路径就好 写css文件,要单独写到一个文件里面去 推 ...
- SQLServer 发布订阅(Replication)造成的Memroy压力(cmemthread等待)
深入了解下发布订阅: 数据复制:允许一个数据源向一个或多个目标数据库分发数据,只需要OLE DB 访问接口即可访问: 整个复制框架包含:复制组件,复制代理,复制类型: 复制组件: 发布服务器: ...