Appearance

代理

coderzhouyu2023/10/4

什么是代理模式

代理模式是一种结构型设计模式,其设计思想就和其名字一样,就是给某个对象提供一个代理对象,由代理对象来控制对原对象的访问。

代理模式的使用场景有哪些

代理模式的使用场景有很多,比如:

  1. 在访问某个对象时需要添加额外的逻辑,比如权限控制、日志记录等。
  2. 在访问某个对象时需要对原对象进行优化,比如缓存、延迟加载等。
  3. 在访问某个对象时需要对原对象进行保护,比如只有特定的人才能访问等。
  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();
Last Updated 2023/10/18 09:39:14