首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >深聊-代理模式与反射

深聊-代理模式与反射

作者头像
浅墨清心
修改2022-05-16 16:54:41
修改2022-05-16 16:54:41
5060
举报

代理模式(Proxy Pattern):控制客户端对对象的访问,访问新的代理对象

代理

一、静态代理是个啥?

它为现有的每一个类都编写一个对应的代理类,并且让它实现和目标类相同的接口。在创建代理对象时,通过构造器塞入一个目标对象,然后在代理对象的方法内部调用目标对象同名方法,并在调用前后打印日志。

也就是说,代理对象 = 增强代码 + 目标对象(原对象)。有了代理对象后,就不用原对象了

二、动态代理是个啥?

借用网络大神的骚气讲解。

接口Class对象是大内太监,里面的方法和字段比做他的一身武艺,但是他没有小DD(构造器),所以不能new实例。一身武艺后继无人。那怎么办呢?非正常途径(动态代理):通过妙手圣医Proxy的克隆大法(Proxy.getProxyClass()),克隆一个Class,但是有小DD。所以这个克隆人Class可以创建实例,也就是代理对象。代理Class其实就是附有构造器的接口Class,一样的类结构信息,却能创建实例。

三、静态原理和动态原理的区别?

静态代理需要自己写代理类,代理类需要实现与目标对象相同的接口。

动态代理不需要自己编写代理类,代理类是动态生成的。

四、动态代理作用

1、动态代理主要用来做方法的增强,让你可以在不修改源码的情况下,增强一些方法,在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法)。因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加调用日志,做事务控制等。

2、还有一个有趣的作用是可以用作远程调用。比如现在有Java接口,这个接口的实现部署在其它服务器上,在编写客户端代码的时候,没办法直接调用接口方法,因为接口是不能直接生成对象的,这个时候就可以考虑代理模式(动态代理)了,通过Proxy.newProxyInstance代理一个该接口对应的InvocationHandler对象,然后在InvocationHandler的invoke方法内封装通讯细节就可以了。具体的应用,最经典的当然是Java标准库的RMI,其它比如hessian,各种webservice框架中的远程调用,大致都是这么实现的。

3、Proxy类的代码量被固定下来,不会因为业务的逐渐庞大而庞大。

4、可以实现AOP编程,实际上静态代理也可以实现,总的来说,AOP可以算作是代理模式的一个典型应用。

5、解耦,通过参数就可以判断真实类,不需要事先实例化,更加灵活多变。

五、深聊代理

代理类的生成时期

静态代理:代理类在编译期就生成了

动态代理:代理类在Java运行时动态生成的,动态代理有JDK代理和CGLib代理两种

静态代理

将真实类作为代理类的一个属性,将对象传入处理,直接访问代理类

由聚合代替继承,而不是创建无数的子类

类图

动态类将实现类传入,作为属性值

动态代理


JDK动态代理

Java提供了动态代理类Proxy,通过创建代理对象的静态方法 newProxyInstance()方法获取

newProxyInstance()需要三个参数

ClassLoader 类加载器,用于加载代理类。可以通过目标对象获取类加载器 Class<?> [] interface 代理类实现的接口的字节码对象 InvocationHandler 代理对象的调用处理程序 重写invoke方法

代理最终会调用 InvocationHandler 类 的 invoke() 方法

invoke()有三个入参,方便做动态增强使用

Object proxy 代理对象。和代理对象是同一个对象,基本不用 Method method 对接口中的方法进行封装的method对象 Object[] args 调用方法的实际参数

执行目标对象的方法

Object object = method.invoke(目标对象,arge); 利用反射

总结:根据实现的接口,利用反射创建接口中方法,组成代理对象,根据调用实现类对象再利用反射执行方法

执行流程

  1. 在测试类中通过代理类调用sell()方法
  2. 根据多态特性,执行的是代理类($Proxy0)的sell()方法
  3. 代理对象($Proxy0)中sell()调用了InvacationHandler接口子实现类对象的invoke()方法
  4. invoke() 通过反射执行真实对象(TrainStation)中的sell方法

反射,这里先说个大概----------------------------------------------------------------

获取Method方法

反射原理:.class 文件经过类加载器 会产生 .class类的实例,每个类的方法属性等都包含在实例上,所有加载完后会有一个class对象集合,找到匹配的class,反射动态生成字节码,加载到JVM运行

