-
Notifications
You must be signed in to change notification settings - Fork 117
动态代理
zhangpan edited this page Aug 21, 2021
·
5 revisions
建议先了解静态代理
-
由于代理类要实现与被代理类一致的接口,当有多个类需要被代理时,要么代理类实现所有被代理类的接口,这样会使代理类过于庞大;要么使用多个代理类,每个代理类只代理一个被代理类,但是这样又会需要构造多个代理类。
-
当接口需要增加、删除、修改方法时,被代理类与代理类都需要修改,不易维护。
为了解决上述问题,可以使用动态代理,自动生成代理类。
这里涉及到两个类:java.lang.reflect.Proxy和
java.lang.reflect.InvocationHandler接口
举个例子:
Ryan想在上海买一套房子,但是他又不懂房地产的行情,于是委托了中介(Proxy)来帮助他买房子。
使用动态代理实现,首先定义一个IPersonBuyHouse的接口,且有一个buyHouse的方法:
public interface IPersonBuyHouse {
// 购买房子的方法,返回值代表是否成功购买
boolean buyHouse(String name);
}
使用动态代理实现Ryan买房子的需求:
public static void main(String[] args) {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在执行购买逻辑前可以先做一些校验,对于不符合要求的不予执行,并返回false。这里省略不写了...
if (method.getName().equals("buyHouse")) {
System.out.println(args[0] + " will buy a house.");
}
// 返回true,表示成功购买
return true;
}
};
IPersonBuyHouse person = (IPersonBuyHouse) Proxy
.newProxyInstance(IPersonBuyHouse.class.getClassLoader(), // ClassLoader
new Class[]{IPersonBuyHouse.class}, // 传入要实现的接口
handler); // 传入处理调用方法的InvocationHandler
person.buyHouse("Ryan");
}
动态代理是在运行时根据某个接口生成对应的代理类的字节码,然后加载到JVM的,并创建这个接口的实例的过程。
对于第二节中的例子进行分析,通过Proxy.newProxyInstance这个方法会生成一个IPersonBuyHouse的实现类。假设生成的这个类叫Ryan,Ryan这个类实现了IPersonBuyHouse,并重写了buyHouse的方法。在这个buyHouse的方法中会调用InvocationHandler的invoke方法。
Ryan类的代码大致如下:
public class Ryan implements IPersonBuyHouse {
// buyHouse的真正逻辑在这个匿名内部类的invoke方法中
private InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
// 被代理类真正执行的逻辑
if (method.getName().equals("buyHouse")) {
System.out.println(args[0] + " will buy a house.");
}
return true;
}
};
@Override
public boolean buyHouse(String name) {
try {
// 反射获取buyHouse这个Method
Method buyHouseMethod = IPersonBuyHouse.class.getDeclaredMethod("buyHouse", String.class);
// 将buyHouse的参数封装成一个数组
Object[] params = new Object[1];
params[0] = name;
// 实际调用了InvocationHandler的invoke方法
return (boolean) handler.invoke(this, buyHouseMethod, params);
} catch (Throwable e) {
e.printStackTrace();
}
return false;
}
}
- JMM与volatile关键字
- synchronized的实现原理
- synchronized等待与唤醒机制
- AQS的实现原理
- ReentrantLock的实现原理
- ReentrantLock等待与唤醒机制
- CAS、Unsafe类以及Automic并发包
- ThreadLocal的实现原理
- 线程池的实现原理
- Java线程中断机制
- 多线程与并发常见面试题
- Android基础知识汇总
- MVC、MVP与MVVM
- SparseArray实现原理
- ArrayMap的实现原理
- SharedPreferences
- Bitmap
- Activity的启动模式
- Fragment核心原理
- 组件化项目架构搭建
- 组件化WebView架构搭建
- 为什么 Activity.finish() 之后 10s 才 onDestroy ?
- Binder与AIDL
- Binder实现原理
- Android系统启动流程
- InputManagerService
- WindowManagerService
- Choreographer详解
- SurfaceFlinger
- ViewRootImpl
- ActivityManagerService
- APP启动流程
- PMS安装与签名校验
- Dalvik与ART
- 内存优化策略
- UI界面及卡顿优化
- App启动优化
- ANR问题
- 包体积优化
- APK打包流程
- 电池电量优化
- Android屏幕适配
- 线上性能监控1--线上监控切入点
- 线上性能监控2--Matrix实现原理
- Glide实现原理
- OkHttp实现原理
- Retrofit实现原理
- RxJava实现原理
- RxJava中的线程切换与线程池
- LeakCanary实现原理
- ButterKnife实现原理
- ARouter实现原理
- Tinker实现原理
- 2. 两数相加
- 19.删除链表的倒数第 N 个结点
- 21. 合并两个有序链表
- 24. 两两交换链表中的节点
- 61. 旋转链表
- 86. 分隔链表
- 92. 反转链表 II
- 141. 环形链表
- 160. 相交链表
- 206. 反转链表
- 206 反转链表 扩展
- 234. 回文链表
- 237. 删除链表中的节点
- 445. 两数相加 II
- 面试题 02.02. 返回倒数第 k 个节点
- 面试题 02.08. 环路检测
- 剑指 Offer 06. 从尾到头打印链表
- 剑指 Offer 18. 删除链表的节点
- 剑指 Offer 22. 链表中倒数第k个节点
- 剑指 Offer 35. 复杂链表的复制
- 1. 两数之和
- 11. 盛最多水的容器
- 53. 最大子序和
- 75. 颜色分类
- 124.验证回文串
- 167. 两数之和 II - 输入有序数组 -169. 多数元素
- 189.旋转数组
- 209. 长度最小的子数组
- 283.移动0
- 303.区域和检索 - 数组不可变
- 338. 比特位计数
- 448. 找到所有数组中消失的数字
- 643.有序数组的平方
- 977. 有序数组的平方