目录
概述
一. 什么是代理模式
1. 举例说明
二. 代理模式作用
1. 保护代理
2. 增强功能
3. 代理交互
4. 远程代理:
三. 代理模式3个角色
四. 静态代理
1. 代码示例:
五. JDK动态代理
1. 代码示例:
六. CGLIB 动态代理
1.代码示例
七. JDK动态代理和CGLIB动态代理区别
八. 两种在spring的使用
概述
代理模式(Proxy Pattern)是设计模式中的一种结构型模式,其核心思想是通过创建一个代理对象来间接访问实际对象(也称为主题对象),从而在不改变实际对象的前提下,为实际对象添加额外的功能或控制。
代理模式根据实现方式可分:
- 接口实现:某个类必须有实现的接口,如果某个类没有实现接口,那么这个类就不能通过 JDK 产生动态代理了
- 静态代理
- 动态代理
- 继承实现:是 CGLIB 通过动态生成一个需要被代理类的子类(即被代理类作为父类),该子类重写被代理类的所有不是 final 修饰的方法,(使用 Java 反射技术创建代理类的实例)CGLIB 是一个第三方库
一. 什么是代理模式
代理模式就是给一个对象提供一个代理,并由代理对象控制对原对象的引用。它使得客户不能直接与真正的目标对象通信。代理对象是目标对象的代表,其他需要与这个目标对象打交道的操作都是和这个代理对象在交涉。
代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了的作用和保护了目标对象的,同时也在一定程度上面减少了系统的耦合度。
1. 举例说明
以卖房为例:房东 A 要卖房,客户 B 要买房,
没有代理模式情况:
- 房东 A 要卖房(可能要贴小广告卖房)
- 带买家 B 来看房
- 买家 B 非常满意,交易达成(出售成功)
使用代理模式:
- 房东 A 要卖房
- 找到代理 C ,由 C 去做卖房信息发布
- 代理 C 带有购买意向的客户看房
- 买家 B 很满意,交易达成
在这个例子中房东只需找到代理即可,其他发布房源信息和看房都由代理去做。
二. 代理模式作用
1. 保护代理
代理可以为实际对象提供一层保护,确保对象不会被不信任的调用者直接访问。
2. 增强功能
在程序中,需要给某个对象的功能进行功能增强的时候,可以考虑找一个代理进行增强,例如:Spring 框架中的 AOP 切面编程就是对代理模式的经典应用。
3. 代理交互
在程序中,A对象无法和B对象直接交互时,也可以使用代理模式来解决。
4. 远程代理:
代理可以作为远程对象的本地代表,隐藏对象位于不同地址空间的事实,使得远程调用就像本地调用一样。
三. 代理模式3个角色
- 目标对象:目标对象也就是被代理对象(对应着被代理类)
- 代理对象:代理对象对应着代理类
- 公共接口:目标对象和代理对象的公共接口(目标对象和代理对象都需要实现这个接口)。注意:在JDK中是通过接口实现代理的,但是在cglib中使用的是继承来实现动态代理的
UML图
四. 静态代理
静态代理实现相对来比较简单,静态代理是代理模式中较简单的一种模式,它在编译时就已经确定代理类和目标类的关系。
1. 代码示例:
- 创建公共接口
package com.demo.proxy; /** * 文件名:Subject * 创建者: * 创建时间:2024-09-05 * 描述:公共接口 */ public interface Subject { void info(); }
- 创建目标类
package com.demo.proxy; /** * 文件名:RealSubject * 创建者: * 创建时间:2024-09-05 * 描述: 目标类 */ public class RealSubject implements Subject{ @Override public void info() { System.out.println("被代理对象方法执行。。。"); } }
- 创建代理类
package com.demo.proxy; /** * 文件名:ProxySubject * 创建者: * 创建时间:2024-09-05 * 描述: 代理类 */ public class ProxySubject implements Subject{ private Subject subject; public ProxySubject(Subject subject) { this.subject = subject; } @Override public void info() { perReqs(); subject.info(); postReqs(); } private void perReqs(){ System.out.println("记录请求日志"); } private void postReqs(){ System.out.println("记录响应日志"); } }
- 测试类
package com.demo.proxy; /** * 文件名:Main * 创建者: * 创建时间:2024-09-05 * 描述: 测试类 */ public class Main { public static void main(String[] args) { //1.创建目标对象 Subject subject = new RealSubject(); //2.创建代理对象(将目标对象传入代理对象构造中) Subject sub = new ProxySubject(subject); sub.info(); } }
- 测试结果
五. JDK动态代理
java.lang.reflect 包中的Proxy类和InvocationHandler接口提供了生成动态代理类的能力。
代理类在程序运行时利用反射机制动态创建而成,主要分为jdk动态代理和cglib动态代理。
JDK 动态代理类的字节码在程序运行时由 Java 反射机制动态生成,无需手工编写它的源代码。
动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为 Java 反射机制可以生成任意类型的动态代理类。
1. 代码示例:
参数说明:
Proxy类的静态方法 newProxyInstance() 参数:
- loader:类加载器,使用本类的类加载器即可,或者 target 对象的类加载器也行。
- interfaces:接口数组,表示被代理类实现的接口,因可能会实现多个接口,所以这里是数组的形式。
- InvocationHandler:核心,在这里创建一个内部实现类实现InvocationHandler接口,重写invoke方法
invoke 里的参数 proxy 是代理类的实例,method是代理类的实例执行的方法,args则是执行的方法里面的参数,
我们可以在这里对执行核心业务逻辑前后增加代码。method.invoke(target, args)这里是用了反射的原理让target对象去执行method方法。
- 定义接口
package com.demo.proxy.dynamic; /** * 文件名:UserService * 创建者: * 创建时间:2024-09-05 * 描述:业务服务接口 */ public interface IUserService { boolean login(String name,String pwd); void signOut(String name); }
- 定义接口实现类(也就是业务实现类)
package com.demo.proxy.dynamic; /** * 文件名:UserService * 创建者: * 创建时间:2024-09-05 * 描述:业务实现类 */ public class UserService implements IUserService{ /** * 登录 * @param name * @param pwd * @return */ @Override public boolean login(String name,String pwd) { System.out.println(name+":登录成功"); return true; } /** * 退出 * @param name */ @Override public void signOut(String name) { System.out.println(name +": 退出成功"); } }
- 定义代理类(无需实现接口)
package com.demo.proxy.dynamic; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 文件名:DefineProxy * 创建者: * 创建时间:2024-09-05 * 描述:动态代理类 */ public class DefineProxy { private Object target; public DefineProxy(Object target){ this.target = target; } public Object createProxy(){ return Proxy.newProxyInstance(DefineProxy.class.getClassLoader(), this.target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { perName(method.getName()); Object invoke = method.invoke(target, args); postName(method.getName()); return invoke; } }); } /** * 前置方法 * @param name */ private void perName(String name){ System.out.println(name+"方法-前置执行"); } /** * 后置方法 * @param name */ private void postName(String name){ System.out.println(name+"方法-后置执行"); } }
- 定义测试类
package com.demo.proxy.dynamic; /** * 文件名:Main * 创建者: * 创建时间:2024-09-05 * 描述:测试类 */ public class Main { public static void main(String[] args) { //1.创建目标对象 UserService userService = new UserService(); //2.创建代理对象 DefineProxy defineProxy = new DefineProxy(userService); //3.调用生成代理方法 IUserService proxy = (IUserService) defineProxy.createProxy(); //4.代理执行方法 boolean flg = proxy.login("小明","12345"); System.out.println("返回值:"+flg); System.out.println("============================="); proxy.signOut("小明"); } }
- 测试结果截图
- 修改动态代理类(通过实现 InvocationHandler 接口来实现代理 )
package com.demo.proxy.dynamic; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 文件名:DefineProxyTwo * 创建者:宁贝贝 * 创建时间:2024-09-05 * 描述:动态代理类,通过实现接口 */ public class DefineProxyTwo implements InvocationHandler { private Object target; public DefineProxyTwo(Object target){ this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { perName(method.getName()); Object res = method.invoke(target,args); postName(method.getName()); return res; } /** * 前置方法 * @param name */ private void perName(String name){ System.out.println(name+"方法-前置执行"); } /** * 后置方法 * @param name */ private void postName(String name){ System.out.println(name+"方法-后置执行"); } }
- 修改测试类
package com.demo.proxy.dynamic; import java.lang.reflect.Proxy; /** * 文件名:MainTwo * 创建者: * 创建时间:2024-09-05 * 描述:测试类 */ public class MainTwo { public static void main(String[] args) { //1.创建被代理对象 UserService userService = new UserService(); //2.获取被代理对象的类加载器,用作生成代理对象的必要参数 ClassLoader classLoader = userService.getClass().getClassLoader(); //3.获取被代理对象的实现接口, // 用作生成代理对象的必要参数 // 方法映射就是基于这个参数实现的 Class<?>[] interfaces = userService.getClass().getInterfaces(); //4.获取被代理对象的增强主题类,用作生成代理对象的必要参数 DefineProxyTwo defineProxyTwo = new DefineProxyTwo(userService); //5.生成代理对象的核心代码 IUserService proxyInstance = (IUserService) Proxy.newProxyInstance(classLoader, interfaces, defineProxyTwo); // 使用代理对象执行方法 proxyInstance.login("星星","765432"); System.out.println("=========================="); proxyInstance.signOut("星星"); } }
六. CGLIB 动态代理
JDK 中提供的生成动态代理类的机制有个鲜明的特点是:某个类必须有实现的接口,如果某个类没有实现接口,那么这个类就不能通过 JDK 产生动态代理了。
CGLIB(Code Generation Library)是一个强大的、高性能、高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
CGLIB 通过动态生成一个需要被代理类的子类(即被代理类作为父类),该子类重写被代理类的所有不是 final 修饰的方法,并在子类中采用方法拦截的技术拦截父类所有的方法调用,进而织入横切逻辑。此外,因为 CGLIB 采用整型变量建立了方法索引,这比使用 JDK 动态代理更快(使用 Java 反射技术创建代理类的实例)。
CGLib 创建某个类 A 的动态代理类的模式是:
- 查找 A 上的所有非 final 的 public 类型的方法定义;
- 将这些方法的定义转换成字节码;
- 将组成的字节码转换成相应的代理的 class 对象;
- 实现 MethodInterceptor 接口,用来处理 对代理类上所有方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的)
1.代码示例
- 定义普通业务类
package com.demo.proxy.cglib; /** * 文件名:UserService * 创建者: * 创建时间:2024-09-05 * 描述:一个普通的业务类 */ public class UserService{ /** * 登录 * @param name * @param pwd * @return */ public boolean login(String name,String pwd) { System.out.println(name+":登录成功"); return true; } /** * 退出 * @param name */ public void signOut(String name) { System.out.println(name +": 退出成功"); } }
- 定义CGLIB动态代理类
package com.demo.proxy.cglib; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * 文件名:CGLibProxy * 创建者: * 创建时间:2024-09-05 * 描述:cglib动态代理类 */ public class CGLibProxy implements MethodInterceptor { private Object target; public CGLibProxy(Object target) { this.target = target; } public Object createProxy(){ //cglib中的增强器,用来创建动态代理 Enhancer enhancer = new Enhancer(); //设置要创建动态代理的类 enhancer.setSuperclass(target.getClass()); //设置回调,这里相当于是对于代理类上所有方法的调用,都会调用callback,而callback则需要实现intercept()方法进行拦截。 enhancer.setCallback(this); //创建代理类 return enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("cglib 代理开始..."); Object object = proxy.invokeSuper(obj,args); System.out.println("cglib 代理结束..."); return object; } }
- 定义测试类
package com.demo.proxy.cglib; /** * 文件名:Main * 创建者: * 创建时间:2024-09-05 * 描述:测试类 */ public class Main { public static void main(String[] args) { //1.创建被代理对象 UserService userService = new UserService(); //2.创建CGLibProxy对象 CGLibProxy cgLibProxy = new CGLibProxy(userService); //3.调用createProxy创建代理对象 UserService proxy = (UserService)cgLibProxy.createProxy(); //4.代理执行方法 boolean res = proxy.login("晓月","5676543"); System.out.println("登录结果:"+res); System.out.println("--------------------------------"); proxy.signOut("晓月"); System.out.println("================================="); //其实上面那几个步骤可以一行代码全搞定 UserService us = (UserService)new CGLibProxy(new UserService()).createProxy(); boolean result = us.login("小红","13123123"); System.out.println("登录结果:"+result); System.out.println("--------------------------------"); us.signOut("小红"); } }
- 测试结果
七. JDK动态代理和CGLIB动态代理区别
- 实现原理:JDK动态代理是基于Java反射机制实现的,它要求目标类必须实现一个或多个接口,代理对象在运行时动态创建,通过实现目标类接口的方式来代理目标类。 CGLIB代理则是基于ASM字节码框架实现的,它可以代理没有实现接口的目标类。CGLIB在运行时通过动态生成目标类的子类来实现代理。
- 性能表现:JDK动态代理因为需要实现目标类接口,所以它的性能相对较低,但是它的应用场景更为广泛,适用于大多数情况下的代理需求。 CGLIB代理则因为不需要实现目标类接口,所以它的性能相对较高,但是它不能代理final类和final方法,以及一些无法生成子类的类。
- 应用场景:JDK动态代理适用于代理接口的场景,例如Spring中的事务处理、日志记录等。 CGLIB代理适用于代理类的场景,例如Spring中的AOP切面编程等。
八. 两种在spring的使用
如果被代理对象实现了需要被代理的接口,则使用 JDK 的动态代理,否则便使用 CGLIB 代理,在spring中它会自动根据使用情况进行切换,但是如果你缺少CGLIB依赖肯定是会报错。需要添加 cglib 依赖包。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>