欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java设计模式篇(一)--代理模式详解(这一篇足够了)

程序员文章站 2022-05-02 23:33:18
...

在设计模式中,代理模式是经常会用到的一种模式。但是光说设计模式,没有实际意义。在Mybatis源码系列中,用到了该设计模式,因此,我们结合Mybatis使用的代理模式过程,来分析下如何使用代理模式,代理模式的核心是什么。

一、基于接口的代理模式

在讲代理模式的时候,我想起在大话模式中讲的一个故事。A暗恋B,想给B送个礼物,但是又不能直接给B送去。因此,它去礼物店,买了一件礼物,告诉店员心仪的B的信息,让店员送过去,这里,店员就帮忙完成了A想送礼物给B的事情。这里的店员,就是代理者的角色。

那我们用代码如何去实现以上过程呢?我们首先讲第一种方式---静态代理模式。

1、静态代理

为什么叫静态代理模式?当然是相对于动态来说,在我们不知道静态代理是什么之前,我们先不说这个概念,先来看过程。

①先定义一个接口,里面包含方法--送礼物

 

public interface GiveGift {
    void giveGift();
}

②定义追求者的实体类,追求者A要实现送礼物给B的方法。

 

 

public class Pursuer implements GiveGift{
    public void giveGift() {
        System.out.print("给B送礼物完成!");
    }
}

 ③定义代理类--我们的店员,要给完成追求者A送礼物给B的任务,店员知道这个礼物是谁让我送的,因此,要持有追求者的信息。

 

 

public class Proxy implements GiveGift {
    Pursuer gg;

    public Proxy(Pursuer gg) {
        this.gg = gg;
    }

    public void giveGift() {
        gg.giveGift();
    }
}

 ④送礼物过程:我是A,请帮我送礼物。

 

public static void main(String[] args){
        Pursuer A = new Pursuer();
        Proxy proxy = new Proxy(A);
        proxy.giveGift();
    }

 一整个故事看下来,静态代理的结构UML类图就明确了,如下图:

 

Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
 

2、动态代理

我们今天要说的动态代理,仅仅说jdk的动态代理,不涉及cglib。

我们先来看一例,如何使用jdk的动态代理,要想使用,首先得看下代理类中有哪些方法可以用到。

在jdk的代理类Proxy中(package java.lang.reflect下),我们看下产生代理类的方法newProxyInstance:

有两种方式:

1、使用newProxyInstance方法

 

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

 这里有三个参数,一个是ClassLoader类加载器,一个是interfaces一组接口类(因为一个类可以实现多个接口),InvocationHandler是回调内容处理器。

 

我们举一例,说明使用过程:

①定义接口类

 

package com.zhaodf.pattern;

public interface GiveGift {
    void giveChocolate(String name);
    void giveFlower(String name);
}

 ②定义接口实现类

 

 

package com.zhaodf.pattern;

public class GiveGiftImpl implements GiveGift{

    public void giveChocolate(String name) {
        System.out.println("送巧克力给B");
    }

    public void giveFlower(String name) {
        System.out.println("送花给B");
    }
}

 

 

③定义回调方法处理器,这里我们仿照Mybatis的处理方法,分开处理Object类的方法和我们目标对象类的方法

 

package com.zhaodf.pattern;

import org.apache.ibatis.reflection.ExceptionUtil;

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

public class GiveGiftInvocationHandler implements InvocationHandler {
    //定义目标对象,最后使用反射要调用目标对象中的方法
    //这里使用Object,所有对象都是Object的子类,这里就有了共性
    private Object target;

    //提供构造函数,将目标对象持有
    public GiveGiftInvocationHandler(Object target){
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //这里我们可以仿照Mybatis中的写法,分开两部分
        //一部分是Object中自有的方法,如toString,HashCode等
        if (Object.class.equals(method.getDeclaringClass())) {
            try {
                System.out.println("调用Object类的方法");
                return method.invoke(this, args);
            } catch (Throwable var5) {
                throw ExceptionUtil.unwrapThrowable(var5);
            }
        } else {
            Object result = method.invoke(target,args);
            return result;
        }
    }
}

 

 

