好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

详解Java Proxy动态代理机制

一、Jvm加载对象

在说 Java 动态代理 之前,还是要说一下Jvm加载对象的过程,这个依旧是理解动态代理的基础性原理:

Java类即源代码程序 .java 类型文件,经过编译器编译之后就被转换成字节代码 .class 类型文件,类加载器负责读取字节代码,并转换成java.lang.Class对象,描述类在元数据空间的数据结构,类被实例化时,堆中存储实例化的对象信息,并且通过对象类型数据的指针找到类。

过程描述:源码->.java文件->.class文件->Class对象->实例对象

所以通过New创建对象,独断其背后很多实现细节,理解上述过程之后,再了解一个常用的设计模式,即代理模式。

二、代理模式

2.1、基本描述

代理模式给某一个(目标)对象提供一个代理对象,并由代理对象持有目标对象的引用。所谓代理,就是一个对象代表另一个对象执行相应的动作程序。而代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式在实际的生活中场景很多,例如中介、律师、代购等行业,都是简单的代理逻辑,在这个模式下存在两个关键角色:

目标对象角色:即代理对象所代表的对象。

代理对象角色:内部含有目标对象的引用,可以操作目标对象;AOP编程就是基于这个思想。

2.2、静动态模式

静态代理:在程序运行之前确定代理角色,并且明确代理类和目标类的关系。 动态代理:基于Java反a射机制,在JVM运行时动态创建和生成代理对象。

三、静态代理

基于上述静态代理的概念,用一段代码进行描述实现,基本逻辑如下:

明确目标对象即被代理的对象; 定义代理对象,通过构造器持有目标对象; 代理对象中定义前后置增强方法;

目标对象与前后置增强代码就组成了代理对象,这样就不用直接访问目标对象,像极了电视剧中那句话:我是律师,我的当事人不方便和你对话。

public class Proxy 01 { public static void main ( String [] args ) { TargetObj targetObj = new TargetObj () ; ProxyObj proxyObj = new ProxyObj ( targetObj ) ; proxyObj . invoke (); } } class TargetObj { public void execute (){ System . out . println ( "目标类方法执行..." ); } } class ProxyObj { private TargetObj targetObj ; /** * 持有目标对象 */ public ProxyObj ( TargetObj targetObj ){ this . targetObj = targetObj ; } /** * 目标对象方法调用 */ public void invoke (){ before () ; targetObj . execute (); after () ; } /** * 前后置处理 */ public void before (){ System . out . println ( "代理对象前置处理..." ); } public void after (){ System . out . println ( "代理对象后置处理..." ); } }

静态代理明确定义了代理对象,即有一个代理对象的 .java 文件加载到JVM的过程,很显然的一个问题,在实际的开发过程中,不可能为每个目标对象都定义一个代理类,同样也不能让一个代理对象去代理多个目标对象,这两种方式的维护成本都极高。

代理模式的本质是在目标对象的方法前后置入增强操作,但是又不想修改目标类,通过前面反射机制可以知道,在运行的时候可以获取对象的结构信息,基于Class信息去动态创建代理对象,这就是动态代理机制。

顺便说一句:技术的底层实现逻辑不好理解是众所周知,然而基础知识点并不复杂,例如代理模式的基本原理,但是结合到实际的复杂应用中(AOP模式),很难活灵活现的理解到是基于反射和动态代理的方式实现的。

四、动态代理

4.1、场景描述

基于一个场景来描述动态代理和静态代理的区别,即最近几年很火的概念,海外代购:

在代购刚兴起的初期,是一些常去海外出差的人,会接代购需求,即代理人固定;后来就兴起海外代购平台,海淘等一系列产品,即用户代购需求(目标对象)由代购平台去实现,但是具体谁来操作这个就看即时分配,这个场景与动态代理的原理类似。

4.2、基础API案例

首先看两个核心类,这里简述下概念,看完基本过程再细聊:

Proxy-创建代理对象,核心参数:

ClassLoader:(目标类)加载器; Interfaces:(目标类)接口数组; InvocationHandler:代理调用机制;

InvocationHandler-代理类调用机制:

invoke:这个上篇说的反射原理; method:反射类库中的核心API;

目标对象和接口

interface IUser { Integer update ( String name ) ; } class UserService implements IUser { @Override public Integer update ( String name ) { Integer userId = 99 ; System . out . println ( "UserId=" + userId + ";updateName=" + name ); return userId ; } }

代理对象执行机制

class UserHandler implements InvocationHandler { private Object target ; public UserHandler ( Object target ){ this . target = target ; } @Override public Object invoke ( Object proxy , Method method , Object [] args ) throws Throwable { System . out . println ( "before()..." ); Object result = method . invoke ( target , args ); System . out . println ( "after()..." ); return result ; } }

具体组合方式

public class Proxy02 { public static void main ( String [] args ) { /* * 生成$Proxy0的class文件 */ System . getProperties (). put ( "sun.misc.ProxyGenerator.saveGeneratedFiles" , "true" ); /* * 目标对象信息 */ IUser userService = new UserService (); ClassLoader classLoader = userService . getClass (). getClassLoader (); Class <?>[] interfaces = UserService . class . getInterfaces () ; /* * 创建代理对象 */ InvocationHandler userHandler = new UserHandler ( userService ); /* * 代理类对象名 * proxyClassName=com.java.proxy.$Proxy0 */ String proxyClassName = Proxy . newProxyInstance ( classLoader , interfaces , userHandler ). getClass (). getName (); System . out . println ( "proxyClassName=" + proxyClassName ); /* * 具体业务实现模拟 */ IUser proxyUser1 = ( IUser ) Proxy . newProxyInstance ( classLoader , interfaces , userHandler ); IUser proxyUser2 = ( IUser ) Proxy . newProxyInstance ( classLoader , interfaces , userHandler ); proxyUser1 . update ( "cicada" ) ; proxyUser2 . update ( "smile" ) ; } }

