设计模式之房产中介——代理模式
手撕设计模式之房产中介——代理模式
1.业务需求
大家好,我是菠菜啊,好久不见,今天给大家带来的是——代理模式。老规矩,在介绍这期内容前,我们先来看看这样的需求:我们有一套房产需要出售,但是我们抽不开身去带客户看房对接而且获客方式有限,我们该怎么实现?
2.代码实现
Talk is cheap,show me your code.
初版实现思路:
既然自己没时间、没客源,可以找个中间人不就行了,这个中间人就是房产代理。
初版代码如下:
//房屋售卖接口
public interface HouseSaleService {void saleHouse();
}
//房东类
public class HouseOwner implements HouseSaleService {@Overridepublic void saleHouse() {System.out.println("我是房东,我签订售房合同");System.out.println("我是房东,售出房源");}
}
//房产中介类
public class ProxyHouse implements HouseSaleService{private HouseSaleService houseSaleService;public ProxyHouse(HouseSaleService houseSaleService) {this.houseSaleService = houseSaleService;}@Overridepublic void saleHouse() {System.out.println("房产中介——开始带客户看房");houseSaleService.saleHouse();System.out.println("房产中介——房源售出,结束");}
}
public class Client {public static void main(String[] args) {HouseSaleService houseSaleService = new HouseOwner();HouseSaleService proxyHouse = new ProxyHouse(houseSaleService);proxyHouse.saleHouse();}
}
输出结果:
实现代码结构图:
这个实现过程其实就是代理设计模式,属于代理模式中的静态代理。
3.需求升级
我们现在不是房东了,我们现在是一个中介平台,平台上有千千万万个房东和中介,如果还是实现上述代理买房的需求,交易的流程是一样的,该怎么做呢?不会添加那么多房产代理人吧。
//售房调用处理器类
public class HouseSaleInvocationHandler implements InvocationHandler {//房东private Object target;public HouseSaleInvocationHandler(Object target){this.target = target;}//三个参数:代理对象实例、方法对象(Method)和参数数组@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("动态生成房产中介代理——开始带客户看房");Object result = method.invoke(target, args);System.out.println("动态生成房产中介代理——房源售出,结束");return result;}//创建动态代理对象public static Object newProxyInstance(Object target){//传入目标对象的类加载器,目标对象实现的接口,调用处理器return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new HouseSaleInvocationHandler(target));}
}
//客户端(动态代理)
public class Client2 {public static void main(String[] args) {HouseSaleService houseSaleService = (HouseSaleService)HouseSaleInvocationHandler.newProxyInstance(new HouseOwner());houseSaleService.saleHouse();}
}
执行结果:
上述代码可以实现一套代理流程动态为多个房东生成房产代理,无需手动一个个手动创建。有没有同学觉得很熟悉,看过jdk源码的同学应该很熟悉(详细实现过程见下方源码剖析部分),它是利用java.lang.reflect包下的Proxy和InvocationHandler核心类实现动态代理机制的,又称JDK的动态代理。实现原理:在运行代码时利用反射动态生成代理类,将目标方法的调用都转发到InvocationHandler的invoke方法,利用反射机制执行目标方法并插入增强逻辑。
核心流程如下:
思考:
上述代码只能解决实现过售卖接口的房东需求,那些没通过实现售卖接口的个人房东好像满足不了他们的需求,所以又有另一种实现方式。
个人房东代码改造:
pom依赖:
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
//cglib代理类
public class CglibProxyHouse {//获取代理对象public static Object getInstance(Object target) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());// 设置目标类enhancer.setCallback(new CglibMethodInterceptor());// 设置方法拦截器return enhancer.create();}}
//cglib方法拦截增强类
public class CglibMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("动态生成房产中介代理———开始带客户看房-CglibProxyHouse");Object result=methodProxy.invokeSuper(proxy, args);// 调用父类(目标类)的方法System.out.println("动态生成房产中介代理———房源售出,结束-CglibProxyHouse");return result;}
}
//个人房东
public class OtherHouseOwner {public void saleHouse() {System.out.println("我是个人房东,我签订售房合同");System.out.println("我是个人房东,售出房源");}
}
//Cglib动态代理测试类
public class Client3 {public static void main(String[] args) {OtherHouseOwner houseOwner = (OtherHouseOwner) CglibProxyHouse.getInstance(new OtherHouseOwner());houseOwner.saleHouse();}
}
运行结果:
上述代码实现了个人房东动态代理卖房需求,个人房东无需实现响应接口。它利用net.sf.cglib.proxy包下的Enhancer和MethodInterceptor核心类实现动态代理机制的,又称CGLIB动态代理。CGLIB通过继承目标类并重写非final方法,在运行时使用ASM字节码技术动态生成代理子类,将方法调用委托给MethodInterceptor实现增强逻辑,并借助FastClass机制通过方法索引直接调用提升性能。
动态代理对比:
特性 | JDK动态代理 | CGLIB |
---|---|---|
代理方式 | 实现接口 | 继承目标类 |
性能 | 反射调用,较慢 | FastClass调用,较快 |
依赖 | 无第三方库 | 需CGLIB库 |
目标类要求 | 必须实现接口 | 不能是final类/方法 |
初始化开销 | 小 | 大(生成两个FastClass) |
方法调用模式 | 通过InvocationHandler | 通过MethodInterceptor |
4.定义
代理设计模式是一种结构型设计模式,其核心思想是提供一个代理对象来控制对原始对象的访问。代理充当原始对象的中间层,允许在访问原始对象前后添加额外逻辑,而无需修改原始对象本身。
组成部分如下:
-
代理对象(Proxy)
- 实现与原始对象相同的接口
- 持有对原始对象的引用
- 控制客户端对原始对象的访问
-
原始对象(Real Subject)
- 实际执行业务逻辑的目标对象
-
抽象接口(Subject)
-
定义代理和原始对象的共同接口,确保二者可互换使用
-
5.应用场景
场景类型 | 典型用途 | 实例 |
---|---|---|
虚拟代理 | 延迟创建开销大的对象 | 图片懒加载:代理先显示占位图,真正需要时才加载高清图片 |
远程代理 | 隐藏远程调用的复杂性 | RPC框架:代理封装网络通信细节,客户端像调用本地对象一样调用远程服务 |
保护代理 | 控制访问权限 | 权限校验:代理验证用户权限后再允许访问敏感操作 |
缓存代理 | 缓存昂贵操作的结果 | API请求缓存:代理缓存计算结果,重复请求直接返回结果,避免重复计算 |
日志代理 | 记录访问日志 | 审计系统:代理在方法调用前后记录日志和时间戳 |
智能引用代理 | 管理对象生命周期 | 自动释放资源:代理统计对象引用计数,引用归零时自动销毁对象 |
适用性总结
✅ 需要隔离客户端与复杂系统(如远程服务)
✅ 需要延迟初始化高开销对象
✅ 需添加访问控制或安全层
✅ 需透明添加日志、监控等横切关注点
✅ 需实现智能引用(如对象池、缓存)
典型场景:
Spring AOP 的底层机制完全基于代理模式实现,通过动态代理在运行时生成代理对象,将切面逻辑(如日志、事务等)织入目标方法中。其具体实现分为两种机制:JDK 动态代理和CGLIB 代理,由 Spring 根据目标类的特性自动选择或通过配置强制指定。目标类实现了接口,使用JDK 动态代理;目标类未实现接口,CGLIB 代理,也可强制指定。
源码剖析
1.JDK动态代理
1.1 Proxy.newProxyInstance() 入口方法
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) {// 1. 验证接口和处理器有效性Objects.requireNonNull(h);final Class<?>[] intfs = interfaces.clone();// 2. 获取或生成代理类Class<?> cl = getProxyClass0(loader, intfs);try {// 3. 获取代理类构造器final Constructor<?> cons = cl.getConstructor(constructorParams);// 4. 创建代理实例return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {// 异常处理...}
}
1.2 代理类生成机制(getProxyClass0)
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {// 1. 接口数量限制(最多65535个)if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}// 2. 从缓存获取或生成代理类return proxyClassCache.get(loader, interfaces);
}
代理类缓存使用WeakCache实现,核心逻辑在ProxyClassFactory中:
private static final class ProxyClassFactoryimplements BiFunction<ClassLoader, Class<?>[], Class<?>>
{@Overridepublic Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {// 1. 生成唯一代理类名long num = nextUniqueNumber.getAndIncrement();String proxyName = proxyPkg + proxyClassNamePrefix + num;// 2. 生成代理类字节码byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);// 3. 定义代理类return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);}
}
1.3 代理类字节码生成(ProxyGenerator)
generateProxyClass
方法生成代理类的字节码,其核心逻辑如下:
public static byte[] generateProxyClass(final String name,Class<?>[] interfaces,int accessFlags) {ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);// 真正生成字节码final byte[] classFile = gen.generateClassFile();// 可选项:保存生成的字节码到文件(调试用)if (saveGeneratedFiles) {java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<Void>() {public Void run() {try {FileOutputStream file = new FileOutputStream(dotToSlash(name) + ".class");file.write(classFile);file.close();} catch (IOException e) { /* 忽略 */ }return null;}});}return classFile;
}
1.4 生成的代理类结构(反编译示例)
假设我们代理HouseSaleService接口,生成的$Proxy0.class反编译后:
public final class $Proxy0 extends Proxy implements HouseSaleService {private static Method m1; // hashCode()private static Method m2; // equals()private static Method m3; // toString()private static Method m4; // saleHouse()static {try {m1 = Class.forName("java.lang.Object").getMethod("hashCode");m2 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m3 = Class.forName("java.lang.Object").getMethod("toString");m4 = Class.forName("com.example.HouseSaleService").getMethod("saleHouse");} catch (NoSuchMethodException e) { /* 处理异常 */ }}public $Proxy0(InvocationHandler h) {super(h); // 调用Proxy的构造函数}public final void saleHouse() {try {// 关键:调用InvocationHandler的invoke方法super.h.invoke(this, m4, null);} catch (RuntimeException | Error e) {throw e;} catch (Throwable e) {throw new UndeclaredThrowableException(e);}}// 其他Object方法实现类似...
}
代理对象实现目标接口,方法调用里其实还是调用InvocationHandler的invoke方法。
2.CGLIB动态代理
2.1Enhancer 入口类
创建代理对象通常使用以下代码:
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetClass.class); // 设置目标类
enhancer.setCallback(new MyMethodInterceptor()); // 设置回调(拦截器)
TargetClass proxy = (TargetClass) enhancer.create(); // 生成代理对象
2.2 enhancer.create() 方法调用链
// Enhancer.java
public Object create() {// 关键:不使用参数return createHelper();
}private Object createHelper() {// 1. 验证回调类型preValidate();// 2. 生成代理类Class<?> proxyClass = createClass();// 3. 创建代理实例return createUsingReflection(proxyClass);
}
2.3 代理类生成(createClass)
// AbstractClassGenerator.java
protected Class<?> createClass() {// 使用字节码生成器生成字节码byte[] b = strategy.generate(this);// 定义类return ReflectUtils.defineClass(getClassName(), b, loader);
}
2.4 字节码生成核心:Enhancer.generateClass
CGLIB通过ASM库直接操作字节码,关键逻辑在Enhancer.generateClass
方法中:
// Enhancer.java
public void generateClass(ClassVisitor v) {// 1. 创建类结构ClassEmitter ce = new ClassEmitter(v);ce.begin_class(/* 版本号 */, ACC_PUBLIC, getClassName(), getSuperclass(), getInterfaces(), "<generated>");// 2. 添加字段ce.declare_field(ACC_PRIVATE, BOUND_FIELD, Type.BOOLEAN_TYPE, null);ce.declare_field(ACC_PRIVATE, THREAD_CALLBACKS_FIELD, THREAD_LOCAL, null);ce.declare_field(ACC_PRIVATE, STATIC_CALLBACKS_FIELD, CALLBACK_ARRAY, null);// 3. 生成构造函数generateConstructor(ce);// 4. 生成回调设置方法generateSetCallbacks(ce);generateSetCallback(ce);generateGetCallback(ce);// 5. 重写目标方法for (Method method : getMethods()) {generateMethod(ce, method);}ce.end_class();
}
2.5 方法生成逻辑(generateMethod)
// Enhancer.java
private void generateMethod(ClassEmitter ce, Method method) {// 1. 创建方法签名Signature sig = ReflectUtils.getSignature(method);// 2. 创建方法体CodeEmitter e = ce.begin_method(ACC_PUBLIC, sig, null);// 3. 获取回调e.load_this();e.getfield(CALLBACK_FIELD);// 4. 检查回调是否存在Label noCallback = e.make_label();e.dup();e.ifnull(noCallback);// 5. 准备调用参数e.push(method);e.create_arg_array();e.push(methodProxy);// 6. 调用拦截器e.invoke_interface(METHOD_INTERCEPTOR_TYPE, new Signature("intercept", Type.OBJECT, new Type[]{TYPE_OBJECT, TYPE_METHOD, TYPE_OBJECT_ARRAY, TYPE_METHOD_PROXY}));// 7. 结果处理e.unbox_or_zero(e.getReturnType());e.return_value();// 8. 没有回调时的处理e.mark(noCallback);e.pop();// 调用原始方法super.generateMethod(ce, method);e.return_value();e.end_method();
}
2.6 生成的代理类结构(反编译示例)
使用CGLIB代理后生成的代理类大致如下:
public class OtherHouseOwner$$EnhancerByCGLIB$$12345678 extends OtherHouseOwner implements Factory {private MethodInterceptor interceptor;private static final Method CGLIB$saleHouse$0$Method;private static final MethodProxy CGLIB$saleHouse$0$Proxy;static {// 初始化目标方法和代理方法CGLIB$saleHouse$0$Method = ReflectUtils.findMethods(new String[]{"saleHouse", "()V"}).get(0);CGLIB$saleHouse$0$Proxy = MethodProxy.create(OtherHouseOwner.class, OtherHouseOwner$$EnhancerByCGLIB$$12345678.class, "()V", "saleHouse", "CGLIB$saleHouse$0");}// 重写目标方法public final void saleHouse() {MethodInterceptor tmp = this.interceptor;if (tmp == null) {super.saleHouse(); // 如果未设置拦截器,直接调用父类方法} else {// 调用拦截器的intercept方法tmp.intercept(this, CGLIB$saleHouse$0$Method, new Object[0], CGLIB$saleHouse$0$Proxy);}}// 原始方法的直接调用(避免拦截)final void CGLIB$saleHouse$0() {super.saleHouse();}// 其他方法...
}
如果未设置拦截器,直接调用父类方法;如果有设置方法拦截器,直接回调拦截器方法。
2.7 MethodProxy 工作原理
MethodProxy
是CGLIB高效调用的关键,它通过生成两个FastClass(目标类FastClass和代理类FastClass)实现快速方法调用。
// MethodProxy.java
public Object invoke(Object obj, Object[] args) throws Throwable {try {// 使用目标类的FastClass直接调用原始方法return fastClass.invoke(targetIndex, obj, args);} catch (InvocationTargetException e) {throw e.getTargetException();}
}public Object invokeSuper(Object obj, Object[] args) throws Throwable {try {// 使用代理类的FastClass调用代理类中的原始方法(即CGLIB$xxx方法)return fastClass.invoke(superIndex, obj, args);} catch (InvocationTargetException e) {throw e.getTargetException();}
}
2.8 FastClass 机制
FastClass为每个方法分配一个索引(index),通过索引直接调用方法,避免反射开销。
// 生成的TargetFastClass
public Object invoke(int index, Object obj, Object[] args) {OtherHouseOwner otherHouseOwner = (OtherHouseOwner) obj;switch (index) {case 0:otherHouseOwner.saleHouse();return null;// 其他方法...}
}// 生成的ProxyFastClass
public Object invoke(int index, Object obj, Object[] args) {OtherHouseOwner$$EnhancerByCGLIB proxy = (OtherHouseOwner$$EnhancerByCGLIB) obj;switch (index) {case 0:proxy.CGLIB$saleHouse$0();return null;// 其他方法...}
}
法,避免反射开销。
// 生成的TargetFastClass
public Object invoke(int index, Object obj, Object[] args) {OtherHouseOwner otherHouseOwner = (OtherHouseOwner) obj;switch (index) {case 0:otherHouseOwner.saleHouse();return null;// 其他方法...}
}// 生成的ProxyFastClass
public Object invoke(int index, Object obj, Object[] args) {OtherHouseOwner$$EnhancerByCGLIB proxy = (OtherHouseOwner$$EnhancerByCGLIB) obj;switch (index) {case 0:proxy.CGLIB$saleHouse$0();return null;// 其他方法...}
}
技术需要沉淀,同样生活也是~
个人链接:博客,欢迎一起交流