
定义:通过一个代理对象控制对目标对象的访问,并在不修改目标类的前提下,对其功能进行增强或控制。

生活中的代理
代理模式的主要角色
比如房屋租赁: Subject:就是提前定义了房东做的事情,交给中介代理,也是中介要做的事情 RealSubject:房东 Proxy:中介
根据代理的创建时期,代理模式分为静态代理和动态代理。
.class 文件就已经存在了。静态代理:在程序运行前,代理类的 .class 文件就已经存在了。
即在出租房子之前,中介已经做好了相关的工作,就等租户来租房子了
public interface HouseSubject {
void rentHouse();
}2. 实现接口(房东出租房子)
public class RealHouseSubject implements HouseSubject{
@Override
public void rentHouse() {
System.out.println("我是房东, 我出租房子");
}
}3. 代理(中介,帮房东出租房子)
public class HouseProxy implements HouseSubject{
// 将被代理对象声明为成员变量
private HouseSubject houseSubject;
public HouseProxy(HouseSubject houseSubject) {
this.houseSubject = houseSubject;
}
@Override
public void rentHouse() {
//开始代理
System.out.println("我是中介, 开始代理");
//代理房东出租房子
houseSubject.rentHouse();
//代理结束
System.out.println("我是中介, 代理结束");
}
}4. 使用
public class StaticMain {
public static void main(String[] args) {
HouseSubject subject = new RealHouseSubject();
// 创建代理类
HouseProxy proxy = new HouseProxy(subject);
// 通过代理类访问目标方法
proxy.rentHouse();
}
}运行结果:

上面这个代理实现方式就是静态代理。
从上述程序可以看出,虽然静态代理也完成了对目标对象的代理,但是由于代码都写死了,对目标对象的每个方法的增强都是手动完成的,非常不灵活,所以日常开发几乎看不到静态代理的场景。
相比于静态代理来说,动态代理更加灵活。
我们不需要针对每个目标对象都单独创建一个代理对象,而是把这个创建代理对象的工作推迟到程序运行时由 JVM 来实现。
也就是说动态代理在程序运行时,根据需要动态创建生成。
比如房屋中介,我不需要提前预测都有哪些业务,而是业务来了我再根据情况创建。
Java 对动态代理进行了实现,并给我们提供了一些 API,常见的实现方式有两种:
JDK动态代理CGLIB动态代理实现步骤:
InvocationHandler 并重写 invoke 方法,在 invoke 方法中我们会调用目标方法(被代理类的方法)并自定义一些处理逻辑。Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象实现 InvocationHandler 接口:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class JDKInvocationHandler implements InvocationHandler {
// 目标对象即就是被代理对象(也就是房东)
private Object target;
public JDKInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 执行代理的事情
System.out.println("我是中介, 开始代理");
// 执行房东的事情
Object retVal = method.invoke(target, args);
// 执行代理的事情
System.out.println("我是中介, 代理结束");
return retVal;
}
}创建一个代理对象并使用:
public class DynamicMain {
public static void main(String[] args) {
HouseSubject target= new RealHouseSubject(); // 创建房东(得实现接口才行)
// 创建一个代理类:通过被代理类、被代理实现的接口、方法调用处理器来创建
HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
new Class[]{HouseSubject.class}, // 代理要实现的接口
new JDKInvocationHandler(target) // 调用处理器
);
proxy.rentHouse();
}
}顺序解析:
proxy.rentHouse()$Proxy...invoke()invoke() 中:method.invoke(target, args) 执行房东的真实方法注意事项:
RealHouseSubject 没有实现 HouseSubject,会报错 IllegalArgumentExceptionHouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(...);
RealHouseSubject3. 每次方法调用都会经过 invoke()
4. 性能问题
method.invoke(),有一点性能开销JDK 动态代理有一个最致命的问题是只能代理实现了接口的类。 有些场景下,我们的业务代码是直接实现的,并没有接口定义。为了解决这个问题,可以用 CGLIB 动态代理机制来解决。
CGLIB(Code Generation Library)是一个基于 ASM 的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理,很多知名的开源框架都使用到了 CGLIB。例如 Spring 中 AOP 模块:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。
注意:需要添加依赖 和 JDK 动态代理不同,CGLIB 实际是属于一个开源项目,如果你要使用它的话,需要手动添加相关依赖。 <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
实现步骤:
MethodInterceptor 并重写 intercept 方法,intercept 用于增强目标方法,和 JDK 动态代理中的 invoke 方法类似Enhancer 类的 create() 创建代理类import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 1. 目标类
class RealHouseSubject {
public void rentHouse() {
System.out.println("房东租房子");
}
}
// 2. 代理拦截器
class CGLIBInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("我是中介, 开始代理");
// 执行父类方法(目标方法)
Object retVal = methodProxy.invokeSuper(o, args);
System.out.println("我是中介, 代理结束");
return retVal;
}
}
// 3. 测试
public class DynamicMain {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealHouseSubject.class); // 继承目标类
enhancer.setCallback(new CGLIBInterceptor()); // 设置拦截器
RealHouseSubject proxy = (RealHouseSubject) enhancer.create();
proxy.rentHouse();
}
}特性 | JDK 动态代理 | CGLIB |
|---|---|---|
接口 | 必须 | 不需要 |
继承 | 无 | 通过继承目标类 |
方法限制 | 无 final 限制 | 不能代理 final 方法 |
生成代理 | Runtime Proxy Class | 子类继承生成 |
Spring 默认 | 优先 | 仅无接口或 proxy-target-class=true |
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。