引出问题:动态代理中是谁调用了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动态代理的思考的更多相关文章

  1. AOP jdk动态代理

    一: jdk动态代理是Spring AOP默认的代理方法.要求 被代理类要实现接口,只有接口里的方法才能被代理,主要步骤是先创建接口,接口里创建要被代理的方法,然后定义一个实现类实现该接口,接着将被代 ...

  2. Spring AOP --JDK动态代理方式

    我们知道Spring是通过JDK或者CGLib实现动态代理的,今天我们讨论一下JDK实现动态代理的原理. 一.简述 Spring在解析Bean的定义之后会将Bean的定义生成一个BeanDefinit ...

  3. Spring AOP JDK动态代理与CGLib动态代理区别

    静态代理与动态代理 静态代理 代理模式 (1)代理模式是常用设计模式的一种,我们在软件设计时常用的代理一般是指静态代理,也就是在代码中显式指定的代理. (2)静态代理由 业务实现类.业务代理类 两部分 ...

  4. AOP学习心得&jdk动态代理与cglib比较

    什么是AOP AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善.OOP引入 ...

  5. Spring AOP详解 、 JDK动态代理、CGLib动态代理

    AOP是Aspect Oriented Programing的简称,面向切面编程.AOP适合于那些具有横切逻辑的应用:如性能监测,访问控制,事务管理以及日志记录.AOP将这些分散在各个业务逻辑中的代码 ...

  6. jdk动态代理与cglib代理、spring aop代理实现原理

    原创声明:本博客来源与本人另一博客[http://blog.csdn.net/liaohaojian/article/details/63683317]原创作品,绝非他处摘取 代理(proxy)的定义 ...

  7. jdk动态代理与cglib代理、spring aop代理实现原理解析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

  8. 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

  9. Spring AOP高级——源码实现(3)AopProxy代理对象之JDK动态代理的创建过程

    spring-aop-4.3.7.RELEASE  在<Spring AOP高级——源码实现(1)动态代理技术>中介绍了两种动态代理技术,当然在Spring AOP中代理对象的生成也是运用 ...

  10. 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理

    Spring AOP详解 . JDK动态代理.CGLib动态代理  原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...

随机推荐

  1. python字典的操作

    思维导图如下 1.字典的增加 dic1={'name':'wujie','age':18,'gender':'男'} dic1['profession']='python全栈' dic1.setdef ...

  2. Iframe 自适应高度

    网页中,经常遇见嵌套问题.我们怎么解决好点,我个人喜欢使用 Html 中的 Iframe 标签.忘记在哪里找的代码了. Iframe 的代码: <iframe src="indexpa ...

  3. nginx日常维护常用命令

    http://www.jb51.net/article/47750.htm 一.简明nginx常用命令 1. 启动 Nginx poechant@ubuntu:sudo ./sbin/nginx 2. ...

  4. EditText禁用系统键盘,光标可以继续使用

    在项目中有时候需要使用到自己的键盘,那这个时候就不希望系统键盘在弹出,而且光标还要继续显示,其实一个方法就可以简单实现 /** * 禁止Edittext弹出软件盘,光标依然正常显示. */ publi ...

  5. DWR3.0 服务器推送及解惑

    前言:在慕课网上学习一下服务器推送给客户端技术,代码亲测过,没毛病,今天整理记录一下: 一.环境搭建 直接上图,简单粗暴,myeclipse上file->new->WebProject 二 ...

  6. OC学习12——字符串、日期、日历

    前面主要学习了OC的基础知识,接下来将主要学习Foundation框架的一些常用类的常用方法.Foubdation框架是Cocoa编程.IOS编程的基础框架,包括代表字符串的NSString(代表字符 ...

  7. XE10 clientDataset 访问 DataSnap 服务端报错问题,锲而不舍找方法,终于解决了

    1. 开发环境说明:win 10 下安装了XE10.2和Delphi7 2.按照网上datasnap 三层与使用xe10 自带的samples 的例子,访问数据库都要报莫名的地址错误,这个太不人性化: ...

  8. Android Weekly Notes Issue #288

    Android Weekly Issue #288 December 17th, 2017 Android Weekly Issue #288 本期内容主要包括介绍Kotlin DSL使用kotlin ...

  9. Java JTS & 空间数据模型

    空间数据模型 判断两个几何图形是否存在指定的空间关系.包括: 相等(equals).分离(disjoint).相交(intersect).相接(touches).交叉(crosses).包含于(wit ...

  10. myecplise自带的tomcat问题

    今天做一个项目时候,发现myecplise自带的tomcat上面部署了是可以运行的,可是当部署到自己下载的tomcat时候,就报错,tomcat可以启动,项目无法启动,查了问题,发现是web,xml中 ...