二:代理模式(静态代理,动态代理)

首先代理模式,能够分为两种,一种是静态代理,一种是动态代理。html

               两种代理从虚拟机加载类的角度来说,本质上都是同样的,都是在原有类的行为基础上,加入一些多出的行为,甚至彻底替换原有的行为。java

               静态代理采用的方式就是咱们手动的将这些行为换进去,而后让编译器帮咱们编译,同时也就将字节码在原有类的基础上加入一些其余的东西或者替换原有的东西,产生一个新的与原有类接口相同却行为不一样的类型。mysql

               说归说,咱们来真实的去试验一下,实验的话须要找一个示例,就拿咱们的数据库链接来作例子吧。程序员

               咱们都知道,数据库链接是很珍贵的资源,频繁的开关数据库链接是很是浪费服务器的CPU资源以及内存的,因此咱们通常都是使用数据库链接池来解决这一问题,即创造一堆等待被使用的链接,等到用的时候就从池里取一个,不用了再放回去,数据库链接在整个应用启动期间,几乎是不关闭的,除非是超过了最大闲置时间。spring

               可是在程序员编写程序的时候,会常用connection.close()这样的方法,去关闭数据库链接,并且这样作是对的,因此你并不能告诉程序员们说,大家使用链接都不要关了,去调用一个其余的相似归还给链接池的方法吧。这是不符合程序员的编程思惟的,也很勉强,并且具备风险性,由于程序员会忘的。sql

               解决这一问题的办法就是使用代理模式,由于代理模式能够替代原有类的行为,因此咱们要作的就是替换掉connection的close行为。数据库

               下面是connection接口原有的样子,我去掉了不少方法,由于都相似,全贴上来占地方。编程

复制代码
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Wrapper;

public interface Connection  extends Wrapper {
    
    Statement createStatement() throws SQLException;
    
    void close() throws SQLException;
    
}
复制代码

              这里只贴了两个方法,可是咱们代理的精髓只要两个方法就能掌握,下面使用静态代理,采用静态代理咱们一般会使用组合的方式,为了保持对程序猿是透明的,咱们实现Connection这个接口, 以下所示。api

复制代码
import java.sql.SQLException;
import java.sql.Statement;


public class ConnectionProxy implements Connection{
    
    private Connection connection;
    
    public ConnectionProxy(Connection connection) {
        super();
        this.connection = connection;
    }

    public Statement createStatement() throws SQLException{
        return connection.createStatement();
    }
    
    public void close() throws SQLException{
        System.out.println("不真正关闭链接,归还给链接池");
    }

}
复制代码

                咱们在构造方法中让调用者强行传入一个原有的链接,接下来咱们将咱们不关心的方法,交给真正的Connection接口去处理,就像createStatement方法同样,而咱们将真正关心的close方法用咱们本身但愿的方式去进行。数组

                此处为了更形象,LZ给出一个本人写的很是简单的链接池,意图在于代表实现的思路。下面咱们来看一下链接池的变化,在里面注明了变化点。

复制代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

public class DataSource {
    
    private static LinkedList<Connection> connectionList = new LinkedList<Connection>();
    