④使用JDK提供的代理类Proxy中newProxyInstance方法,动态生成代理类,调用对应方法:

 

package com.zhaodf.pattern;

import java.lang.reflect.Proxy;

public class TestProxy {
    public static void main(String[] args){

        GiveGift giveGift = (GiveGift)Proxy.newProxyInstance(
                GiveGiftImpl.class.getClassLoader(),
                new Class[]{GiveGift.class},
                new GiveGiftInvocationHandler(new GiveGiftImpl()));
        giveGift.giveChocolate("Angel");
        giveGift.giveFlower("Angel");
        giveGift.toString();
    }
}

 ⑤结果截图:

 

Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
 

2、使用getProxyClass方法

其他内容一样,我们只修改TestProxy中的调用方法 。方法二与方法一不同的地方在于,方法二将步骤拆成了两步,其实源码中调用的生成代理类的方法是一样的,一会说到源码我们再说。

 

package com.zhaodf.pattern;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class TestProxy {
    public static void main(String[] args) throws Exception {
        //方法二
        Class proxy0 = Proxy.getProxyClass(GiveGiftImpl.class.getClassLoader(),
               new Class[]{GiveGift.class}) ;
        Constructor constructor = proxy0.getConstructor(InvocationHandler.class);
        GiveGift giveGift2 = (GiveGift)constructor.newInstance(new GiveGiftInvocationHandler((new GiveGiftImpl())));
        giveGift2.giveChocolate("Angel");
        giveGift2.giveFlower("Angel");
        giveGift2.toString();
    }
}

 

 

从静态代理和动态代理的使用过程我们可以解答上述问题了--为什么叫静态代理?

这是因为,在我们静态代理使用过程中,我们需要自己去写代理类Proxy,在代理类中调用实现类的方法,对于不同的代理对象,我们需要自己提供不同的代理类。

而动态代理中,使用了JDK提供的代理类Proxy,能够实现不同对象的代理,利用反射去调用实现类中的方法,更具有扩展性和一般性,在使用中我们可以根据需求使用JDK提供的代理类动态的生成我们的代理类对象。

因为,静态代理的静态体现在我们自己写的代理类,只能去满足当前一次性需求,因此叫静态。

而JDK提供的动态代理方式,则可以满足更多对象的代理请求,因此,动态代理也是具有一般性的一种设计模式。这也告诉我们,在日常需求涉及中,要从具体中抽象一般,达到多用,重用的目标。

 

二、动态代理的源码解析

在会使用,如果是我,我还想继续了解它的生成过程,这样可以知其然也知其所以然,理解的通透。

问自己几个问题:

  • 代理类长啥样?在哪,我想看见它,内容是什么?
  • 源码的调用过程,代理类是怎么生成的?

我们带着疑问去了解这些内容。首先要说明的是,我使用的jdk版本是1.8。

1、代理类长啥样?在哪?

既然是动态代理,那么如果我们没做任何操作,生成的代理类会通过类加载器被JVM直接被加载在内存中,我们是看不到的,因此,我们需要做些额外操作。这里提供两种方法:

①通过设置系统属性

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

 

public static void main(String[] args) throws Exception {
        //方法一
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        GiveGift giveGift = (GiveGift)Proxy.newProxyInstance(
                GiveGiftImpl.class.getClassLoader(),
                new Class[]{GiveGift.class},
                new GiveGiftInvocationHandler(new GiveGiftImpl()));
        giveGift.giveChocolate("Angel");
        giveGift.giveFlower("Angel");
        giveGift.toString();
    }

 

 

 

我们知道,System.getProperties()中的属性集是系统执行程序的全局属性。那为什么我们设置sun.misc.ProxyGenerator.saveGeneratedFiles为true就能将代理类文件保存下来呢?问的好!

设置了总得用了才能知道为什么这么设置,一会我们源码就会看到。

