Java设计模式之动态代理模式原理分析

有一段时间没有写博客了,工作了就真的没有什么时间写了,主要是上了一天的班晚上回家什么都不想做,现在有了一点时间就打算写几篇博客,今天主要讲一下代理模式中的动态代理模式,记得以前在学校的时候学spring里面的Aop就看过这个模式,但是没有详细了解其中的原理,今天就详细看一下动态代理模式的原理。

动态代理的实现

实现动态代理需要使用到jdk为我们提供的一个类Proxy和一个接口InvocationHandler;
举个栗子,首先被代理的类必须要继承一个接口,这是前提条件,(也可以使用继承,这篇暂时不讲这么多),我们定义一个接口Person,这个接口贯穿整个动态代理:

public interface Person {
    void study();

    void lesson(String lesson);
}

接着创建一个被代理类继承这个接口:

public class Student implements Person {

    @Override
    public void study() {
        System.out.println("学习");
    }

    @Override
    public void lesson(String lesson) {
        System.out.println("上课:" + lesson);
    }
}

可以看到这个类有两个方法,动态代理的好处之一就是可以再一个代理类中用一个方法代理所有方法,我们就代理这两个方法。
接着创建一个类MyInvocationHandler,并实现InvocationHandler,这个类中持有一个被代理对象的实例。InvocationHandler中有一个invoke方法,所有执行代理对象的方法都会被替换成执行invoke方法(具体怎么替换后面详细说)。

public class MyInvocationHandler implements InvocationHandler {
    //被代理的对象
    Object object;

    public MyInvocationHandler(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法执行前: 方法名" + method.getName());
        Object result = method.invoke(object, args);
        System.out.println("方法执行后: 方法名" + method.getName());
        return result;
    }
}

可以看到创建这个类需要传一个对象过来,这个对象就是被代理的对象,执行方法时使用的是:

Object result = method.invoke(object, args);

这是反射中的一个用法,其中method是要执行的方法(动态代理的关键就是这个方法是不固定的,所以才能动态代理),object是我们传过来的被代理的对象,而args是方法中的参数,那么这一句的意思就是执行object对象里的参数为args的方法method,方法名是什么呢,我们先提前看一下最后的打印结果:

方法执行前: 方法名lesson
上课:音乐
方法执行后: 方法名lesson
-----------------------
方法执行前: 方法名study
学习
方法执行后: 方法名study

可以看到方法就是我们再Person接口中定义的接口。这个方法是怎么获得的呢,别着急这个是本文的核心,看到后面就知道了。需要注意的是我们虽然在这个类中写代理需要执行的步骤,但是这个类并不是代理类

上面已经有了被代理对象和代理对象需要执行的代码,下面就是怎么把他们联系起来:

    public static void main(String[] args) {
        Student student = new Student();
        InvocationHandler invocationHandler = new MyInvocationHandler(student);
        Person person = (Person) Proxy.newProxyInstance(invocationHandler.getClass().getClassLoader(), student.getClass().getInterfaces(), invocationHandler);
        person.lesson("音乐");
        System.out.println("-----------------------");
        person.study();
    }

这个首先创建一个被代理的对象Student,然后创建代理对象MyInvocationHandler并与被代理对象关联,接着就到了最重要的一步了,通过Proxy的newProxyInstance方法创建一个代理对象(这个代理对象是动态生成的),我们来仔细看一下这个方法的三个参数:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
  • loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载,通俗一点就是用那类加载器加载这个动态生成的.class文件,一般是AppClassLoader,不懂什么是类加载器的可以去百度一下。我们传的invocationHandler.getClass().getClassLoader()就是加载invocationHandler时用的加载器。
  • interfaces:这个非常重要,他是我们在一开始定义的那个接口Person
  • h:这个很明显就是我们创建的InvocationHandler实例

拿到代理对象之后调用对应的方法,运行结果如下:

方法执行前: 方法名lesson
上课:音乐
方法执行后: 方法名lesson
-----------------------
方法执行前: 方法名study
学习
方法执行后: 方法名study

可以看到代理的代码和被代理的方法都已经执行。

原理分析