这里之所以要生成代理类的结构信息,因为从JVM加载的过程看不到相关内容,关键信息再次被独断:

javap -v Proxy02.class

查看代理类名称

/* * proxyClassName=com.java.proxy.$Proxy0 */ String proxyClassName = Proxy . newProxyInstance ( classLoader , interfaces , userHandler ). getClass (). getName (); System . out . println ( "proxyClassName=" + proxyClassName );

下意识输出代理对象名称,这里即对应JVM机制,找到Class对象名,然后分析结构,这样就明白动态代理具体的执行原理了。

生成代理类.class文件

System . getProperties (). put ( "sun.misc.ProxyGenerator.saveGeneratedFiles" , "true" );

通过上面JVM加载对象的机制可知,描述代理类的Class对象一定存在,只是在运行时并没有生成显式的 .class 文件,通过上面生成代理类 .class 的语法,会在项目目录的 /com/java/proxy 路径下创建文件。

顺便说一句:作为一只程序员,复杂总是和我们环环相绕,说好的简单点呢?

4.3、代理类结构

继承与实现

class $Proxy0 extends Proxy implements IUser {}

从代理类的功能来思考,可以想到需要继承Proxy与实现IUser接口,还有就是持有调用机制的具体实现类,用来做业务增强。

构造方法

public $Proxy0 ( InvocationHandler var1 ) throws { super ( var1 ); }

通过构造方法,持有UserHandler具体的执行机制对象。

接口实现

final class $Proxy0 extends Proxy implements IUser { private static Method m3 ; public final Integer update ( String var1 ) throws { try { return ( Integer ) super . h . invoke ( this , m3 , new Object []{ var1 }); } catch ( RuntimeException | Error var3 ) { throw var3 ; } catch ( Throwable var4 ) { throw new UndeclaredThrowableException ( var4 ); } } }

目标类的基本需求 update() 方法,通过代理类进行承接,并基于UserHandler实现具体的增强业务处理。

基础方法

final class $Proxy0 extends Proxy implements IUser { private static Method m0 ; private static Method m1 ; private static Method m2 ; public $Proxy0 ( InvocationHandler var1 ) throws { super ( var1 ); } static { try { m1 = Class . forName ( "java.lang.Object" ). getMethod ( "equals" , Class . forName ( "java.lang.Object" )); m2 = Class . forName ( "java.lang.Object" ). getMethod ( "toString" ); m3 = Class . forName ( "com.java.proxy.IUser" ). getMethod ( "update" , Class . forName ( "java.lang.String" )); m0 = Class . forName ( "java.lang.Object" ). getMethod ( "hashCode" ); } catch ( NoSuchMethodException var2 ) { throw new NoSuchMethodError ( var2 . getMessage ()); } catch ( ClassNotFoundException var3 ) { throw new NoClassDefFoundError ( var3 . getMessage ()); } } public final boolean equals ( Object var1 ) throws { try { return ( Boolean ) super . h . invoke ( this , m1 , new Object []{ var1 }); } catch ( RuntimeException | Error var3 ) { throw var3 ; } catch ( Throwable var4 ) { throw new UndeclaredThrowableException ( var4 ); } } public final String toString () throws { try { return ( String ) super . h . invoke ( this , m2 , ( Object []) null ); } catch ( RuntimeException | Error var2 ) { throw var2 ; } catch ( Throwable var3 ) { throw new UndeclaredThrowableException ( var3 ); } } public final int hashCode () throws { try { return ( Integer ) super . h . invoke ( this , m0 , ( Object []) null ); } catch ( RuntimeException | Error var2 ) { throw var2 ; } catch ( Throwable var3 ) { throw new UndeclaredThrowableException ( var3 ); } } }

基于Object类,定义Java中几个常用方法equals()判断,toString()方法,hashCode()值,这个在分析Map源码的时候有说过为什么这几个方法通常都是一起出现。

4.4、JDK源码

上面是案例执行的过程和原理,还有一个关键点要明白,即JDK源码的逻辑:

IUser proxyUser = ( IUser ) Proxy . newProxyInstance ( classLoader , interfaces , userHandler );

Proxy提供的静态方法 newProxyInstance() ,通过各个参数的传入,构建一个新的代理Class对象,即$Proxy0类的结构信息,这里再回首看下三个核心参数:

ClassLoader:基于JVM运行过程,所以需要获取目标类UserService的类加载器; Interfaces:目标类UserService实现的接口,从面向对象来考虑,接口与实现分离,代理类通过实现IUser接口,模拟目标类的需求; InvocationHandler:代理类提供的功能封装即UserHandler,可以在目标方法调用前后做增强处理;

最后总结一下动态代理的实现的核心技术点:Jvm加载原理、反射机制、面向对象思想;每次阅读JDK的源码都会惊叹设计者的鬼斧神工,滴水穿石坚持才会有收获。

五、源代码地址

GitHub·地址

https://github测试数据/cicadasmile/java-base-parent

GitEE·地址

https://gitee测试数据/cicadasmile/java-base-parent

以上就是详解Java的Proxy动态代理机制的详细内容,更多关于Java Proxy 动态代理机制的资料请关注其它相关文章!

原文链接:https://HdhCmsTestcnblogs测试数据/cicada-smile/p/14942945.html

查看更多关于详解Java Proxy动态代理机制的详细内容...

  阅读:13次