    static{
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    private static Connection createNewConnection() throws SQLException{
        return DriverManager.getConnection("url","username", "password");
    }
    
    private DataSource(){
        if (connectionList == null || connectionList.size() == 0) {
            for (int i = 0; i < 10; i++) {
                try {
                    connectionList.add(createNewConnection());
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public Connection getConnection() throws Exception{
        if (connectionList.size() > 0) {
            //return connectionList.remove();  这是原有的方式,直接返回链接,这样可能会被程序员把链接给关闭掉
            //下面是使用代理的方式,程序员再调用close时,就会归还到链接池
            return new ConnectionProxy(connectionList.remove());
        }
        return null;
    }
    
    public void recoveryConnection(Connection connection){
        connectionList.add(connection);
    }
    
    public static DataSource getInstance(){
        return DataSourceInstance.dataSource;
    }
    
    private static class DataSourceInstance{
        
        private static DataSource dataSource = new DataSource();
        
    }
    
}
复制代码

               链接池咱们把它作成单例,因此假设是上述链接池的话,咱们代理中的close方法能够再具体化一点,就像下面这样,用归还给链接池的动做取代关闭链接的动做。

    public void close() throws SQLException{
        DataSource.getInstance().recoveryConnection(connection);
    }

               好了,这下咱们的链接池返回的链接全是代理,就算程序员调用了close方法也只会归还给链接池了。

               咱们使用代理模式解决了上述问题,从静态代理的使用上来看,咱们通常是这么作的。

               1,代理类通常要持有一个被代理的对象的引用。

               2,对于咱们不关心的方法,所有委托给被代理的对象处理。

               3,本身处理咱们关心的方法。

               这种代理是死的,不会在运行时动态建立,由于咱们至关于在编译期,也就是你按下CTRL+S的那一刻,就给被代理的对象生成了一个不可动态改变的代理类。

               静态代理对于这种,被代理的对象很固定,咱们只须要去代理一个类或者若干固定的类,数量不是太多的时候,可使用,并且其实效果比动态代理更好,由于动态代理就是在运行期间动态生成代理类,因此须要消耗的时间会更久一点。就像上述的状况,其实就比较适合使用静态代理。

               下面介绍下动态代理,动态代理是JDK自带的功能,它须要你去实现一个InvocationHandler接口,而且调用Proxy的静态方法去产生代理类。

               接下来咱们依然使用上面的示例,可是此次该用动态代理处理,咱们来试一下看如何作。

复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;


public class ConnectionProxy implements InvocationHandler{
    
    private Connection connection;
    
    public ConnectionProxy(Connection connection) {
        super();
        this.connection = connection;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //这里判断是Connection接口的close方法的话
        if (Connection.class.isAssignableFrom(proxy.getClass()) && method.getName().equals("close")) {
            //咱们不执行真正的close方法
            //method.invoke(connection, args);
            //将链接归还链接池
            DataSource.getInstance().recoveryConnection(connection);
            return null;
        }else {
            return method.invoke(connection, args);
        }
    }
    
    public Connection getConnectionProxy(){
        return (Connection) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class}, this);
    }

//JDK实现代理只须要使用newProxyInstance方法,可是该方法须要接收三个参数(参数this=InvocationHandler接口)
  public Object getProxy(){
  //static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
  return Proxy.newProxyInstance(source.getClass().getClassLoader(), source.getClass().getInterfaces(), this);
}


//给目标对象生成代理对象(举例子:这是匿名内部类实现 InvocationHandler接口)
  public Object getProxyInstance(){
   return Proxy.newProxyInstance(getClass().getClassLoader(), getClass().getInterfaces(),
   new InvocationHandler() {
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   System.out.println("开始事务2");
   Object returnValue = method.invoke("", args);
   System.out.println("提交事务2");
   return returnValue;
   }
  }
  );
 }

}

复制代码

               上面是咱们针对connection写的动态代理,InvocationHandler接口只有一个invoke方法须要实现,这个方法是用来在生成的代理类用回调使用的,关于动态代理的原理一会作详细的分析,这里咱们先只关注用法。很显然,动态代理是将每一个方法的具体执行过程交给了咱们在invoke方法里处理。而具体的使用方法,咱们只须要创造一个ConnectionProxy的实例,而且将调用getConnectionProxy方法的返回结果做为数据库链接池返回的链接就能够了。

               上述即是咱们针对connection作动态代理的方式,可是咱们从中得不到任何好处,除了能少写点代码之外,由于这个动态代理仍是只能代理Connection这一个接口,若是咱们写出这种动态代理的方式的话,说明咱们应该使用静态代理处理这个问题,由于它表明咱们其实只但愿代理一个类就好。从重构的角度来讲,其实更简单点,那就是在你发现你使用静态代理的时候,须要写一大堆重复代码的时候,就请改用动态代理试试吧。

               一般状况下,动态代理的使用是为了解决这样一种问题,就是咱们须要代理一系列类的某一些方法,最典型的应用就是咱们前段时间讨论过的springAOP,咱们须要创造出一批代理类,切入到一系列类当中的某一些方法中。下面给出一个常用的动态代理方式。

复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class DynamicProxy implements InvocationHandler{
    
    private Object source;
    
    public DynamicProxy(Object source) {
        super();
        this.source = source;
    }
    
    public void before(){
        System.out.println("在方法前作一些事,好比打开事务");
    }
    
    public void after(){
        System.out.println("在方法返回前作一些事,好比提交事务");
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //假设咱们切入toString方法,其余其实也是相似的,通常咱们这里大部分是针对特定的方法作事情的,一般不会对类的所有方法切入
        //好比咱们经常使用的事务管理器,咱们一般配置的就是对save,update,delete等方法才打开事务
        if (method.getName().equals("toString")) {
            before();
        }
        Object result = method.invoke(source, args);
        if (method.getName().equals("toString")) {
            after();
        }
        return result;
    }
    
    public Object getProxy(){
        return Proxy.newProxyInstance(getClass().getClassLoader(), source.getClass().getInterfaces(), this);
    }
    
    
}
复制代码

                上述我作了一些注释,其实已经说明一些问题,这个代理类的做用是能够代理任何类,由于它被传入的对象是Object,而再也不是具体的类,好比刚才的Connection,这些产生的代理类在调用toString方法时会被插入before方法和after方法。

                动态代理有一个强制性要求,就是被代理的类必须实现了某一个接口,或者自己就是接口,就像咱们的Connection。

                道理其实很简单,这是由于动态代理生成的代理类是继承Proxy类的,而且会实现被你传入newProxyInstance方法的全部接口,因此咱们能够将生成的代理强转为任意一个代理的接口或者Proxy去使用,可是Proxy里面几乎全是静态方法,没有实例方法,因此转换成Proxy意义不大,几乎没什么用。假设咱们的类没有实现任何接口,那么就意味着你只能将生成的代理类转换成Proxy,那么就算生成了,其实也没什么用,并且就算你传入了接口,能够强转,你也用不了这个没有实现你传入接口的这个类的方法。

               你可能会说,假设有个接口A,那我将接口A传给newProxyInstance方法,并代理一个没实现接口A的类B,但类B与接口A有同样的方法能够吗?

               答案是能够的,而且JDK的动态代理只认你传入的接口,只要你传入,你就能够强转成这个接口,这个一会解释,可是你没法在invoke方法里调用method.invoke方法,也就是说,你只能所有替换A接口的方法,而不能使用类B中原有与接口A方法描述相同的方法,这是由于invoke中传入的Method的class信息是接口A,而类B由于没实现接口A,因此没法执行传入的Method,会抛出非法参数异常。

               下面我贴出测试代码,各位能够本身试一下,具体为什么会这样是在后面解释的,这里再也不多作解释。

              先是一个普通接口。

复制代码
public interface TestInterface {

    void method1();
    
    void method2();
    
    void method3();
}
复制代码

            而后是一个类,和接口如出一辙的方法,可是就是没实现这个接口。

复制代码
public class TestClass{

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

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

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

}
复制代码

               下面是测试类。

复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class DynamicProxy implements InvocationHandler{
    
    private Object source;
    
    public DynamicProxy(Object source) {
        super();
        this.source = source;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("接口的方法所有变成这样了");
        //这里source是TestClass,可是咱们不能使用反射调用它的方法,像下面这样,放开这一行会抛异常
        //return method.invoke(source, args);
        return null;
    }
    
    public static void main(String[] args) {
        //只要你传入就能够强转成功
        TestInterface object =  (TestInterface) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{TestInterface.class}, new DynamicProxy(new TestClass()));
        object.method1();
        object.method2();
        object.method3();
    }
}
复制代码