我们先来段(在ProxyClassFactory类的apply方法中,jdk版本是1.8):

 

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);

 在ProxyGenerator的静态方法generateProxyClass调用时候,我们看下源码:

 

 

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        final byte[] var4 = var3.generateClassFile();
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                            Files.createDirectories(var3);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class");
                        }

                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }

        return var4;
    }

 看到没,看if判断,在源码中会根据saveGeneratedFiles(是否保存生成的文件属性)来判断,如果为true,那么会将生成的文件内容保存在com\sun\proxy\$ProxyX(X表示数字)中。这里的WindowsPath是表示生成的路径是相对于你的Windows环境来说的。最后合起来的生成路径就是:E:\mybatis\com\sun\proxy。(因个人项目环境而已)

Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
 
Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
 

②直接使用ProxyGenerator类的方法

我们的测试方法中,使用JDK的动态代理类的Proxy.newProxyInstance方法,已经获取到了代理类,那么我们就可以将该代理类的字节码写到文件中,保存为class文件,这样也能看到具体的内容,我们看我们写的方法内容:

 

public static void main(String[] args) throws Exception {
        //方法一
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        GiveGift giveGift = (GiveGift)Proxy.newProxyInstance(
                GiveGiftImpl.class.getClassLoader(),
                new Class[]{GiveGift.class},
                new GiveGiftInvocationHandler(new GiveGiftImpl()));
        giveGift.giveChocolate("Angel");
        giveGift.giveFlower("Angel");
        giveGift.toString();
        byte[] proxyClass = ProxyGenerator.generateProxyClass(giveGift.getClass().getSimpleName(), giveGift.getClass().getInterfaces());
        //将字节码文件保存到E盘,文件名为$Proxy1.class
        FileOutputStream outputStream = new FileOutputStream(new File("E:\\mybatis\\com\\zhaodf\\Proxy1.class"));
        outputStream.write(proxyClass);
        outputStream.flush();
        outputStream.close();
    }

 

 

生成的文件及目录如下图(这里的目录E:\\mybatis\\com\\zhaodf\\如果没有就需要手动新建):

Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
 

按照上述两种方式,都可以获取到生成的代理类,那么内容是什么?当然,生成的class文件是字节码,我们可以使用工具看到字节码内容:

 

import com.zhaodf.pattern.GiveGift;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

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

  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void giveFlower(String paramString)
    throws 
  {
    try
    {
      this.h.invoke(this, m3, new Object[] { paramString });
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final String toString()
    throws 
  {
    try
    {
      return ((String)this.h.invoke(this, m2, null));
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void giveChocolate(String paramString)
    throws 
  {
    try
    {
      this.h.invoke(this, m4, new Object[] { paramString });
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

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

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.zhaodf.pattern.GiveGift").getMethod("giveFlower", new Class[] { Class.forName("java.lang.String") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m4 = Class.forName("com.zhaodf.pattern.GiveGift").getMethod("giveChocolate", new Class[] { Class.forName("java.lang.String") });
      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());
    }
  }
}

 生成的代理类内容很简单,它继承了Proxy,实现了我们给定的接口GiveGift。

 

静态代码块中初始化了5个方法,分别是Object类的三个--equals、toString、hashCode(因为我们没有重写这三个方法,直接继承的就是Object类的),以及GiveGift接口中的两个方法giveChocolate和giveFlower。

 

每个方法的调用都是调用回调器类对象h中的invoke方法。

 

2、源码解析--代理类是如何生成的?

我们从Proxy.newProxyInstance说起。先来源码瞧一瞧:

 

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

        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<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            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);
        }
    }

我们一行一行来解释:

 

  •  final Class<?>[] intfs = interfaces.clone();将我们传入的接口类数组做了一个克隆
  • Class<?> cl = getProxyClass0(loader, intfs);根据我们传进来的类加载器和克隆后的接口数组获取代理类,此部分我们单独作为一点重点讲。
  • final Constructor<?> cons = cl.getConstructor(constructorParams);根据得到的代理类cl,调用它的构造函数,传入的参数是constructorParams,内容为Class<?>[] constructorParams = { InvocationHandler.class };也就是我们生成的代理类中的构造函数部分:
    public $Proxy0(InvocationHandler paramInvocationHandler)
        throws 
      {
        super(paramInvocationHandler);
      }
  • final InvocationHandler ih = h;将我们传入的GiveGiftInvocationHandler对象h赋值给ih
  • return cons.newInstance(new Object[]{h});使用GiveGiftInvocationHandler对象h构造代理类对象。

