代理模式
代理模式(Proxy Pattern)属于结构型模式的一种。它的核心思想是为其他对象提供一个代理以控制对这个对象的访问。这个模式允许我们在访问某个对象时,插入一个代理层,该代理层可以在不修改原有对象的情况下,增强原有对象的功能,或者对访问加以控制和管理。代理模式在很多场景下都非常有用,比如延迟初始化、权限验证、日志记录、性能监控、远程访问等。
类图
代理模式的角色
代理模式主要涉及以下三个角色:
- Subject(抽象主题):这是真实主题和代理主题共同实现的接口,定义了两者都必须实现的方法,从而保证了代理可以代替真实主题工作。
- RealSubject(真实主题):实现了Subject接口,代表了真正进行业务逻辑的对象。客户端最终操作的对象就是这个真实主题,但客户端不直接与之交互,而是通过代理进行。
- Proxy(代理):同样实现了Subject接口,它持有对RealSubject的引用,并通过这个引用来调用RealSubject的方法。代理可以在调用前后添加额外的处理逻辑,比如权限检查、日志记录、缓存等,但对客户端来说,它看起来就像是在直接与真实主题交互。
类型
- 静态代理:代理类在编译期间就已经确定,代理类和真实主题类的关系在代码中明确指定。这种类型的代理通常在编写代码时就创建好。
- 动态代理:代理类在运行时动态生成,通常利用反射机制生成。Java中的JDK动态代理和CGLIB是实现动态代理的两种常见方式。动态代理更加灵活,可以在不修改原有类的情况下,为不同的真实主题提供代理服务。
优点
- 增加灵活性:可以在不修改原有对象的基础上,通过代理添加额外的功能。
- 简化复杂度:代理可以封装复杂的操作,让客户端调用变得简单。
- 提高效率:例如通过缓存代理减少对真实对象的访问,提升系统性能。
- 增强安全性:可以在代理层进行权限控制,防止非法访问。
缺点
- 增加系统复杂度:引入代理层会增加系统的复杂度,尤其是在动态代理中,需要处理反射和字节码操作。
- 性能开销:虽然代理模式可以提高某些场景下的效率,但是引入代理也会带来一定的性能开销,尤其是在频繁创建动态代理的情况下。
应用场景
- 远程代理:用于分布式系统中,为远程对象提供一个局部代理对象,隐藏网络通信的细节。
- 虚拟代理:延迟加载大对象,直到真正需要时才创建,节省资源。
- 保护代理:控制对真实对象的访问权限,例如访问控制列表(ACL)。
- 缓存代理:为结果提供缓存,避免重复计算。
示例
静态代理
- Subject
java
public interface Subject {
void request();
}
- RealSubject
java
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: 处理请求");
}
}
- StaticProxy
java
public class StaticProxy implements Subject {
private final RealSubject realSubject;
public StaticProxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
preRequest();
realSubject.request();
postRequest();
}
private void preRequest() {
System.out.println("StaticProxy: 在调用真实对象前做一些预处理");
}
private void postRequest() {
System.out.println("StaticProxy: 在调用真实对象后做一些后续处理");
}
}
动态代理(JDK)
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxyExample {
interface Subject {
void request();
}
static class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: 处理请求");
}
}
static class DynamicProxyHandler implements InvocationHandler {
private final Subject realSubject;
public DynamicProxyHandler(Subject realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
preRequest();
Object result = method.invoke(realSubject, args);
postRequest();
return result;
}
private void preRequest() {
System.out.println("DynamicProxy: 在调用真实对象前做一些预处理");
}
private void postRequest() {
System.out.println("DynamicProxy: 在调用真实对象后做一些后续处理");
}
}
public static void main(String[] args) {
// 真实主题实例
Subject realSubject = new RealSubject();
// 创建动态代理实例
Subject proxySubject = (Subject) Proxy.newProxyInstance(
RealSubject.class.getClassLoader(),
new Class[]{Subject.class},
new DynamicProxyHandler(realSubject)
);
// 调用代理方法
proxySubject.request();
}
}
动态代理(cglib)
java
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxyExample implements MethodInterceptor {
private Object target;
public CglibProxyExample(Object target) {
this.target = target;
}
public Object getProxyInstance() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
// 设置回调方法
enhancer.setCallback(this);
// 创建并返回代理对象
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("Before method call");
Object result = methodProxy.invokeSuper(proxy, args);
System.out.println("After method call");
return result;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建目标对象
SomethingDto target = new SomethingDto();
//获取到代理对象,并且将目标对象传递给代理对象
SomethingDto proxyInstance = (SomethingDto)new CglibProxyExample(target).getProxyInstance();
//执行代理对象的方法,触发intecept 方法,从而实现 对目标对象的调用
String res = proxyInstance.teach();
System.out.println("res=" + res);
}
}
class SomethingDto {
public String teach() {
System.out.println("我是cglib代理,不需要实现接口 ");
return "hello cglib";
}
}