`

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

阅读更多

在设计模式中,代理模式是经常会用到的一种模式。但是光说设计模式,没有实际意义。在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类图就明确了,如下图:

 


 

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();
    }
}

 ⑤结果截图:

 


 

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。(因个人项目环境而已)


 

 

②直接使用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\\如果没有就需要手动新建):


 

按照上述两种方式,都可以获取到生成的代理类,那么内容是什么?当然,生成的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存在,那么说明接口数组中传递的接口类参数中有重复。


 

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

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

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


 这里获取代理类的名称,因为并发情况下可能会存在多个代理类,所以这里使用了原子类,保证唯一,因为我们是第一次生成,因此代理类的名称连同包名为:com.sun.proxy.$Proxy0

 

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

 

/*
 * 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,将生成的代理类字节码写到文件中,并放到对应的目录。

     

我们先来看第一个方法 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);
            }
        }
    }

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

代码过程图如下:




 


 

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

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

 

 

三、综述

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

 

 

  • 大小: 206.9 KB
  • 大小: 4.3 KB
  • 大小: 8 KB
  • 大小: 26.7 KB
  • 大小: 6.8 KB
  • 大小: 5.2 KB
  • 大小: 7 KB
  • 大小: 12.8 KB
  • 大小: 14.3 KB
  • 大小: 32.4 KB
  • 大小: 36.1 KB
  • 大小: 35.6 KB
分享到:
评论

相关推荐

    java设计模式—图解-附有代码

    ### Java设计模式详解——附有代码与图解 #### 一、设计模式概述 设计模式是一种在特定场景下解决问题的最佳实践。在软件工程领域,设计模式可以帮助开发人员编写出易于维护、扩展性强的代码。Java设计模式主要包括...

    C++与java设计模式分开详解

    "java设计模式"这个文件可能包含了关于Java中设计模式的详细讨论,涵盖了各种模式的用途、优缺点以及如何在Java项目中有效地应用它们。你可能还会找到一些实用的代码示例,帮助你快速掌握这些模式。 总结来说,设计...

    java设计模式 pdf

    ### Java设计模式详解 在软件开发领域,设计模式是一种被广泛认可且经验证有效的解决方案,它们可以帮助开发者解决在设计过程中遇到的各种常见问题。本篇文章将详细介绍几种常见的Java设计模式及其背后的原理,包括...

    Java设计模式详解(含图和代码)

    本篇文档将深入解析Java设计模式,并结合图表和代码示例进行详细讲解。 首先,我们来看创建型模式。这类模式主要关注对象的创建过程,减少类与类之间的耦合。 1.1 工厂方法模式:它定义了一个创建对象的接口,但让...

    java设计模式.docx

    ### Java设计模式之六大创建型模式详解 #### 引言 设计模式是在软件工程领域内,为了解决特定类型的问题而形成的解决方案模板。在《Java设计模式》中,重点介绍了六大创建型设计模式,这些模式提供了不同的方法来...

    23种设计模式整理pdf

    设计模式详解 设计模式是软件开发中的一种解决方案,旨在提高代码的可重用性、可维护性和可扩展性。在这篇文章中,我们将对23种常见的设计模式进行整理和解释。 1. Singleton 模式 Singleton 模式是一种创建型模式...

    Java设计模式中单一职责原则详解.rar

    其中,"单一职责原则"(Single Responsibility Principle,SRP)是面向对象设计的基本原则之一,也是Java设计模式中的重要组成部分。本篇文章将深入探讨单一职责原则的概念、意义、应用及其在Java编程中的实际运用。...

    java设计模式

    ### Java设计模式详解 #### 一、概述 Java设计模式是一种在特定上下文中解决软件设计问题的方案。设计模式能够帮助开发人员编写出更灵活、更易于维护的代码。本篇文档将详细介绍几种常见的Java设计模式,包括工厂...

    java 多线程编程实战指南(核心 + 设计模式 完整版)

    《Java多线程编程实战指南》这本书深入浅出地讲解了Java多线程的核心概念和实战技巧,分为核心篇和设计模式篇,旨在帮助开发者掌握并应用多线程技术。 1. **线程基础** - **线程的创建**:Java提供了两种创建线程...

    Java多线程设计模式详解

    从给出的内容来看,这篇文章主要讨论了关于Java多线程设计模式的书籍,而这本书则由结城洛著作、博硕文化翻译。尽管用户在描述中提到,能找到的相关多线程书籍相对较少,并且对于这本由日本人著的书表示出一定的遗憾...

    JAVA.BUG模式详解

    在Java编程中,"BUG模式"通常指的是程序员在编写代码时容易犯的一些常见错误或陷阱,这些错误可能导致程序运行不正常、性能下降甚至...《JAVA.BUG模式详解》这本书将深入讲解这些主题,帮助你成为更出色的Java程序员。

    关于Java 23种设计模式的有趣见解

    在网络上流畅很广的一篇旧文,暂时没找到原作者,目前所看到的最早转载时间是 2005 年 2 月 28 日。...(更新:已找到一篇发布时间是 2003 年 11 月20 日发布的文章,原标题是《追MM与设计模式(java的32种设计模式)》

    Java.Bug模式详解

    在Java编程中,"Bug模式"是指在实践中常见的编程错误或陷阱,这些错误可能导致程序运行异常、性能下降或者...详细内容请参考提供的《Java.Bug模式详解.pdf》文档,深入学习和理解每个模式的成因、影响以及解决方案。

    Java面试宝典(设计模式,算法,Linux篇)

    **代理模式**(Proxy模式)是一种结构型设计模式,它为另一个对象提供代理或占位符,以便控制对该对象的访问。 - **目的**:控制对某个对象的访问,通常用于实现更复杂的控制机制。 - **应用场景**: - 实现权限...

    Java经典设计模式(1)五大创建型模式(附实例和详解)J

    Java设计模式是软件工程中的一种最佳实践,它们是解决常见编程问题的经验总结,可以提高代码的可读性、可维护性和可扩展性。本篇主要关注的是创建型设计模式,这是设计模式中的一个重要类别,主要处理对象的创建过程...

    Java语言程序设计-进阶篇

    - **设计模式**:设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。例如单例模式、工厂模式等。 - **性能优化**:包括内存管理、垃圾回收机制的理解以及避免内存泄漏等问题。 - **单元...

    java毕业设计&课设-奥多停车小程序.zip

    本篇将深入探讨一个名为“奥多停车小程序”的Java项目,该项目包含了完整的源码、项目部署视频和相关资料,旨在帮助学生理解和实践Java开发流程。 首先,我们要明确的是,这个项目的核心是开发一个停车管理应用。...

    java自学必读书目

    - **主要内容:** 这是设计模式领域的奠基之作。 - **涵盖知识点:** - 设计模式的定义 - 设计原则 - 创建型模式 - 结构型模式 - 行为型模式 - 模式的应用案例分析等。 #### 六、敏捷开发与测试驱动开发篇 ...

Global site tag (gtag.js) - Google Analytics