代理
coderzhouyu2023/10/4
什么是代理模式
代理模式是一种结构型设计模式,其设计思想就和其名字一样,就是给某个对象提供一个代理对象,由代理对象来控制对原对象的访问。
代理模式的使用场景有哪些
代理模式的使用场景有很多,比如:
- 在访问某个对象时需要添加额外的逻辑,比如权限控制、日志记录等。
- 在访问某个对象时需要对原对象进行优化,比如缓存、延迟加载等。
- 在访问某个对象时需要对原对象进行保护,比如只有特定的人才能访问等。
- 在访问某个对象时需要对原对象进行扩展,比如增加统计信息等。
代理模式的优缺点
代理模式最大的优点就是,代理模式能够对原对象进行扩展,而不需要修改原对象的代码。
代理模式最大的缺点就是,由于代理模式增加了代理对象,所以会增加系统的复杂度。
代理模式的实现(Java)
代理模式的实现有很多种,比如静态代理、动态代理、CGLIB代理。这里分别用这三种方式实现给一个请求添加日志记录的功能。
原始类
public interface Subject {
void request();
}
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject request");
}
}
静态代理
public class ProxySubject implements Subject {
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public void request() {
System.out.println("ProxySubject request start");
subject.request();
System.out.println("ProxySubject request end");
}
}
动态代理
Java 提供的动态代理是通过反射实现的
,其原理是通过Proxy.newProxyInstance
方法创建一个代理对象,然后通过代理对象调用被代理对象的方法,最后在代理对象的方法中添加额外的逻辑。这种方式的缺点是,被代理的类必须实现接口
。如果没有实现接口,可以使用CGLIB代理。
public class DynamicProxySubject implements InvocationHandler {
private Subject subject;
public DynamicProxySubject(Subject subject) {
this.subject = subject;
}
// 实现InvocationHandler接口的invoke方法后JVM会自动调用该方法并传入参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("DynamicProxySubject request start");
Object result = method.invoke(subject, args);
System.out.println("DynamicProxySubject request end");
return result;
}
}
// 使用动态代理
Subject subject = new RealSubject();
Subject proxySubject = (Subject) Proxy.newProxyInstance(
subject.getClass().getClassLoader(),
subject.getClass().getInterfaces(),
new DynamicProxySubject(subject));
proxySubject.request();
CGLIB代理
CGLIB代理是通过继承的方式实现的
,所以被代理的类不能是final类,而且被代理的方法不能是final方法。 其原理是通过JDK的Instrumentation API在运行时动态修改字节码,实现对类的继承
,然后通过继承的方式实现代理。如果被代理的类没有实现接口,那么CGLIB会自动创建一个接口并实现该接口,然后再通过继承的方式实现代理。
public class CglibProxySubject implements MethodInterceptor {
private Subject subject;
public CglibProxySubject(Subject subject) {
this.subject = subject;
}
// 实现MethodInterceptor接口的intercept方法后CGLIB会自动调用该方法并传入参数
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CglibProxySubject request start");
Object result = method.invoke(subject, objects);
System.out.println("CglibProxySubject request end");
return result;
}
}
// 使用CGLIB代理
Subject subject = new RealSubject();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(subject.getClass());
enhancer.setCallback(new CglibProxySubject(subject));
Subject proxySubject = (Subject) enhancer.create();
proxySubject.request();