2021年6月27日星期日

Java进阶 | Proxy动态代理机制详解

静态代理明确定义了代理对象,即有一个代理对象的`.java`文件加载到JVM的过程,很显然的一个问题,在实际的开发过程中,不可能为每个目标对象都定义一个代理类,同样也不能让一个代理对象去代理多个目标对象,这两种方式的维护成本都极高。代理模式的本质是在目标对象的方法前后置入增强操作,但是又不想修改目标类,通过前面反射机制可以知道,在运行的时候可以获取对象的结构信息,基于Class信息去动态创建代理对象,这就是动态代理机制。

一、Jvm加载对象

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

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

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

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

二、代理模式

1、基本描述

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

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

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

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

2、静动态模式

  • 静态代理:在程序运行之前确定代理角色,并且明确代理类和目标类的关系。

  • 动态代理:基于Java反射机制,在JVM运行时动态创建和生成代理对象。

三、静态代理

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

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

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

public class Proxy01 { 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模式),很难活灵活现的理解到是基于反射和动态代理的方式实现的。

四、动态代理

1、场景描述

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

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

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路径下创建文件。

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

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");   ......

原文转载:http://www.shaoqun.com/a/833877.html

跨境电商:https://www.ikjzd.com/

环球易购:https://www.ikjzd.com/w/1034

心怡科技:https://www.ikjzd.com/w/1327

Sunrate:https://www.ikjzd.com/w/2685


静态代理明确定义了代理对象,即有一个代理对象的`.java`文件加载到JVM的过程,很显然的一个问题,在实际的开发过程中,不可能为每个目标对象都定义一个代理类,同样也不能让一个代理对象去代理多个目标对象,这两种方式的维护成本都极高。代理模式的本质是在目标对象的方法前后置入增强操作,但是又不想修改目标类,通过前面反射机制可以知道,在运行的时候可以获取对象的结构信息,基于Class信息去动态创建代理对
cbo:https://www.ikjzd.com/w/2670
lithium:https://www.ikjzd.com/w/2505
拍怕网:https://www.ikjzd.com/w/2205
华翰物流:https://www.ikjzd.com/w/1799
阿里巴巴数字化工具帮传统外贸企业重获新生!:https://www.ikjzd.com/articles/21876
(精品分析)亚马逊英国站汽车钥匙保护套类目市场调查数据报告:https://www.ikjzd.com/articles/21877
亚马逊FBA和自发货模式优劣势详解!哪一种更适合你?:https://www.ikjzd.com/articles/21878
亚马逊跨境电商的未来发展趋势如何?:https://www.ikjzd.com/articles/21880
为什么我的心会痛一段时间?因为心肌坏死了:http://lady.shaoqun.com/a/390512.html
牙痛过后,就不再疼了。是"自愈"吗?也许不是。......:http://lady.shaoqun.com/a/390513.html
"刚开始有点疼,后来就没感觉了。"这个13岁的女孩在校园里被扫雪冻伤了。谁错了:http://lady.shaoqun.com/a/390514.html
痛苦过后,你不会感到痛苦,有的只会是莫莫的心:http://lady.shaoqun.com/a/390515.html

没有评论:

发表评论