1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- 代理模式
- 代理模式(Proxy Pattern)是Java中常用的一种设计模式。它属于【结构型】模式。
- 1、代理模式的核心思想:
- 通过一个"代理对象"来代替"真实对象",通过这种方式来控制对"真实对象"的访问。
- 说人话,代理模式中代理,就是代理人,中介。
- 代理模式就像中介、代购、经纪人。比如想租房,不需要直接找房东(真实对象),
- 而是找中介(代理对象)。中介会帮你联系房东,还能额外提供看房、签合同等等服务。
- 2、代理模式的核心特点:
- 1、【代理类和真实类实现相同的接口,或者继承相同的父类】,保证代理对象和真实对象都能
- 被统一使用,比如房东可以令你看房,中介也可以领你看房。
- 2、【代理类持有真实对象的引用,可以直接调用真实对象的方法】,比如:中介手里有房东的联系方式,
- 能够让房东最终交房。
- 3、【代理类可以在调用真实对象的方法前后,添加额外的操作】。比如:中介要在签合同之前核实你的身份,
- 签完合同要给中介费。
- 3、为什么需要代理模式?
- 代理模式的核心价值其实就是"控制访问"和"增强功能":
- 1、保护真实对象,比如限制只有管理员才能访问某一个敏感对象
- 2、增强功能:在不修改真实对象的代码的前提下,添加一些功能,日志、权限校验、缓存等等功能
- 3、远程访问:通过代理模式访问远端服务器里面的对象
- 4、代理模式的分类:
- 静态代理
- 动态代理
- 两种代理方式核心逻辑是一样的, 但是实现的方式和使用的场景不一样。
- --------------------------------------------------------------
- 静态代理(static proxy)
- 静态代理就是在编译期就已经确定了代理类的代码, 需要我们手动写代理类。
- 实现步骤:租房
- 1、定义接口(代表代理类和真实类有共同的行为)
- 接口,其实就是代理类和真实类的"约定",确保两个类能够被统一调用,
- 创建一个Rent接口,包含租房方法。
- 2、实现真实类---被代理的对象
- 就是房东,是最终提供服务的对象,需要实现Rent接口
- 3、实现代理类---中介
- |-实现Rent接口,保证行为和真实类一致。
- |-持有真实类的引用,可以通过构造传入。
- |-在调用真实类的方法的前后,添加额外的操作,比如中介的服务
- 静态代理的优缺点:
- 优点:
- 简单、直观、代码好理解、不需要依赖复杂的框架。
- 缺点:
- 当接口新增方法,代理类和真实类都需要修改。
- 有多少个真实类,就得有多少个代理类
- 动态代理(Dynamic Proxy)
- 动态代理是在程序运行时,通过反射动态生成代理类的代码,不需要我们手动编写代理类。
- 解决了静态代理的每个真实类都要手写一个代理类的情况,适合批量处理多个类的场景。
- Java中常用的动态代理有两种:
- JDK动态代理,基于接口的。
- CGLIB动态代理,基于继承的。
- 1、JDK动态代理是Java官方提供的,java.lang.reflect包里,要求被代理的类必须要实现接口。
- 以给所有的方法添加日志,作为例子:
- 实现步骤:
- 1、定义接口(比如:UserService)和真实类(实现类:UserServiceImpl)
- 2、实现InvocationHandler接口,里面是代理逻辑
- JDK动态代理需要一个"调用处理程序|调用处理器",实现InvocationHandler接口,
- 用来定义代理类的额外操作,比如日志。
- InvocationHandler接口里只有一个抽象方法:
- Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
- 包含参数:
- proxy:动态生成的奥地利对象,一般我们是不用的
- method:当前调用的方法,比如addUser、deleteUser
- args:方法的参数列表,比如addUser的参数。
- 我们既然要使用JDK动态代理实现为每个方法添加日志,就要创建一个日志处理类
- 3、生成代理对象,并且进行测试
- 通过Proxy.newProxyInstance()方法动态的生成代理对象。
- newProxyInstance()方法需要传入3个参数:
- |-ClassLoader loader 真实类的类加载器 target.getClass().getClassLoader();
- |-Class<?>[] interfaces 真实类实现的接口 target.getClass().getInterfaces();
- |-InvocationHandler h 调用处理器,比如我们自己创建的日志处理器LogHandler
- JDK动态代理只能代理"实现了某个接口的类",如果某个类没有接口,那么就代理不了。这时候就需要CGLIB动态代理
- CGLIB动态代理,基于继承的。
- CGLIB动态代理是一个第三方库,是通过继承被代理类生成代理子类,所以被代理类不能是final修饰的,否则就无法继承,不能代理。
- 实现步骤:
- 0、先导入CGLIB依赖库。
- 1、定义没有接口的真实类,假如某个类没有实现任何接口
- 2、实现MethodInterceptor接口--处理代理逻辑
- CGLIB动态代理是使用MethodInterceptor接口定义代理的额外操作,核心的方法是intercept()方法
- intercept()方法包含4个参数:
- Object obj 代理对象,子类实例
- java.lang.reflect.Method method 当前调用的方法
- Object[] args 方法的参数
- MethodProxy proxy 方法的代理对象(用来调用父类的方法)
- 3、生成代理类对象并且进行测试,过增强器对象Enhancer,生成代理类
- 动态代理的优缺点:
- 优点:不需要手动编写代理类、可以批量处理多个类,比如给所以方法统一添加日志,灵活性很高。
- 缺点:逻辑相比静态代理要复杂一些,理解难度也高一些。
- JDK动态代理只能代理接口及其实现类。CGLIB需要依赖第三方库,而且不能代理final类。
- 使用代理模式的场景:
- Spring AOP面向切面编程,核心就是动态代理。
- 日志框架,通过代理模式给所有的方法都自动添加日志输出
- 权限控制,每当调用一些敏感方法之前,都是通过代理验证用户的权限。
- 缓存,在调用方法之前,可以通过代理检查缓存,避免重复计算。
|