手撕设计模式之房产中介——代理模式
1.业务需求
大家好,我是菠菜啊,好久不见,今天给大家带来的是——代理模式。老规矩,在介绍这期内容前,我们先来看看这样的需求:我们有一套房产需要出售,但是我们抽不开身去带客户看房对接而且获客方式有限,我们该怎么实现?
2.代码实现
Talk is cheap,show me your code.
初版实现思路:
既然自己没时间、没客源,可以找个中间人不就行了,这个中间人就是房产代理。
初版代码如下:
//房屋售卖接口
public interface HouseSaleService {
void saleHouse();
}
//房东类
public class HouseOwner implements HouseSaleService {
@Override
public void saleHouse() {
System.out.println("我是房东,我签订售房合同");
System.out.println("我是房东,售出房源");
}
}
//房产中介类
public class ProxyHouse implements HouseSaleService{
private HouseSaleService houseSaleService;
public ProxyHouse(HouseSaleService houseSaleService) {
this.houseSaleService = houseSaleService;
}
@Override
public 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)和参数数组
@Override
public 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 {
@Override
public 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 ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
@Override
public 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;
// 其他方法...
}
}
技术需要沉淀,同样生活也是~
个人链接:博客,欢迎一起交流