               上面咱们运行就会发现接口的方法所有都只能输出一个很2的字符串了。若是是要继续使用TestClass的方法也不是不行,只要你确认你传入的类包括了全部你传入的接口的方法,只是没实现这些接口而已,那么你能够在invoke中这样使用。

复制代码
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Method sourceMethod = source.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
        sourceMethod.setAccessible(true);
        Object result = sourceMethod.invoke(source, args);
        System.out.println("after");
        return result;
    }
复制代码

                这就与你实现接口的表现行为一致了,可是咱们原本就只须要一句method.invoke就能够了,就由于没实现接口就要多写两行,因此这种突破JDK动态代理必须实现接口的行为就有点多此一举了。由于你原本就实现了该接口的方法,只是差了那一句implements而已。

               上面写这个例子只是为了解释LZ当初的疑惑,由于LZ曾一度认为不实现接口就不能使用动态代理,如今想一想那时候LZ有点2,呵呵。

               好了,从如今开始,咱们开始详细讲解动态代理的原理,这算是进阶篇,若是是新手的话,能够跳过下面的内容,由于如今还不必知道这些,并且弄很差会越看越蒙,不过仅仅是LZ我的建议,你要有耐心,彻底能够继续看下去。

               接下来咱们结合源码去看一下,代理类是如何产生的,首先固然就是要进入Proxy的newProxyInstance方法,这里是产生代理的入口,源码以下。

