代理模式 代理模式(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面向切面编程,核心就是动态代理。 日志框架,通过代理模式给所有的方法都自动添加日志输出 权限控制,每当调用一些敏感方法之前,都是通过代理验证用户的权限。 缓存,在调用方法之前,可以通过代理检查缓存,避免重复计算。