我们来详细解释Class<?> cl = getProxyClass0(loader, intfs);部分。

源码如下:

 

/**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }
  •  这里有个限制条件if (interfaces.length > 65535),就是代理类实现的接口个数不能超过65535个(估计也没人这么傻),这里可以作为跟别人吹牛的一个条件。
  • proxyClassCache.get(loader, interfaces);如果发现通过给定接口数组的类的加载器已经定义过代理类。那么直接返回缓存的副本,否则,需要通过代理类的工厂类去新创建一个代理类。这里我们重点看下。

 

 

在讲proxyClassCache.get(loader, interfaces);方法之前,我们先看下proxyClassCache是什么:

 

/**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

 proxyClassCache字面意思是代理类的缓存,它是WeakCache<ClassLoader, Class<?>[], Class<?>>的对象。我们看下WeakCache的构造函数:

 

/**
     * Construct an instance of {@code WeakCache}
     *
     * @param subKeyFactory a function mapping a pair of
     *                      {@code (key, parameter) -> sub-key}
     * @param valueFactory  a function mapping a pair of
     *                      {@code (key, parameter) -> value}
     * @throws NullPointerException if {@code subKeyFactory} or
     *                              {@code valueFactory} is null.
     */
    public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }

 这里的K为ClassLoader,P为Class<?>[]接口数组,V为对应的代理类。

 

 我们看proxyClassCache.get(loader, interfaces);源码部分:

public V get(K key, P parameter) {
        //参数检查不为空
        Objects.requireNonNull(parameter);
        //清除掉缓存的cacheKey值
        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

 我们详细解释下关键代码:

Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

我们看下subKeyFactory.apply(key, parameter)的源码:

 

@Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " 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.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * 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 (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.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, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return 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());
            }
        }
    }

 这里就是代理类生成的关键地方了。我们关注下重点代码,这里传入的参数,一个是类加载器,一个是接口克隆数组。

 

在第一个for循环中,首先根据参数接口数组获取到全限定名的接口类,判断接口数组中的每个接口类是否存在,是否是接口,在第三个判断中,将接口类放到了IdentityHashMap中,如果已经在IdentityHashMap存在,那么说明接口数组中传递的接口类参数中有重复。

Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
 

我们接着往下看,proxyPkg定义了最后生成的代理类放的包位置。

第二个for循环,验证所有的非non-public的接口在同一个包下。

因为上述步骤都没有成功设置proxyPkg,因此这里proxyPkg为null,那么将代理包位置设置为默认值:

Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
 这里获取代理类的名称,因为并发情况下可能会存在多个代理类,所以这里使用了原子类,保证唯一,因为我们是第一次生成,因此代理类的名称连同包名为:com.sun.proxy.$Proxy0
Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
 

下面将正式进入代理类的生成过程:

 

/*
 * Generate the specified proxy class.
 */
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);

 这里传递了三个参数,一是代理的类的名称com.sun.proxy.$Proxy0,一个是接口类数组,一个是类的访问标识(其实就是修饰符的数值)。

 

我们进去看下源码:

 

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        final byte[] var4 = var3.generateClassFile();
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                            Files.createDirectories(var3);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class");
                        }

                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }

        return var4;
    }

在这个方法中完成了两个功能:

 

  • final byte[] var4 = var3.generateClassFile();这个方法将生成代理类的内容,包含代理的方法,构造函数等,我们下面详细阐述。
  • if (saveGeneratedFiles),根据saveGeneratedFiles属性值判断,如果为true,将生成的代理类字节码写到文件中,并放到对应的目录。
    Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
     

我们先来看第一个方法 var3.generateClassFile():

 