复制代码
public static Object newProxyInstance(ClassLoader loader,
                      Class<?>[] interfaces,
                      InvocationHandler h)
    throws IllegalArgumentException
    {
    if (h == null) {
        throw new NullPointerException();
    }

    /*
     * Look up or generate the designated proxy class.
     */
    Class cl = getProxyClass(loader, interfaces);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        Constructor cons = cl.getConstructor(constructorParams);
        return (Object) cons.newInstance(new Object[] { h });
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString());
    } catch (IllegalAccessException e) {
        throw new InternalError(e.toString());
    } catch (InstantiationException e) {
        throw new InternalError(e.toString());
    } catch (InvocationTargetException e) {
        throw new InternalError(e.toString());
    }
    }
复制代码

               这个方法其实很简单,首先获取了代理类的运行时Class引用,而后调用了这个Class中的构造方法,这个构造方法只有一个参数,正是InvocationHandler接口,由此产生了一个代理类的实例。那么关键的地方就在于如何获取的代理类运行时的class信息的呢?咱们进入getProxyClass方法看一下。为了方便起见,我直接加注释,这个方法须要解释的地方比较多。

复制代码
public static Class<?> getProxyClass(ClassLoader loader,
            Class<?>... interfaces) throws IllegalArgumentException {
        //若是传入的接口长度大于65535就抛出异常,我去你妹。。。
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        
        Class proxyClass = null;

        /* collect interface names to use as key for proxy class cache */
        String[] interfaceNames = new String[interfaces.length];

        Set interfaceSet = new HashSet(); // for detecting duplicates

        for (int i = 0; i < interfaces.length; i++) {
            /*
             * Verify that the class loader resolves the name of this interface
             * to the same Class object.
             */
            String interfaceName = interfaces[i].getName();
            Class interfaceClass = null;
            try {
                //加载每个接口的运行时Class信息
                interfaceClass = Class.forName(interfaceName, false, loader);
            } catch (ClassNotFoundException e) {
            }
            //若是采用你传入的类加载器载入的Class和你传入的Class不相等则抛出异常
            if (interfaceClass != interfaces[i]) {
                throw new IllegalArgumentException(interfaces[i]
                        + " is not visible from class loader");
            }

            //若是你传入的不是接口抛出异常
            /*
             * Verify that the Class object actually represents an interface.
             */
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(interfaceClass.getName()
                        + " is not an interface");
            }

            //若是你传入的接口重复抛异常
            /*
             * Verify that this interface is not a duplicate.
             */
            if (interfaceSet.contains(interfaceClass)) {
                throw new IllegalArgumentException("repeated interface: "
                        + interfaceClass.getName());
            }
            interfaceSet.add(interfaceClass);

            interfaceNames[i] = interfaceName;
        }

        /*
         * Using string representations of the proxy interfaces as keys in the
         * proxy class cache (instead of their Class objects) is sufficient
         * because we require the proxy interfaces to be resolvable by name
         * through the supplied class loader, and it has the advantage that
         * using a string representation of a class makes for an implicit weak
         * reference to the class.
         */
        Object key = Arrays.asList(interfaceNames);

        /*
         * Find or create the proxy class cache for the class loader.
         */
        Map cache;
        synchronized (loaderToCache) {
            //这个是为了存储每个类加载器所载入过的代理接口的代理类
            cache = (Map) loaderToCache.get(loader);
            if (cache == null) {
                cache = new HashMap();
                loaderToCache.put(loader, cache);
            }
            /*
             * This mapping will remain valid for the duration of this method,
             * without further synchronization, because the mapping will only be
             * removed if the class loader becomes unreachable.
             */
        }

        /*
         * Look up the list of interfaces in the proxy class cache using the
         * key. This lookup will result in one of three possible kinds of
         * values: null, if there is currently no proxy class for the list of
         * interfaces in the class loader, the pendingGenerationMarker object,
         * if a proxy class for the list of interfaces is currently being
         * generated, or a weak reference to a Class object, if a proxy class
         * for the list of interfaces has already been generated.
         */
        synchronized (cache) {
            /*
             * Note that we need not worry about reaping the cache for entries
             * with cleared weak references because if a proxy class has been
             * garbage collected, its class loader will have been garbage
             * collected as well, so the entire cache will be reaped from the
             * loaderToCache map.
             */
            do {
                //检查是否有生成好的代理
                Object value = cache.get(key);
                if (value instanceof Reference) {
                    proxyClass = (Class) ((Reference) value).get();
                }
                //有的话直接返回
                if (proxyClass != null) {
                    // proxy class already generated: return it
                    return proxyClass;
                //不然看一下这个代理类是否是正在构造中,是的话就在cache对象上等待
                } else if (value == pendingGenerationMarker) {
                    // proxy class being generated: wait for it
                    try {
                        cache.wait();
                    } catch (InterruptedException e) {
                        /*
                         * The class generation that we are waiting for should
                         * take a small, bounded time, so we can safely ignore
                         * thread interrupts here.
                         */
                    }
                    continue;
                //若是没有现成的,也没有创造中的,那就开始创造代理类
                } else {
                    /*
                     * No proxy class for this list of interfaces has been
                     * generated or is being generated, so we will go and
                     * generate it now. Mark it as pending generation.
                     */
                    //将当前代理类置为正在构造中,并直接退出循环
                    cache.put(key, pendingGenerationMarker);
                    break;
                }
            } while (true);
        }

        try {
            String proxyPkg = null; // package to define proxy class in

            //这一段是看你传入的接口中有没有不是public的接口,若是有,这些接口必须所有在一个包里定义的,不然抛异常
            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package. Verify that all
             * non-public proxy interfaces are in the same package.
             */
            for (int i = 0; i < interfaces.length; i++) {
                int flags = interfaces[i].getModifiers();
                if (!Modifier.isPublic(flags)) {
                    String name = interfaces[i].getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                                "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) { // if no non-public proxy interfaces,
                proxyPkg = ""; // use the unnamed package
            }

            {
                /*
                 * Choose a name for the proxy class to generate.
                 */
                long num;
                synchronized (nextUniqueNumberLock) {
                    num = nextUniqueNumber++;
                }
                //生成一个随机代理类名
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
                /*
                 * Verify that the class loader hasn't already defined a class
                 * with the chosen name.
                 */
                
                //这一句就是重中之重了,生成代理类的class文件,这就是JDK动态代理的原理了,经过动态生成class文件来产生的代理类
                //这个generateProxyClass方法下面会着重介绍
                /*
                 * Generate the specified proxy class.
                 */
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                        proxyName, interfaces);
                try {
                    //获得class文件二进制流后,直接载入代理类
                    proxyClass = defineClass0(loader, proxyName,
                            proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {
                    /*
                     * A ClassFormatError here means that (barring bugs in the
                     * proxy class generation code) there was some other invalid
                     * aspect of the arguments supplied to the proxy class
                     * creation (such as virtual machine limitations exceeded).
                     */
                    throw new IllegalArgumentException(e.toString());
                }
            }
            //proxyClasses这个Map是为了来判断是否是代理的Class
            // add to set of all generated proxy classes, for isProxyClass
            proxyClasses.put(proxyClass, null);

        } finally {
            /*
             * We must clean up the "pending generation" state of the proxy
             * class cache entry somehow. If a proxy class was successfully
             * generated, store it in the cache (with a weak reference);
             * otherwise, remove the reserved entry. In all cases, notify all
             * waiters on reserved entries in this cache.
             */
            synchronized (cache) {
                if (proxyClass != null) {
                    //最终将生成的代理用弱引用包装起来放到cache当中
                    cache.put(key, new WeakReference(proxyClass));
                } else {
                    //若是代理类是空则移除代理的接口所表明的key值
                    cache.remove(key);
                }
                //通知正在等待在cache对象上的线程,告诉他们能够继续了,代理Class加载完毕了
                cache.notifyAll();
            }
        }
        return proxyClass;
    }
复制代码

                上面基本上已经解释的很清楚了,下面就是去看一下byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces)这句话是如何处理的,也就是如何获得的代理类的class文件的,咱们进去源码看一下,我依然会加上注释。

