一、定义

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。

二、为什么要用代理模式?

  • 中介隔离作用:

在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。

  • 开闭原则,增加功能:

代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

三、有哪几种代理模式?

我们有多种不同的方式来实现代理。如果按照代理创建的时期来进行分类的话,可以分为两种:静态代理、动态代理。

  1. 静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。

  2. 动态代理是在程序运行时通过反射机制动态创建的。

四、静态代理

1、实现

接口

public interface Person {
    void speak();
}

实现类

class Actor implements Person {
    private String content;

    public Actor(String content) {
        this.content = content;
    }

    @Override
    public void speak() {
        System.out.println(this.content);
    }
}

代理类

class Agent implements Person {
    private Actor actor;
    private String before;
    private String after;

    public Agent(Actor actor, String before, String after) {
        this.actor = actor;
        this.before = before;
        this.after = after;
    }

    @Override
    public void speak() {
        // before speak
        System.out.println("Before actor speak, Agent say: " + before);
        // real speak
        this.actor.speak();
        // after speak
        System.out.println("After actor speak, Agent say: " + after);
    }
}

测试

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Agent a = new Agent(new Actor("我是演员"), "我之前是一个演员", "我以后也是一个演员");
        a.speak();
    }

输出

Before actor speak, Agent say: 我之前是一个演员
我是演员
After actor speak, Agent say: 我以后也是一个演员

2、缺点

我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。

五、JDK动态代理

1、 实现

public class DynamicProxyHandler implements InvocationHandler {

    private Object object;

    public DynamicProxyHandler(Object object) {
        super();
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("之前我是一个演员");
        Object result = method.invoke(object, args);
        System.out.println("之后我还是一个演员");
        return result;
    }
}
public static void main(String[] args) {
    Person p = new Actor("我是演员");
    Person agentP = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, new DynamicProxyHandler(p));
    agentP.speak();
}

2、原理

关于InvocationHandler

每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

我们看到这个方法一共接受三个参数,那么这三个参数分别代表如下:

  1. proxy:指代JDK动态生成的最终代理对象
  2. method:指代的是我们所要调用真实对象的某个方法的Method对象
  3. args:指代的是调用真实对象某个方法时接受的参数
关于Proxy

Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:

public static Object newProxyInstance(ClassLoader loader,
    Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:

  1. loader:一个ClassLoader对象,定义了由哪个ClassLoader来对生成的代理对象进行加载
  2. interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
  3. InvocationHandler:表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
源码部分
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }
        // 获得所有实现的接口
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        // 生成代理对象的class文件(其内部实现是先生成字节码文件,然后通过类加载加载生成class实例)
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            // 使用构造器以及传入的InvocationHandler来实例化代理对象
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }
关于生成的代理类:
  1. 通过jdk动态代理生成的代理对象会继承Proxy,并实现被代理的接口,由于java不能多继承,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
  2. 生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。(使用了反射)
  3. 提供了一个使用InvocationHandler作为参数的构造方法,代理类内部的方法的实现其实都是调用了InvocationHandler的invoke方法。

详细参考:https://blog.csdn.net/zhangdefeng2008/article/details/79399898

六、cglib动态代理

1、实现

ublic class CGlibAgent implements MethodInterceptor {

    private Object proxy;

    public Object getInstance(Object proxy) {
        this.proxy = proxy;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.proxy.getClass());
        // 回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("之前我是一个码农");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("之后我就是一个rapper了");
        return result;
    }

}
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        CGlibAgent cGlibAgent = new CGlibAgent();
        Rapper rapper = (Rapper) cGlibAgent.getInstance(new Rapper());
        rapper.speak();
    }

2、原理

CGLIB是一种字节码增强库,利用其提供的字节码技术可以实现动态代理。其底层依赖ASM字节码技术。

通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用。(即MethodInterceptor的intercept方法)

七、JDK动态代理和Gglib动态代理的区别

  1. JDK动态代理是实现了被代理对象的接口,因此被代理的对象必须实现了某个接口,而Cglib是继承了被代理对象,因此不能代理final修饰的类。
  2. JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
  3. JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。