jdk 动态代理的原理

时间:2022-12-04 18:12:44

一、代理设计模式

代理设计模式是Java常用的设计模式之一。

特点:

01.委托类和代理类有共同的接口或者父类;

02.代理类负责为委托类处理消息,并将消息转发给委托类;

03.委托类和代理类对象通常存在关联关系,一个代理类对象与一个委托类对象关联;

04.代理类本身不是真正的实现者,而是通过调用委托类方法来实现代理功能;

二、静态代理与动态代理

按照代理类创建的时机,代理类分为两种:

01.静态代理:由我们程序猿或者特定的工具自动生成了源代码,在程序运行之前,class文件已经存在了;例如在serviceImpl.java中调用dao.xx(),真正的实现者是dao,service就可以理解为一个代理类;

02.动态代理:在程序运行期间,通过反射创建出来的代理类;

三、jdk动态代理

顾名思义,这种方式是由jdk为我们提供的。下面通过一个例子来演示。

01.创建一个Person接口

public interface Person {
void eat();
void sleep();
}

02.创建ZhangSan.java实现Person接口

public class ZhangSan implements Person {

    public void eat() {
System.out.println("吃饭...");
} public void sleep() {
System.out.println("睡觉...");
} }

03.创建代理类ZSProxy.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class ZSProxy implements InvocationHandler { //被代理类对象引用
private ZhangSan zhangSan; public ZSProxy(ZhangSan zhangSan) {
super();
this.zhangSan = zhangSan;
} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
//zhangSan.eat();
Object result = method.invoke(zhangSan, args);//可以获取目标方法的返回值
after();
return null;
} private void before() {
System.out.println("前置...");
}
private void after() {
System.out.println("后置...");
}
}

jdk动态代理中必须了解一个类和一个接口:Proxy类和InvocationHandler接口。

001.上述中的代理类实现了 InvocationHandler接口,此接口只有一个invoke方法

public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;

proxy:代理类对象

method:被代理的方法

args:被代理方法的参数列表

002.Proxy类

public class Proxy implements java.io.Serializable {
... public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException ...
}

loader:类加载器

interfaces:代理类实现的所有接口

h:InvocationHandler接口的一个实例

public class Test {

    public static void main(String[] args) throws Throwable {
System.out.println("----------------------JDK动态代理----------------------------");
//获取代理类对象
Person proxyInstance = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(),
new Class[] {Person.class}, new ZSProxy(new ZhangSan()));
proxyInstance.eat();
}

通过Proxy的newProxyInstance方法创建出代理对象,再有代理对象执行方法。

程序运行结果:

jdk 动态代理的原理

虽然效果实现了,但我们并不能从代码看到哪里调用的invoke方法??那么底层到底是怎么执行的呢??

jdk 动态代理的原理

首先要了解一个类 ==》$Proxy0.java

001. $Proxy0 是内存中的代理类,在$Proxy0中会持有一个InvocationHandler接口的实例类的引用,所以此Test类先是调用了内存中的$Proxy0.eat();

002.执行$Proxy0类中的invoke

我们debug运行观察:

jdk 动态代理的原理

内存中代理类的特征:

01.它是对目标类的代理,那么这个内存中中的代理类的类型必须和被代理类(目标类)的类型一致。

代码中 (Person)Proxy.newProxyInstance.. 进行了向下转型操作。

02.内存中的代理类必须持有InvocationHandler接口的实现类引用。

整个过程中$Proxy0我们是看不到的!那么有没有办法让它原形毕露呢?

