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 ...
随机推荐
- Java filter中的chain
一.Filter Filter:用来拦截请求,处于客户端和被请求资源之间,是为了代码的复用性.Filter链,在web.xml中哪个先配置就先调用哪个 二.FilterChain(过滤链) 服务器会按 ...
- 自学Zabbix1.3-zabbix进程
默认情况下zabbix包含5个程序:zabbix_agentd.zabbix_get.zabbix_proxy.zabbix_sender.zabbix_server,另外一个zabbix_java_ ...
- 自学Python5.3-内置模块(1)
内置模块(1)内置模块是Python自带的功能,在使用内置模块相应的功能时,需要 先导入 再 使用 1.OS模块 用于提供系统级别的操作: os.getcwd() 获取当前工作目录,即 ...
- 自学WPF之Binding(一)
Binding的重要性就不作介绍了,是作为数据交互的支撑,下面来介绍一下为Binding指定源(Source)的几种方法: 把普通CLR类型的单个对象指定为Source:包括.NET Framewor ...
- Tomcat配置(二):tomcat配置文件server.xml详解和部署简介
*/ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...
- ERROR: Java 1.7 or later is required to run Apache Drill.
问题 Apache 的 drill 执行启动命令 drill-embedded 报错: ERROR: Java 1.7 or later is required to run Apache Drill ...
- 《调试九法——软硬件错误的排查之道》【PDF】下载
<调试九法--软硬件错误的排查之道>[PDF]下载链接: https://u253469.ctfile.com/fs/253469-231196352 内容简介 <调试九法:软硬件错 ...
- MVC系列——一个异常消息传递引发的思考
前言:最近在某个项目里面遇到一个有点纠结的小问题,经过半天时间的思索和尝试,问题得到解决.在此记录一下解决的过程,以及解决问题的过程中对.net里面MVC异常处理的思考.都是些老生常谈的问题,不多说, ...
- Linux:如何进行c++编程
不适应美帝的饮食,当一只咸鱼在apartment里Coding一波,学习学习如何在Ubuntu实现C++的编程 正文如下: (预备知识) 学习Vim: http://www.cnblogs.com/ ...
- Tableau Desktop 10.4.2 的安装和激活
在安装之前,首先我们要弄清楚Tableau是个什么鬼东西,我们为什么需要安装这款软件? Tableau将数据运算与美观的图表完美地嫁接在一起.它的程序很容易上手,各公司可以用它将大量数据拖放到数字&q ...