private byte[] generateClassFile() {
        //将Object的三个代理方法hashCode,equals,toString放在代理方法的map中
        //Map<String, List<ProxyGenerator.ProxyMethod>> proxyMethods
        this.addProxyMethod(hashCodeMethod, Object.class);
        this.addProxyMethod(equalsMethod, Object.class);
        this.addProxyMethod(toStringMethod, Object.class);
        Class[] var1 = this.interfaces;
        int var2 = var1.length;

        int var3;
        Class var4;
        for(var3 = 0; var3 < var2; ++var3) {
            var4 = var1[var3];
            //获取代理类的接口方法
            Method[] var5 = var4.getMethods();
            int var6 = var5.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Method var8 = var5[var7];
                //循环遍历,将代理类中的接口方法也放到代理方法的map中
                this.addProxyMethod(var8, var4);
            }
        }

        Iterator var11 = this.proxyMethods.values().iterator();

        List var12;
        //循环校验代理方法Map中的方法返回类型是否合法
        while(var11.hasNext()) {
            var12 = (List)var11.next();
            checkReturnTypes(var12);
        }

        Iterator var15;
        try {
            //生成代理类的构造函数
            this.methods.add(this.generateConstructor());
            var11 = this.proxyMethods.values().iterator();

            while(var11.hasNext()) {
                var12 = (List)var11.next();
                var15 = var12.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
                    this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                    this.methods.add(var16.generateMethod());
                }
            }

            this.methods.add(this.generateStaticInitializer());
        } catch (IOException var10) {
            throw new InternalError("unexpected I/O Exception", var10);
        }

        if (this.methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        } else if (this.fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        } else {
            this.cp.getClass(dotToSlash(this.className));
            this.cp.getClass("java/lang/reflect/Proxy");
            var1 = this.interfaces;
            var2 = var1.length;

            for(var3 = 0; var3 < var2; ++var3) {
                var4 = var1[var3];
                this.cp.getClass(dotToSlash(var4.getName()));
            }

            this.cp.setReadOnly();
            ByteArrayOutputStream var13 = new ByteArrayOutputStream();
            DataOutputStream var14 = new DataOutputStream(var13);

            try {
                var14.writeInt(-889275714);
                var14.writeShort(0);
                var14.writeShort(49);
                this.cp.write(var14);
                var14.writeShort(this.accessFlags);
                var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
                var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
                var14.writeShort(this.interfaces.length);
                Class[] var17 = this.interfaces;
                int var18 = var17.length;

                for(int var19 = 0; var19 < var18; ++var19) {
                    Class var22 = var17[var19];
                    var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
                }

                var14.writeShort(this.fields.size());
                var15 = this.fields.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
                    var20.write(var14);
                }

                var14.writeShort(this.methods.size());
                var15 = this.methods.iterator();

                while(var15.hasNext()) {
                    ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
                    var21.write(var14);
                }

                var14.writeShort(0);
                return var13.toByteArray();
            } catch (IOException var9) {
                throw new InternalError("unexpected I/O Exception", var9);
            }
        }
    }

此方法中,完成的操作就是我们最后看到的代理类的字节码内容,包含属性,代理方法,代理类的构造函数等等。

代码过程图如下:

Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 

Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
 

Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
 

最后一步,将生成的代理类加载到内存中。

defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);

 

 

三、综述

以上就是JDK代理设计模式的所有内容,看过此篇,对代理类的所有过程都没有疑问。

 

 

  • Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
  • 大小: 206.9 KB
  • Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
  • 大小: 4.3 KB
  • Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
  • 大小: 8 KB
  • Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
  • 大小: 26.7 KB
  • Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
  • 大小: 6.8 KB
  • Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
  • 大小: 5.2 KB
  • Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
  • 大小: 7 KB
  • Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
  • 大小: 12.8 KB
  • Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
  • 大小: 14.3 KB
  • Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
  • 大小: 32.4 KB
  • Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
  • 大小: 36.1 KB
  • Java设计模式篇(一)--代理模式详解(这一篇足够了)
            
    
    博客分类: 设计模式 设计模式mybatisjdk代理模式动态代理 
  • 大小: 35.6 KB