/**
* 使用IO的方式将内存中代理类写入到文件中,然后反编译出来进行观察
*/
private static void createProxyClassFile() {
byte[] data = ProxyGenerator.generateProxyClass("$Proxy0.class", new Class[] {Person.class}); try {
FileOutputStream out = new FileOutputStream("$Proxy0.class");
out.write(data);
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

对$Proxy0.java反编译后

public final class class extends Proxy
implements Person
{ private static Method m1;
private static Method m4;
private static Method m2;
private static Method m3;
private static Method m0; public class(InvocationHandler invocationhandler) //通过构造将handler传入
{
super(invocationhandler);
} public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} public final void eat()
{
try
{
super.h.invoke(this, m4, null);// h:就是上文说的 此类中必须存在InvocationHandler接口实现类的引用,也是这里真正调用了invoke方法
return;
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} public final void sleep()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch (Error ) { }
catch (Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
} static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m4 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("eat", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("cn.yzx.jdkProxy.Person").getMethod("sleep", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}
catch (NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch (ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}

那么就真相大白了。

********************************************************************************************************************************************

以上就是jdk为我们提供的动态代理。我们也可以模仿它的实现原理,自定义我们的动态代理。

01.创建Person接口

package cn.yzx.myProxy;

public interface Person {
void eat() throws Throwable;
void sleep() throws Throwable;
}

02.创建ZhangSan实现类

package cn.yzx.myProxy;

public class ZhangSan implements Person {

    public void eat() {
System.out.println("吃饭...");
} public void sleep() {
System.out.println("睡觉...");
}
}

03.ZSProxy代理类

package cn.yzx.myProxy;
import java.lang.reflect.Method; public class ZSProxy implements MyInvocationHandler {
//目标对象
private Person person; public ZSProxy(Person person) {
this.person = person;
} @Override
public Object invoke(Object proxy, Method method, Object args) {
before();
try {
person.eat();
} catch (Throwable e) {
e.printStackTrace();
}
after();
return null;
} private void before() {
System.out.println("前置...");
}
private void after() {
System.out.println("后置...");
} }

04.自定义动态代理Proxy类

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider; /**
* 自定义的动态代理Proxy类
* @author Xuas
*/
public class MyProxy {
static String rt = "\r\n";
/**
* 自定义创建内存中代理实例的方法
* @param loader
* @param interfaces
* @param handler
* @return
*/
public static Object newProxyInstance(ClassLoader loader, Class interfaces, MyInvocationHandler handler) { try {
Method[] methods = interfaces.getMethods();
//01.使用拼凑字符串的方式将内存中的代理类拼出来
String proxyClass = "package cn.yzx.myProxy;" + rt + "import java.lang.reflect.Method;" + rt
+ "public class $Proxy0 implements " + interfaces.getName() + "{" + rt + "MyInvocationHandler h;" + rt
+ "public $Proxy0(MyInvocationHandler h) {" + rt + "this.h = h;" + rt + "}"
+ getMethodString(methods, interfaces) + rt + "}";
//02.使用IO将拼凑的代理类写入到文件中
String filePathName = "F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy/$Proxy0.java";
File file = new File(filePathName);
FileWriter fw = new FileWriter(file);
fw.write(proxyClass);
fw.flush();
fw.close(); //03.编译上面生成的java文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null,
null, null);//文件管理器
Iterable javaFileObjects = fileManager.getJavaFileObjects(filePathName);//获取java文件
CompilationTask task = compiler.getTask(null, fileManager, null, null, null, javaFileObjects);//获取编译任务
task.call();//编译
fileManager.close();//关闭文件管理器
/**
* 此时被编译后的class文件还在硬盘之中!它应该存在于JVM内存中!
* 所以要把硬盘中class文件加载到JVM内存中去!
*/
//04.把硬盘中的class文件加载到内存中去,要使用ClassLoader
MyClassLoader loader1 = new MyClassLoader("F:/Java/SpringAopDemo/src/main/java/cn/yzx/myProxy"); //proxy0Clazz:就是内存中代理类的class对象
Class<?> proxy0Clazz = loader1.findClass("$Proxy0");//返回被代理类的class对象
/**
* public $Proxy0(MyInvocationHandler h) {..}
* 要通过构造器的参数将handler对象的引用传进来
*/
Constructor<?> constructor = proxy0Clazz.getConstructor(MyInvocationHandler.class);
Object instance = constructor.newInstance(handler);//返回内存中代理类实例
return instance;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
} private static String getMethodString(Method[] methods, Class interfaces) { String proxyMe = ""; for (Method method : methods) {
proxyMe += "public void " + method.getName() + "() throws Throwable {" + rt + "Method md = "
+ interfaces.getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + rt
+ "this.h.invoke(this,md,null);" + rt + "}" + rt;
} return proxyMe;
}
}

05.自定义InvocationHandler接口

import java.lang.reflect.Method;

/**
* 自定义InvocationHandler接口
* @author Xuas
*
*/
public interface MyInvocationHandler {
public Object invoke(Object proxy, Method method, Object args);
}

06.自定义的类加载器

package cn.yzx.myProxy;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException; public class MyClassLoader extends ClassLoader { private File dir; public MyClassLoader(String path) {//要加载的文件路径
dir = new File(path);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException { if(null != dir) {
File clazzFile = new File(dir, name + ".class");
if(clazzFile.exists()) {
try {
FileInputStream input = new FileInputStream(clazzFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while((len = input.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
return defineClass("cn.yzx.myProxy."+name,
baos.toByteArray(),
0,
baos.size());//最终把输出流输出到JVM内存中
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return super.findClass(name);
} }

07.内存中自定义生成的 $Proxy0.java

package cn.yzx.myProxy;
import java.lang.reflect.Method;
public class $Proxy0 implements cn.yzx.myProxy.Person{
MyInvocationHandler h;
public $Proxy0(MyInvocationHandler h) {
this.h = h;
}
public void sleep() throws Throwable {
Method md = cn.yzx.myProxy.Person.class.getMethod("sleep",new Class[]{});
this.h.invoke(this,md,null);
}
public void eat() throws Throwable {
Method md = cn.yzx.myProxy.Person.class.getMethod("eat",new Class[]{});
this.h.invoke(this,md,null);
} }

08.测试类

public class Test {

    public static void main(String[] args) throws Throwable {
System.out.println("----------------------自定义动态代理----------------------------");
cn.yzx.myProxy.Person person2 = (cn.yzx.myProxy.Person)MyProxy.newProxyInstance(cn.yzx.myProxy.Person.class.getClassLoader(),
cn.yzx.myProxy.Person.class,
new cn.yzx.myProxy.ZSProxy(new cn.yzx.myProxy.ZhangSan()));
person2.eat();
}
}

程序运行结果:

jdk 动态代理的原理

总结:

通过观察Proxy中的newProxyInstance方法的参数可知,jdk动态代理只支持委托类和代理类实现共同的接口的方式。

出处:https://www.cnblogs.com/9513-/p/8432276.html

jdk 动态代理的原理的更多相关文章

  1. 解析JDK动态代理实现原理

    JDK动态代理使用实例 代理模式的类图如上.关于静态代理的示例网上有很多,在这里就不讲了. 因为本篇讲述要点是JDK动态代理的实现原理,直接从JDK动态代理实例开始. 首先是Subject接口类. p ...

  2. JDK 动态代理实现原理

    一.引言 Java动态代理机制的出现,使得Java开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象便能动态生成代理类.代理类会负责将所有方法的调用分派到委托对象上反射执行,在分派执行的过 ...

  3. JDK动态代理实现原理--转载

    之前虽然会用JDK的动态代理,但是有些问题却一直没有搞明白.比如说:InvocationHandler的invoke方法是由谁来调用的,代理对象是怎么生成的,直到前几个星期才把这些问题全部搞明白了.  ...

  4. Java&comma;JDK动态代理的原理分析

    1. 代理基本概念: 以下是代理概念的百度解释:代理(百度百科) 总之一句话:三个元素,数据--->代理对象--->真实对象:复杂一点的可以理解为五个元素:输入数据--->代理对象- ...

  5. JDK动态代理实现原理

    之前虽然会用JDK的动态代理,但是有些问题却一直没有搞明白.比如说:InvocationHandler的invoke方法是由谁来调用的,代理对象是怎么生成的.直到看了他的文章才彻底明白,附网址:htt ...

  6. 代理模式&lpar;静态代理、JDK动态代理原理分析、CGLIB动态代理&rpar;

    代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ...

  7. JDK动态代理

    一.基本概念 1.什么是代理? 在阐述JDK动态代理之前,我们很有必要先来弄明白代理的概念.代理这个词本身并不是计算机专用术语,它是生活中一个常用的概念.这里引用*上的一句话对代理进行定义: A ...

  8. 【原创】JDK动态代理,此次之后,永生难忘。

    动态代理,这个词在Java的世界里面经常被提起,尤其是对于部分(这里强调“部分”二字,因为有做了一两年就成大神的,实力强的令人发指,这类人无疑是非常懂动态代理这点小伎俩的)做了一两年新人来说,总是摸不 ...

  9. JDK动态代理深入理解分析并手写简易JDK动态代理(下)

    原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-05/27.html 作者:夜月归途 出处:http://www.guitu ...

随机推荐

  1. WPF EventSetter Handler Command

    最近做一个工具,突然发现ListBox和ListView等列表控件的MouseDoubleClick事件有时候是获取不到当前双击的行对象数据的,比如这样写: <ListBox Grid.Row= ...

  2. jQuery对象与DOM对象之间的转换(转)

    什么是jQuery对象? —就是通过jQuery包装DOM对象后产生的对象.jQuery对象是jQuery独有的,其可以使用jQuery里的方法. 比如: $(“#test”).html()   意思 ...

  3. 转载:C&plus;&plus; map的基本操作和使用

    声明:本文转自:http://www.cnblogs.com/hailexuexi/archive/2012/04/10/2440209.html 1.map简介 map是一类关联式容器.它的特点是增 ...

  4. iOS 南京互联网大会分享及个人见解 韩俊强的博客

    首先分两大块: 1.如何打造高效/稳定的App (重点): 2.软件自动化测试: 每日更新关注:http://weibo.com/hanjunqiang  新浪微博! 每日更新关注:http://we ...

  5. table-layout&colon;fixed&semi; 表格比例固定

    固定表格布局: 固定表格布局与自动表格布局相比,允许浏览器更快地对表格进行布局. 在固定表格布局中,水平布局仅取决于表格宽度.列宽度.表格边框宽度.单元格间距,而与单元格的内容无关. 通过使用固定表格 ...

  6. 【图文详细教程】maven3安装配置&plus;eclipse离线安装maven3插件《《唯一成功的教程~~~2018-01-09》》

    环境搭建前提: 1.电脑上已经安装了1.7以及以上版本的JDK(因为我提供的maven版本是最新的3.3.9的,要求最低JDK1.7) 2.配置好了ecplise并且能正常启动 第一步:下载maven ...

  7. layer&period;confirm在ASP&period;NET控件onclick上面的应用方法

    有些时候,你可能要修改控件的事件,元素本身.等,这个时候如何操作呢?下面提供一个思路: <asp:LinkButton Visible="false" ID="sh ...

  8. IDEA 及 Gradle 使用总结

    自动编译组件 目前Android开发的主流开发工具是 Eclipse 和 IDEA 目前主流的自动化打包工具时 ant,maven,gradle. maven工具中有自己的依赖仓库维护,很多开源支持包 ...

  9. New Year and Domino 二维前缀和

    C. New Year and Domino time limit per test 3 seconds memory limit per test 256 megabytes input stand ...

  10. Android 花钱 划动手指每天一元钱

    花钱(英文ColorMoney)是由上海花动传媒开发的一款免费的应用程序,现支持Android操作系统.安装花钱app后,用户将获得全新的手机锁屏背景图,其中包含各种有趣.唯美的壁纸类图片,亦包含应用 ...