复制代码
public static byte[] generateProxyClass(String paramString, Class[] paramArrayOfClass) {
        //新建一个ProxyGenerator实例,传入类名和接口数组
        ProxyGenerator localProxyGenerator = new ProxyGenerator(paramString, paramArrayOfClass);
        //这个才是真正生成class文件的地方
        final byte[] arrayOfByte = localProxyGenerator.generateClassFile();
        //看保存生成文件的标志是否为真,若是是就将class文件生成到本地,生成时要检查权限
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    try {
                        FileOutputStream localFileOutputStream = new FileOutputStream( ProxyGenerator.dotToSlash(this.val$name) + ".class");
                        localFileOutputStream.write(arrayOfByte);
                        localFileOutputStream.close();
                        return null;
                    } catch (IOException localIOException) {
                        throw new InternalError( "I/O exception saving generated file: " + localIOException);
                    }
                }

            });
        }

        return arrayOfByte;
    }
复制代码

                咱们继续跟踪到localProxyGenerator.generateClassFile()这一句当中,依然会加上注释。

复制代码
private byte[] generateClassFile() {
        //addProxyMethod方法,就是将方法都加入到一个列表中,并与对应的class对应起来
        //这里给Object对应了三个方法hashCode,toString和equals
        addProxyMethod(hashCodeMethod, Object.class);
        addProxyMethod(equalsMethod, Object.class);
        addProxyMethod(toStringMethod, Object.class);

        //一样将接口与接口下的方法对应起来
        for (int i = 0; i < this.interfaces.length; i++) {
            localObject1 = this.interfaces[i].getMethods();
            for (int k = 0; k < localObject1.length; k++) {
                addProxyMethod(localObject1[k], this.interfaces[i]);
            }

        }
        //检查全部代理方法的返回类型
        for (Iterator localIterator1 = this.proxyMethods.values().iterator(); localIterator1
                .hasNext();) {
            localObject1 = (List) localIterator1.next();
            checkReturnTypes((List) localObject1);
        }

        Object localObject2;
        try {
            //方法中加入构造方法,这个构造方法只有一个,就是一个带有InvocationHandler接口的构造方法
            //这个才是真正给class文件,也就是代理类加入方法了,不过还没真正处理,只是先加进来等待循环,构造方法在class文件中的名称描述是<init>
            this.methods.add(generateConstructor());
            //循环代理方法
            for (localIterator1 = this.proxyMethods.values().iterator(); localIterator1
                    .hasNext();) {
                localObject1 = (List) localIterator1.next();
                for (localIterator2 = ((List) localObject1).iterator(); localIterator2
                        .hasNext();) {
                    localObject2 = (ProxyMethod) localIterator2.next();
                    //给每个代理方法加一个Method类型的属性,数字10是class文件的标识符,表明这些属性都是private static的
                    this.fields.add(new FieldInfo(((ProxyMethod) localObject2).methodFieldName,"Ljava/lang/reflect/Method;", 10));
                    //将每个代理方法都加到代理类的方法中
                    this.methods.add(((ProxyMethod) localObject2).generateMethod());
                }
            }
            Iterator localIterator2;
            //加入一个静态初始化块,将每个属性都初始化,这里静态代码块也叫类构造方法,其实就是名称为<clinit>的方法,因此加到方法列表
            this.methods.add(generateStaticInitializer());
        } catch (IOException localIOException1) {
            throw new InternalError("unexpected I/O Exception");
        }
        //方法和属性个数都不能超过65535,包括刚才的接口个数也是这样,
        //这是由于在class文件中,这些个数都是用4位16进制表示的,因此最大值是2的16次方-1
        if (this.methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        }
        if (this.fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        }
        //这里是将类名中的.转成成斜线为了写入class文件。
        this.cp.getClass(dotToSlash(this.className));
        this.cp.getClass("java/lang/reflect/Proxy");
        for (int j = 0; j < this.interfaces.length; j++) {
            this.cp.getClass(dotToSlash(this.interfaces[j].getName()));
        }

        this.cp.setReadOnly();
        //这里开始真正的写class文件
        ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();
        Object localObject1 = new DataOutputStream(localByteArrayOutputStream);
        try {
            //写入class文件的标识号,标识这是一个class文件
            ((DataOutputStream) localObject1).writeInt(-889275714);
            //次版本号0
            ((DataOutputStream) localObject1).writeShort(0);
            //主版本号,49表明的是JDK1.5
            ((DataOutputStream) localObject1).writeShort(49);
            //这里写入的是常量池,包括一些属性名称,类名称,方法描述符,属性描述符等等,常量池在加载时会被放到方法区或者说永久代。
            this.cp.write((OutputStream) localObject1);
            //这里写入的是这个类的访问标识,49表明的是public final,也就是说JDK动态代理生成的代理类都是final的
            ((DataOutputStream) localObject1).writeShort(49);
            //写入代理类的类名
            ((DataOutputStream) localObject1).writeShort(this.cp
                    .getClass(dotToSlash(this.className)));
            //写入代理类的父类类名,也就是Proxy类,这个位置的类若是说是JAVA文件,至关于extend后面的类,也就是父类
            ((DataOutputStream) localObject1).writeShort(this.cp
                    .getClass("java/lang/reflect/Proxy"));
            //写入代理类所实现的接口数量
            ((DataOutputStream) localObject1)
                    .writeShort(this.interfaces.length);
            //写入代理类所实现的接口类名,一样的,对于JAVA文件来讲,至关于implements后面的接口,也就是实现的接口
            for (int m = 0; m < this.interfaces.length; m++) {
                ((DataOutputStream) localObject1).writeShort(this.cp
                        .getClass(dotToSlash(this.interfaces[m].getName())));
            }
            //写入属性个数
            ((DataOutputStream) localObject1).writeShort(this.fields.size());
            //写入属性描述
            for (Iterator localIterator3 = this.fields.iterator(); localIterator3
                    .hasNext();) {
                localObject2 = (FieldInfo) localIterator3.next();
                ((FieldInfo) localObject2)
                        .write((DataOutputStream) localObject1);
            }
            //写入方法个数
            ((DataOutputStream) localObject1).writeShort(this.methods.size());
            //写入方法描述,方法的code属性,以及构造方法和类构造方法都在这里被写入了。
            for (localIterator3 = this.methods.iterator(); localIterator3
                    .hasNext();) {
                localObject2 = (MethodInfo) localIterator3.next();
                ((MethodInfo) localObject2)
                        .write((DataOutputStream) localObject1);
            }
            //结束
            ((DataOutputStream) localObject1).writeShort(0);
        } catch (IOException localIOException2) {
            throw new InternalError("unexpected I/O Exception");
        }

        return localByteArrayOutputStream.toByteArray();
    }
