1.代理模式概念
**代理模式(Proxy Pattern)**是一种结构型设计模式,属于GOF23种设计模式之一。它通过某种代理来控制对目标对象的访问,扩展目标对象的功能,甚至可以替换目标对象。它的应用相当广泛,许多框架都使用了代理模式来实现各种中间件功能。
代理模式使用场景:
- 远程代理:为一个对象在不同的地址空间提供局部代表,此代表会将客户端的请求转发给真实的对象,并返回真实对象的响应数据。这是代理模式的典型应用,例如Dubbo RPC框架。
- 缓存代理:为一个对象提供缓存,以缓解目标对象的压力。例如Ehcache框架。
- 懒加载:为一个对象提供懒加载,只在真正需要对象时创建它。例如Hibernate的sessionFactory。
- 日志记录:在访问一个对象时记录日志或其他与之相关的事件。例如Spring AOP。
- 智能代理:为一个对象提供额外的行为,如统计功能或异常处理。例如Dubbo框架对服务提供者进行统计和监控。
总结来说,代理模式常用于以下目的:
- 控制目标对象的访问
- 延迟目标对象的创建(懒加载)
- 为目标对象提供中间件服务(远程调用、缓存、日志等)
- 对目标对象进行装饰以提供附加功能(监控统计、安全校验等)
2.代理模式实现方式
2.1 静态代理
静态代理:代理类和目标对象的类都是在编译期确定的。代理类包含目标对象的实例,并在同一时刻只能为一个目标对象提供服务。实现简单,但是会产生很多代理类。
图中的 Hello是程序中的业务逻辑接口,HelloImpl是实现了 Hello接口的真正业务类,Proxy 是实现了 Hello接口的代理类,封装了一个 HelloImpl引用。在程序中不会直接调用 HelloImpl对象的方法,而是使用 Proxy 对象实现相关功能。
public interface Hello {
void sayHello();
}
public class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println("Hello World!");
}
}
public class Proxy implements Hello {
private Hello hello;
public Proxy(Hello hello) {
this.hello = hello;
}
@Override
public void sayHello() {
System.out.println("Welcome!");
hello.sayHello();
System.out.println("Goodbye!");
}
}
Proxy.sayHello() 方法的实现会调用其中封装的 HelloImpl对象的 sayHello() 方法,执行真正的业务逻辑。代理的作用不仅仅是正常地完成业务逻辑,还会在业务逻辑前后添加一些代理逻辑,也就是说,Proxy.sayHello() 方法会在 HelloImpl.sayHello() 方法调用前后进行一些预处理以及一些后置处理。
代理对象可以协调真正HelloImpl对象与调用者之间的关系,在一定程度上实现了解耦的效果。
2.2 动态代理
为解决静态代理会产生大量代理类的问题,动态代理出现了
Java动态代理实现:
- JDK动态代理:利用JDK的Proxy类和InvocationHandler接口实现动态代理。代理对象在运行时创建,可以为多个目标对象提供代理服务。需要目标对象实现接口,代理类需要实现InvocationHandler并持有目标对象实例。
- CGLIB动态代理:利用ASM字节码操作生成代理类,不需要目标对象实现接口。目标类的方法会被拦截,并在方法执行前后执行代理逻辑。性能好于JDK动态代理。
- Javassist 动态代理:Javassist 是一个开源的生成 Java 字节码的类库,其主要优点在于简单、快速,直接使用Javassist 提供的 Java API 就能动态修改类的结构,或是动态生成类。
此处以JDK代理为例展开说明下
JDK 动态代理的核心是InvocationHandler 接口。这里提供一个 InvocationHandler 的Demo 实现,代码如下:
public interface Hello {
void sayHello();
}
public class HelloInvocationHandler implements InvocationHandler {
private Object target;
public HelloInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Welcome!");
method.invoke(target, args);
System.out.println("Goodbye!");
return null;
}
}
public static void main(String[] args){
Hello proxy = (Hello) Proxy.newProxyInstance(
HelloImpl.class.getClassLoader(),
new Class[] {Hello.class},
new HelloInvocationHandler(new HelloImpl()));
}