Java代理模式
Java代理模式
- 代理模式:使用代理对象代替对真实对象的访问,可以在不修改真实对象的前提下提供额外功能。
- 代理模式分类:
- 静态代理
- 动态代理
1. 静态代理
-
静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的class文件。我们对每个对象的增强都是手动完成的(对每一个目标类的增强都需要单独写一个代理类),非常不灵活。
-
实现步骤:
- 定义一个接口及实现类
- 创建一个代理类同样实现这个接口
- 将目标对象注入进代理类,然后再代理类的对应方法调用目标类中的对应方法。
-
简单来说:B是A的代理类,B中持有A的实例,并且B和A实现同一个接口。当调用B的方法时,B会在方法中调用A的方法。
-
示例:
- 定义发送短信的接口
1
2
3public interface SmsService {
String send(String message);
} - 实现接口的类
1
2
3
4
5
6
7public class SmsServiceImpl implements SmsService {
public String send(String message) {
System.out.println("send message: " + message);
return message;
}
} - 代理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class SmsServiceProxy implements SmsService {
private final SmsService smsService;
public SmsServiceProxy(SmsService smsService) {
this.smsService = smsService;
}
public String send(String message) {
// 调用方法之前添加其他操作
System.out.println("Before sending message");
String result = smsService.send(message);
// 调用方法之后添加其他操作
System.out.println("After sending message");
return result;
}
} - 使用代理类
1
2
3
4
5
6
7public class Main {
public static void main(String[] args) {
SmsService smsService = new SmsServiceImpl();
SmsService proxy = new SmsServiceProxy(smsService);
proxy.send("Hello, World!");
}
} - 输出
1
2
3Before sending message
send message: Hello, World!
After sending message
2. 动态代理
- 动态代理是在运行时动态生成类字节码,并加载到JVM中。
- Java提供了两种动态代理方式:
- JDK动态代理:只能代理实现了接口的类。
- CGLIB动态代理:可以代理没有实现接口的类。
2.1 JDK动态代理
-
JDK动态代理中的核心:
InvocationHandler接口:用于自定义代理逻辑。其定义了一个方法invoke(),用于处理代理实例上的方法调用。
1
2
3
4
5
6public interface InvocationHandler {
// proxy: 动态生成的代理对象
// method: 与代理类对象调用的方法相应
// args: 调用method传入的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}Proxy类:提供了创建动态代理实例的方法。其中使用频率最高的方法是newProxyInstance(),用于生成一个代理对象。
1
2
3
4
5
6
7
8
9
10// loader: 类加载器用于加载代理对象
// interfaces: 代理对象需要实现的接口列表
// h: 实现了InvocationHandler接口的对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
......
} -
动态代理过程:使用
Proxy.newProxyInstance()创建代理对象,该代理对象在调用方法时会调用InvocationHandler的invoke()方法,在invoke()方法中可以添加额外的逻辑(如日志、权限检查等)。 -
实现步骤:
- 定义一个接口类及其实现类
- 自定义
InvocationHandler并重写invoke方法,在invoke方法中我们会调用原生方法并自定义处理逻辑 - 通过
Proxy.newProxyInstance()方法创建代理对象
-
示例:
- 定义发送短信的接口
1
2
3public interface SmsService {
String send(String message);
} - 实现接口
1
2
3
4
5
6
7public class SmsServiceImpl implements SmsService {
public void send(String message) {
System.out.println("send message: " + message);
return message;
}
} - 自定义JDK动态代理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
public class MyInvocationHandler implements InvocationHandler {
private final Object target;
// 构造方法
public MyInvocationHandler(Object target) {
this.target = target;
}
// 重写invoke方法,自定义处理逻辑
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法调用前的处理逻辑
System.out.println("Before send message");
Object result = method.invoke(target, args);
// 方法调用后的处理逻辑
System.out.println("After send message");
return result;
}
} - 获取代理对象的工厂类
1
2
3
4
5
6
7
8
9public class ProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new MyInvocationHandler(target)
)
}
} - 使用动态代理
1
2SmsService smsService = (SmsService)ProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("Hello, World!"); // 直接调用原生方法 - 输出结果:
1
2
3Before send message
send message: Hello, World!
After send message
2.2 CGLIB动态代理
-
CGLIB(Code Generation Library)是一个功能强大的高性能代码生成库,可以在运行时动态生成被代理类的子类来实现代理。与JDK动态代理相比,CGLIB可以代理没有实现接口的类。
-
CGLIB核心:
MethodInterceptor接口:用于自定义代理逻辑。其定义了一个方法intercept(),用于拦截增强被代理类的方法。
1
2
3
4
5
6
7public interface MethodInterceptor {
// obj: 被代理的对象
// method: 被拦截的方法
// args: 方法入参
// proxy: 用于调用原始方法
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;
}Enhancer类:用于创建代理对象。通过设置父类和回调函数来生成代理对象。
-
实现步骤:
- 定义一个类
- 自定义
MethodInterceptor并重写intercept方法,intercept方法和invoke方法类似 - 通过
Enhancer类的create()创建代理类
3. 静态代理和动态代理的区别
| 对比维度 | 静态代理(Static Proxy) | 动态代理(Dynamic Proxy) |
|---|---|---|
| 代理关系确定时机 | 编译时(编译后生成固定的.class字节码文件) |
运行时 |
| 实现方式 | 对于每个目标类都需要一一实现对应的代理类 | 无需手动编写代理类,通过Handler/Intercept封装增强逻辑,一对多复用 |
| 接口依赖 | 必须实现接口 | 支持代理接口或直接代理实现类 |
| 代码量与维护性 | 代码量大,维护成本高,难以维护 | 代码量少,维护性好;与接口解耦 |
| 核心优势 | 实现简单,逻辑直观 | 灵活性强、代码复用性高 |
| 典型应用场景 | 简单的装饰器模式、少量固定类的增强需求 | SpringAOP、RPC框架、ORM框架 |