复制代码

                其实代理类的class文件并不复杂,仍是有不少规律可循的,因此上述过程基本上可让各位了解下JDK动态代理生成代理类时都生成了什么东西。

                下面咱们能够调用下JDK中生成Class文件的方法,而且写入到本地文件,而后使用反编译工具来看一下生成的代理类究竟是什么样子的。下面是生成文件的测试类。咱们暂且将生成的类名写成TestProxy,代理的接口就是咱们上面的TestInterface。以下。

复制代码
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import sun.misc.ProxyGenerator;



public class CreateClassTest {

    public static void main(String[] args) throws IOException {
        byte[] classFile = ProxyGenerator.generateProxyClass("TestProxy", new Class[]{TestInterface.class});
        File file = new File("F:/TestProxy.class");
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(classFile);
        fos.flush();
        fos.close();
    }
    
}
复制代码

                生成后,咱们反编译过来会是以下格式的JAVA文件。我加入了注释,大体说明了下文件中生成的部分与刚才分析的时候写入的过程的对应关系。

复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

//public final的,继承Proxy,实现你传入的接口
public final class TestProxy extends Proxy
  implements TestInterface
{
  //private static 的Method属性,对应全部方法
  private static Method m1;
  private static Method m5;
  private static Method m3;
  private static Method m4;
  private static Method m0;
  private static Method m2;
  //惟一的构造方法,须要一个InvocationHandler接口传入
  public TestProxy(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }
  //重写Object的三个方法
  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void method3()
    throws 
  {
    try
    {
      this.h.invoke(this, m5, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  //代理的三个方法,回调传入的InvocationHandler的invoke方法
  public final void method1()
    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 method2()
    throws 
  {
    try
    {
      this.h.invoke(this, m4, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final int hashCode()
    throws 
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  //这个就是刚才this.methods.add(generateStaticInitializer());这一句话所加入的静态初始化块,初始化每个属性
  static
  {
    try
    {//每个属性所表明的Method都是与上面加入代理方法列表时与固定类绑定的,这是class文件中的格式,方法要与固定的类绑定
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m5 = Class.forName("TestInterface").getMethod("method3", new Class[0]);
      m3 = Class.forName("TestInterface").getMethod("method1", new Class[0]);
      m4 = Class.forName("TestInterface").getMethod("method2", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}
复制代码

        看到这里就知道invoke方法是干吗的了,其实就是生成的代理类对每个方法的处理就是回调invoke方法。从生成的代理类源文件中也能够发现,每个Method除了hashCode,toString和equals外,都是与所属的接口绑定的,因此这也就解释了为何咱们不实现这个接口,只传入进入的话,不能直接使用method.invoke,而是要转成source对应的method才能够调用。

        好了,代理模式就分析到这里了,这里讲的更多的是代理模式的原理,对于如何使用并无讲述太多,是由于代理模式在平时工做中用的虽然不少,但咱们大可能是使用的现成的,缘由很简单,就是由于spring的AOP已经给咱们弄了一个很好的动态代理的框架,因此咱们几乎不须要本身去写,只要明白其原理,知道动态代理和静态代理主要处理的问题是那种的,知道在何处用,也可以用起来驾轻就熟就能够了

转载:https://www.cnblogs.com/zuoxiaolong/p/pattern4.html

相关文章
相关标签/搜索