概念

当某一对象不想或者不能直接调用一个对象时,需要一个第三方的对象来控制这个对象的访问。这个第三方对象就是代理。

代理模式的结构

代理模式主要由三个角色组成

  • 抽象角色(Subject):真实角色和代理角色共同实现的父类,可以是具体类、抽象类和接口。调用方面对抽象角色编程。
  • 真实角色(Real Subject):实现具体业务的类,被代理角色调用。
  • 代理角色(Proxy):持有真实角色的引用,在调用真实角色之前或之后进行其它操作。

在实际编程过程中,代理一般会被理解为功能或方法的增强,而对调用者无感知。Spirng的AOP是非常经典的代理模式。

架构图

image.png

静态代理

在程序运行前代理的.class文件就已经存在了。对真实对象的引用是固定的。
代码示例:

/**
 * 抽象角色
 **/
public interface Subject {
    void request();
}

/**
 * 真实角色
 **/
public class RealSubject implements Subject{
    @Override
    public void request() {
    System.out.println("具体的业务实现。");
    }
} 

/**
 * 代理角色
 **/
public class ProxySubject implements Subject {

    RealSubject real = new RealSubject(); 

    @Override
    public void request() {
    beforeRequest();
    real.request(); 
    afterRequest();
    }

    public void beforeRequest() {
    System.out.println("beforeRequest");
    }

    public void afterRequest() {
    System.out.println("afterRequest");
    }
}

/**
 * 客户端(调用方)
 **/
@Test
public void Test() {
    Subject subject = new ProxySubject(new RealSubject());
    subject.request();
}

输出:
beforeRequest
具体的业务实现。
afterRequest

优点:

  • 可以做到在不修改真实对象的前提下,对真实对象进行扩展。
    缺点
  • 具体的主题类与他的代理类是一一对应的,有多少个主题类,就需要有多少个代理类,而且还可能存在每个代理类的预处理和后处理都是相同的,这样的话就会导致系统过于臃肿。

动态代理

为了解决静态代理的缺点,使系统与代码更加灵活。动态代理出现了,动态代理与静态代理最大的区别就是代理类是在程序运行的过程中创建的。

JDK动态代理

/**
 * 抽象角色
 **/
public interface Subject {
    void request();
}

/**
 * 真实角色
 **/
public class RealSubject implements Subject{
    @Override
    void request() {
    System.out.println("具体的业务实现。");
    }
} 

/**
 * JDK动态代理的核心,实现InvocationHandler接口
 **/
public class ProxyJDKSubject implements InvocationHandler {

    private Subject subject;

    public Subject getInstance(Subject subject) {
        this.subject = subject;
        Class<?> clazz = subject.getClass();
        return (Subject) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        beforeRequest();
        Object invoke = method.invoke(this.subject, args);
        afterRequest();
        return invoke;
    }

    void beforeRequest () {
        System.out.println("beforeRequest");
    }

    void afterRequest () {
        System.out.println("afterRequest");
    }
}

/**
 * 客户端(调用方)
 **/
@Test
public void Test1() {
    ProxyJDKSubject proxy = new ProxyJDKSubject();
    // 真实角色测试1
    Subject instance = proxy.getInstance(new RealSubject());
    instance.request();

}
输出:
beforeRequest
具体的业务实现。
afterRequest

这里需要注意一下Proxy.newProxyInstance()这个方法,他接受三个参数

  • ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的。
  • Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
  • InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法

CGLIB动态代理

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢?这时候就需要CGLIB了。

CGLIB采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有的父类方法的调用,顺势织入横切逻辑。但因为采用的继承,所以不能对final修饰的类进行代理。

/**
 * 抽象角色
 **/
public interface Subject {
    void request();
}

/**
 * 真实角色
 **/
public class RealSubject implements Subject{
    @Override
    void request() {
    System.out.println("具体的业务实现。");
    }
} 

/**
 * CGLIB动态代理的核心,实现MethodInterceptor接口
 **/
public class ProxyCgLibSubject implements MethodInterceptor{
    public Object getInstance(final Object target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new ProxyCgLibSubject());
        return enhancer.create();
    }

    @Override
    public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        beforeRequest();
        Object object = methodProxy.invokeSuper(sub, objects);
        afterRequest();
        return object;
    }

    void beforeRequest () {
        System.out.println("beforeRequest");
    }

    void afterRequest () {
        System.out.println("afterRequest");
    }
}
输出:
/**
 * 客户端(调用方)
 **/
@Test
public void Test2() {
    ProxyCgLibSubject proxy = new ProxyCgLibSubject();
    RealSubject instance = (RealSubject) proxy.getInstance(new RealSubject());
    instance.request();
}
输出:
beforeRequest
具体的业务实现。
afterRequest

Cglib总结:CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的类和final修饰的方法无法进行代理。