下面到了这篇博客的重点,我们想一下上面整个动态代理模式的实现的关键在那里,应该就是Proxy.newProxyInstance()这个方法生成代理对象吧,我们看一下他的代码是怎么实现的:

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

        //1.复制接口
        final Class<?>[] intfs = interfaces.clone();
        // Android-changed: sm is always null
        // final SecurityManager sm = System.getSecurityManager();
        // if (sm != null) {
        //     checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        // }

        /*
         * Look up or generate the designated proxy class.
         */
        //2.生成代理类
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            // Android-changed: sm is always null
            // if (sm != null) {
            //     checkNewProxyPermission(Reflection.getCallerClass(), cl);
            // }
            //3.获得代理类中一个带有InvocationHandler参数的构造器constructor
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                // Android-changed: Removed AccessController.doPrivileged
                cons.setAccessible(true);
            }
            //4.通过构造器constructor来创建一个动态实例——代理对象(注意这里的h就是我们传过来的InvocationHandler)
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

上面的代码中一共有4个步骤,注释中已经写的很清楚了,我就不一一详细的说了,我们来看一下最重要的第二步生成代理类,具体是怎么生成的这里就不深入研究了,我们看一下最后生成的代理类是什么样的:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

import proxy.Person;

public final class $Proxy0 extends Proxy implements Person {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m0;

    /**
     * 注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
     * 为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
     * 被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
     * <p>
     * super(paramInvocationHandler),是调用父类Proxy的构造方法。
     * 父类持有:protected InvocationHandler h;
     * Proxy构造方法:
     * protected Proxy(InvocationHandler h) {
     * Objects.requireNonNull(h);
     * this.h = h;
     * }
     */
    public $Proxy0(InvocationHandler paramInvocationHandler)
            throws {
        super(paramInvocationHandler);
    }

    //这个静态块本来是在最后的,我把它拿到前面来,方便描述
    static {
        try {
            //看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("proxy.Person").getMethod("study", new Class[0]);
            m4 = Class.forName("proxy.Person").getMethod("lesson", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            return;
        } catch (NoSuchMethodException localNoSuchMethodException) {
            throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        } catch (ClassNotFoundException localClassNotFoundException) {
            throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
    }

    /**
     * 这里调用代理对象的throws方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
     * this.h.invoke(this, m3, null);这里简单,明了。
     * 来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
     * 再联系到InvacationHandler中的invoke方法。嗯,就是这样。
     */
    public final void study()
            throws {
        try {
            this.h.invoke(this, m3, null);
            return;
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    public final void lesson(Object[] args)
            throws {
        try {
            this.h.invoke(this, m4, args);
            return;
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    //注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和study方法一毛一样。

}

这个类的内容注释都已经说的很清楚了,我来总结一下:
首先在构造函数中将InvocationHandler实例保存到父类Proxy中:

    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

首先将接口中的方法通过反射取出来:

 m3 = Class.forName("proxy.Person").getMethod("study", new Class[0]);
 m4 = Class.forName("proxy.Person").getMethod("lesson", new Class[0]);

然后我们在调用person.lesson(“音乐”)和person.study()时会走到下面的函数中:

 public final void study()
            throws {
        try {
            this.h.invoke(this, m3, null);
            return;
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    public final void lesson(Object[] args)
            throws {
        try {
            this.h.invoke(this, m4, args);
            return;
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

实际上只有this.h.invoke(this, m4, args)一行代码,this.h是我们刚刚在构造函数中传入到父类Proxy中的,其实就是调用InvocationHandler的invoke()方法,往前追溯一下你会发现我们创建的MyInvocationHandler对象一直传到了这里。到这里这个代理类的任务就完成了,下面就走到了这里:

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法执行前: 方法名" + method.getName());
        Object result = method.invoke(object, args);
        System.out.println("方法执行后: 方法名" + method.getName());
        return result;
    }

又回到最初的起点,呆呆的站在镜子前。走了一圈最后调用的都是InvocationHandler的invoke()方法。

最后附上一个我参考的链接

发布了65 篇原创文章 · 获赞 24 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/shanshui911587154/article/details/81542638
今日推荐