代码语言:javascript
复制
// 1,获取Person.class 字节码文件对象
Class c = Class.forName(className);
// 2,获取构造方法
// public Person(String name, int age, String address)
Constructor con = c.getConstructor(String.class, int.class, String.class);
 
// 3,创建对象,运行构造方法
Object obj = con.newInstance("小明", 20, "中国");
System.out.println(obj);
 
// 4,获取指定的方法
// private void method5(){}
String methodName = prop.getProperty("methodName");
Method m5 = c.getDeclaredMethod(methodName, null);
// 5,开启暴力访问
m5.setAccessible(true);
// 6,执行找到的方法
m5.invoke(obj, null);

getDeclaredMethod() 方法可以获取 指定class并入参一致的方法,封装成Method对象 getDeclaredMethod() 核心方法有三个 1. privateGetDeclaredMethods() 返回class声明的方法列表 2.searchMethods() 找到匹配名称的方法对象 3.copy()方法 返回一个新对象 ----privateGetDeclaredMethods() 核心方法 -------reflectionData() 查询是否有缓存 -------ReflectionData 数据结构缓存JVM属性数据 是SoftReference类型(弱应用)GC高时会被回收 -------newReflectionData 先cas,通过unsafe.compareAndSwapObject方法重新设置reflectionData字段 --------------unsafe.compareAndSwapObject 比较的就是两个 Java Object 的地址,如果相等则将新的地址(Java Object)赋给该字段 --------------最终采用ASM 动态修改二进制,从JVM获取并创建ReflectionData对象

invoke()方法实现原理

MethodAccessor 对象是实现的关键,MethodAccessor本身是个接口 通过acquireMethodAccessor() 方法中,ReflectionFactory类的newMethodAccessor创建了实现MethodAccessor的对象 ReflectionFactory有 2 个重要的字段:noInflation(默认false)和inflationThreshold(默认15 当noInflation为false ,用DelegatingMethodAccessorImpl创建代理对象,将当前对象传入并invoke调用的NativeMethodAccessorImpl对象的invoke方法 NativeMethodAccessorImpl 对象中用到了inflationThreshold字段,如果大于15创建一个新的调用对象,创建MethodAccessorImpl会在ClassDefiner.defineClass方法实现中,每被调用一次都会生成一个DelegatingClassLoader类加载器对象 如果一直沿用一个类加载器,类加载器就无法被回收,对应类也无法回收,会占用内存

反射总结:反射可以根据类加载器中类信息创建对象,并获取对象方法,创建方法实例并执行

反射知识结束----------------------------------------------------------------------

asm 原理-------------------------------

可以操作字节码,是通过指令级别,也就是JVM指令操作的

JVM栈帧是通过指令 将 局部变量表中-- 加载到 --操作数栈-- 通过总线-- 放到CPU计算 ,返回操作数栈 - 最终将计算结果修改到 --局部变量表中 -- 最后返出栈帧

asm 采用访问者模式 ,为我们提供了ClassVisitor,MethodVisitor,FieldVisitor API接口,每当ASM扫描到类字段是会回调visitField方法,扫描到类方法是会回调MethodVisitor,

可以将程序编译成指令集执行

琥珀川:史上最通俗易懂的ASM教程​zhuanlan.zhihu.com/p/94498015?utm_source=wechat_timeline


CGLIB动态代理

jdk要求必须定义接口,他是对接口代理的,CGLIB 是可以不传入接口的

CGLIB是动态生成被代理类的子类,底层使用ASM 字节码生成框架实现的 不能对final修饰的类和方法代理

指定父类,设置回调函数,创建代理对象

实现MethodInterceptor 接口,编写 intercept 回调方法

动态代理与静态代理对比

动态代理所有方法回调同一个方法,方便增强

静态代理 接口添加一个方法,所有实现都要实现这个方法

代理模式的优点

  1. 代理对象相当于是中介,保护目标对象
  2. 可以扩展目标对象
  3. 能将客户端和目标对象分离,降低系统的耦合度

使用场景

  1. 远程代理
  2. 防火墙代理
  3. 保护代理

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 代理
    • 一、静态代理是个啥?
    • 二、动态代理是个啥?
    • 三、静态原理和动态原理的区别?
    • 四、动态代理作用
    • 五、深聊代理
    • 动态代理
  • 反射,这里先说个大概----------------------------------------------------------------
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档