GoF之代理模式
一、什么是代理模式,究竟有什么用?
一句话说明
代理模式 = “用一个替身对象帮本体完成一件事,调用者只跟替身打交道;替身可以在不改动本体的情况下,附加额外功能(权限、缓存、事务、日志…)。”
举一个例子
你(客户端)想找明星(真实对象)拍电影,实际上先跟经纪人谈档期、谈钱;经纪人可以“拦截”一些不合理请求(权限控制),也可以帮明星安排行程(额外功能),但拍戏还是明星本人去拍。
为什么要有代理模式,解决什么问题?
当我需要对目标对象业务进行扩展的时候,但是又不想改变目标对象的代码。这时候就可以借助代理类去帮我对目标对象进行扩展,符合OCP原则。
代理模式的核心价值是 “在不侵入目标对象的前提下,对其访问进行控制或功能增强”。借助动态代理或 AOP,可在运行时动态织入新行为,从而更符合开闭原则。
代理模式是怎样的?
代理模式中的角色:
- 代理类
- 目标类
- 公共的接口:客户端在使用代理类时就像在使用目标类,不被客户端所察觉,所以代理类和目标类要有共同的行为,也就是实现共同的接口。
代理模式实现的方法
二、静态代理
上面说了,一个代理模式,需要三个重要的角色,代理类、目标类和公共接口
现在我们定义这先定义这三个重要的角色:
2.1 OrderService
package com.wangxin.proxy.service;
/**
* @program: Spring_Project
* @description: 订单的功能接口
* @author: WangXin
* @create: 2025-08-12 11:37
**/
public interface OrderService {
/**
* 生成订单
*/
void generate();
/**
* 查看订单详情
*/
void detail();
/**
* 修改订单
*/
void modify();
}
2.2 OrderProxyStatic
package com.wangxin.proxy.stat;
import com.wangxin.proxy.service.OrderService;
/**
* @program: Spring_Project
* @description: 静态代理模式
* @author: WangXin
* @create: 2025-08-12 11:40
**/
public class OrderProxyStatic implements OrderService {
private OrderService OderService;
public OrderProxyStatic(OrderService oderService) {
OderService = oderService;
}
@Override
public void generate() {
long start = System.currentTimeMillis();
OderService.generate();
long end = System.currentTimeMillis();
System.out.println(end – start);
}
@Override
public void detail() {
long start = System.currentTimeMillis();
OderService.detail();
long end = System.currentTimeMillis();
System.out.println(end – start);
}
@Override
public void modify() {
long start = System.currentTimeMillis();
OderService.modify();
long end = System.currentTimeMillis();
System.out.println(end – start);
}
}
2.3 OrderServiceImpl
package com.wangxin.proxy.service.impl;
import com.wangxin.proxy.service.OrderService;
/**
* @program: Spring_Project
* @description: 具体实现类
* @author: WangXin
* @create: 2025-08-12 11:42
**/
public class OrderServiceImpl implements OrderService {
@Override
public void generate() {
try {
Thread.sleep(1234);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单已生成");
}
@Override
public void detail() {
try {
Thread.sleep(2541);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单信息如下:******");
}
@Override
public void modify() {
try {
Thread.sleep(1010);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("订单已修改");
}
}
2.4 主函数调用代理方法
OrderService orderService = new OrderServiceImpl();
OrderProxyStatic proxyStatic = new OrderProxyStatic(orderService);
proxyStatic.detail();
2.5 静态代理的缺陷
完成上面静态代理的代码,我们会发现我们扩展的功能是一样的,但是每个方法都要去重复这个扩展功能的书写,这样如果有100个方法,岂不是要写100扩展功能?而且一个扩展方法就表示一个代理类,需要扩展100个不同的方法岂不是需要100代理。因此,为解决这个问题,我们就出现动态代理
三、动态代理
在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。
在内存当中动态生成类的技术常见的包括:
- JDK动态代理技术:只能代理接口。
- CGLIB动态代理技术:CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM。)
- Javassist动态代理技术:Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。
动态代理的原理和静态代理原理是一样的,只是动态代理是在内存中动态生成代理类,从而减少代理类的数量
3.1jdk动态代理
也是需要三个角色,只是代理类在内存中动态生成,其他两个角色和上面静态代理一样
多了一个方法功能增强的类。基于反射
3.1.2 主函数
//创建目标对象
OrderService target = new OrderServiceImpl();
//创建代理对象
OrderService orderServiceProxy = (OrderService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new TimerInvocationHandler(target));
//调用目标对象的增强后的方法
orderServiceProxy.detail();
orderServiceProxy.modify();
orderServiceProxy.generate();
3.1.3 new TimerInvocationHandler(target) 增强方法
package com.wangxin.proxy.dynamiic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @program: Spring_Project
* @description: 计算运行时间的增强方法
* @author: WangXin
* @create: 2025-08-12 12:09
**/
public class TimerInvocationHandler implements InvocationHandler {
private Object target;
public TimerInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("耗时"+(end – start)+"毫秒");
//表明如果有返回值,需要返回
return result;
}
}
InvocationHandler接口中有一个方法invoke,这个invoke方法上有三个参数:
- 第一个参数:Object proxy。代理对象。设计这个参数只是为了后期的方便,如果想在invoke方法中使用代理对象的话,尽管通过这个参数来使用。
- 第二个参数:Method method。目标方法。
- 第三个参数:Object[] args。目标方法调用时要传的参数。
3.2 CGLIB动态代理
CGLIB既可以代理接口,又可以代理类。底层采用继承的方式实现。所以被代理的目标类不能使用final修饰。
为什么所代理的目标类不能使用final修饰?
因为底层是用继承的方式实现,final修饰的类不能被继承和重写
使用CGLIB,需要引入它的依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
同样和静态代理一样三个角色,多了一个方法功能增强的类。基于反射
主函数
//创建字节码增强器
Enhancer enhancer = new Enhancer();
//指定继承哪个类
enhancer.setSuperclass(OrderServiceImpl.class);
//设置回调接口
enhancer.setCallback(new TimerMethodInterceptor());
//生成源码,并编译成class,加载到JVM中,最后生成代理类
OrderService orderServiceProxy = (OrderService) enhancer.create();
//调用目标对象的增强后的方法
orderServiceProxy.detail();
orderServiceProxy.modify();
orderServiceProxy.generate();
TimerMethodInterceptor 增强方法
和JDK动态代理原理差不多,在CGLIB中需要提供的不是InvocationHandler,而是:net.sf.cglib.proxy.MethodInterceptor
package com.wangxin.proxy.dynamiic;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @program: Spring_Project
* @description: CGLIB动态代理的实现
* @author: WangXin
* @create: 2025-08-12 12:20
**/
public class TimerMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long start = System.currentTimeMillis();
Object result = methodProxy.invokeSuper(o, objects);
long end = System.currentTimeMillis();
System.out.println("耗时"+(end – start)+"毫秒");
return result;
}
}
MethodInterceptor接口中有一个方法intercept(),该方法有4个参数:
第一个参数:目标对象
第二个参数:目标方法
第三个参数:目标方法调用时的实参
第四个参数:代理方法
3.3 JDK和CGLIB动态代理之间的区别
技术基础 | Java 反射 (java.lang.reflect.Proxy) | 字节码框架 ASM |
代理目标 | 接口(必须) | 类(也可以接口) |
实现方式 | 运行时生成一个 实现接口 的新类 | 运行时生成一个 继承目标类 的子类 |
对 final 的要求 | 无 | 目标类/方法 不能是 final |
构造限制 | 必须提供接口 | 类必须有 无参构造器(或可用 Objenesis) |
性能 | 首次生成快、调用稍慢(反射) | 首次生成慢、调用更快(方法索引) |
依赖 | JDK 自带 | 需额外依赖 cglib(Spring 已内置) |
Spring AOP 规则 | 有接口 → 默认 JDK | 无接口或强制 proxyTargetClass=true → CGLIB |
代理示例 | Foo proxy = (Foo) Proxy.newProxyInstance(…) | Enhancer.create(UserService.class, callback) |
评论前必须登录!
注册