Анализ исходного кода динамического прокси

Java
Анализ исходного кода динамического прокси

предисловие

Динамический прокси - очень важная идея в Java. В этой статье будет представлен динамический прокси и анализ исходного кода прокси от мелкого до глубокого. Статья не обязательно верна. Пожалуйста, прочитайте эту статью с критическим отношением.

Режим прокси является одним из режимов проектирования, и он очень часто используется в реальной разработке, например, мы часто используем Spring AOP и прокси mybatis.

Определение режима прокси:

Когда к объекту нельзя получить прямой доступ или доступ к объекту затруднен, к нему можно получить косвенный доступ через прокси-объект.Чтобы обеспечить прозрачность использования клиента, реальный объект, к которому осуществляется доступ, и прокси-объект должны быть реализованы одинаково интерфейс.

статический прокси

Из приведенного выше введения видно, что режим прокси на самом деле является инструментом для решения проблем с доступом или обеспечения прозрачности.Это похоже на тот факт, что мы не можем получить доступ к Google и нуждаемся в помощи промежуточного агента.Простой пример для введения:

public class ProxyDemo {

    static interface BaseUser{
        void info();
        void real();
    }

    static class ProxyUser implements BaseUser{
        BaseUser baseUser;
        public ProxyUser(BaseUser baseUser) {
            this.baseUser = baseUser;
        }
        public void info() {
            System.out.println("I'm Proxy,I can help you");

        }
        public void real() {
            System.out.println("I will help you visit google");
            baseUser.real();
            System.out.println("I had help you visit google");
        }

    }
    static class TargetUser implements BaseUser{
        public void info() {
            System.out.println("I'm google,what you what do?");
        }
        public void real() {
            System.out.println("I.m google,this is searched info");
        }
    }

    public static void main(String[] args) {
        BaseUser targetUser = new TargetUser();
        BaseUser proxyUser = new ProxyUser(targetUser);
        proxyUser.info();
        proxyUser.real();
    }

}

Здесь мы также можем думать, что агент является носителем для посещения или взаимодействия двух сторон, и мы должны быть хорошо знакомы с обеими сторонами, чтобы помочь вам делать определенные вещи, как если бы мне нужно было купить сейчас от моего имени. , мне может понадобиться найти нового агента!!!

Вот что мы называемстатический прокси

Хотя статический прокси также может помочь мне реализовать некоторые функции, могу сказать только, что он недостаточно мощный.В настоящее время мы можем использоватьДинамический проксичтобы помочь нам быть более гибкими搞事情

Динамический прокси

Преимущества динамических прокси:

1. 降低各个功能模块之间的耦合度,提高开发的效率和方便程序的维护度。
2. 减少代码量。
3. 不关注目标的具体实现。

Реализация динамических прокси

Динамический прокси JDK

Динамический прокси, поставляемый с jdk, в основном реализован путем реализации InvocationHandler.

Основной метод InvocationHandler

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

Обработайте вызов метода экземпляра прокси и верните результат. Этот метод вызывается в обработчике вызовов, когда метод вызывается в экземпляре прокси, связанном с этим методом. То есть в этот метод вызова будет входить метод, который вызывает реальное дело, а почему, я объясню позже.

Подробности метода

  • Параметры: proxy — объект экземпляра прокси, который вызывает метод метода — объект экземпляра метода интерфейса, вызываемый объектом экземпляра прокси.
  • Метод — относится к методу, который специально делегирован. args — содержит аргументы, переданные в вызов метода экземпляра прокси, или значение null, если метод интерфейса не принимает аргументов.

  • return: значение, возвращаемое вызовом метода экземпляра прокси.

  • throws: Throwable — Исключения, возникающие при вызовах методов экземпляра прокси.

кейс

Этот случай демонстрирует работу наиболее часто используемого метода перехвата, а затем регистрации.

3.1 Бизнес-интерфейс

public interface Base {
    public void hello(String name);
}

3.2 Класс бизнес-реализацииLoginImpl

public class LoginImpl implements Base{
    @Override
    public void hello(String name) {
        System.out.println("welcome "+name+", success !!1");
    }
}

3.3 Класс проксиLoginProxy

class DynamicProxy implements InvocationHandler {

        Object originalObj;

        Object bind(Object originalObj) {
            this.originalObj = originalObj;
            return Proxy.newProxyInstance(originalObj.getClass().getClassLoader(), originalObj.getClass().getInterfaces(), this);
        }

    /**
     * 切入点 对所有对象的方法都进行调用
     * method.invoke方法对应代理对象调用login方法
     * @param proxy 代理对象
     * @param method 代理对象的方法
     * @param args  代理对象调用接口方法的参数值
     * @return 代理对象调用方法的返回值
     * @throws Throwable
     */
  @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    before();
    Object invoke = method.invoke(originalObj, args);
    if (invoke != null){
        result(invoke);
    }
    after();

  return invoke;
  }

  private void before() {
  System.out.println("方法执行之前");
  }
  private void after() {
  System.out.println("方法执行之后");
  }
  private void result(Object o) {
  o.toString();
  }


}

3.4 Класс испытанийLoginClient

public class LoginClient {
    public static void main(String[] args) {
  //用于生成代理文件      //System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Base hello = (Base) new DynamicProxy().bind(new LoginImpl());
        hello.hello("zhangsan");
    }
}

3.5 Результаты выполнения:

方法执行之前
Hello zhangsan
方法执行之后

Из приведенного выше примера мы видим, что динамический прокси-сервер эффективно уменьшает связь каждого модуля, а код, используемый для реализации функции журнала, и код, используемый для реализации функции входа в систему, изолированы друг от друга. Для обоих условий нет, только когда дело действительно вызывается и требуется функция журнала, они связаны. Любому бизнесу, которому нужна функция журнала, нужно только создать прокси-объект через прокси-класс, и нет необходимости повторно создавать прокси-класс.

принцип

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");Раскомментируйте этот метод, мы сгенерируем файл прокси после запуска или компиляции кода, по умолчанию это папка с тем же именем, что и имя пакета в корневом каталоге проекта. Давайте посмотрим на декомпилированное содержимое сгенерированного прокси-класса:


import ProxyDemo.Base;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
//$Proxy0是生成代理的格式决定的
final class $Proxy0 extends Proxy implements Base {
  //将基础的tostring,equils,hashcode,还有base接口的方法生成method的对象
    private static Method m1;
    private static Method m2;
    private static Method m4;
    private static Method m3;
    private static Method m0;

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

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void hello(String var1) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void out() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    //具体的实现
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("ProxyDemo$Base").getMethod("hello", Class.forName("java.lang.String"));
            m3 = Class.forName("ProxyDemo$Base").getMethod("out");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

Прежде всего, это легко понять из отношения наследования классов
Proxy.newProxyInstance(originalObj.getClass().getClassLoader(),originalObj.getClass().getInterfaces(), this);метод работает.

То есть наследование прокси-класса также реализует интерфейс Base.Base hello = (Base) new DynamicProxy().bind(new LoginImpl());Это также причина, по которой его можно привести к базовому объекту.В то же время, эквиваленты, tostring, hashcode и все базовые методы интерфейса в объектном классе генерируются в прокси для соответствующих методов прокси.

Возьмем в качестве примера метод hello, h представляет InvocationHandler в прокси-классе, который на самом деле ссылается на наш предыдущий объект DynamicProxy, а затем вызовите метод вызова, чтобы вернуться к методу вызова DynamicProxy. Мы можем снова выполнить множество промежуточных операций. .


    public final void hello(String var1) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

Прокси интерпретация

После прочтения содержимого прокси-класса нам нужно подробно рассмотреть, как Proxy генерирует прокси-класс $Proxy0, Узнайте о рабочем процессе и принципах.

Сначала взгляните на метод 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);
        }
        //查找或者生成代理类
        Class<?> cl = getProxyClass0(loader, intfs);

        //生成构造函数
        try {
            if (sm != null) {
   //检查是否有权限
  checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            //public $Proxy0(InvocationHandler var1)
            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);
        }
    }

Конкретная логика реализации находится в методе getProxyClass0, самый внешний метод просто описывает создание соответствующего прокси-объекта после создания прокси. Сначала посмотрите на конкретное содержимое метода checkProxyAccess.

    //主要作用检查权限是否可以操作
    private static void checkProxyAccess(Class<?> caller, ClassLoader loader, Class<?>... interfaces) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            ClassLoader ccl = caller.getClassLoader();
            //classloader验证
            if (VM.isSystemDomainLoader(loader) && !VM.isSystemDomainLoader(ccl)) {
                sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
            }
            //
            ReflectUtil.checkProxyPackageAccess(ccl, interfaces);
        }
    }
    //检查接口的包权限
    public static void checkProxyPackageAccess(ClassLoader var0, Class... var1) {
        SecurityManager var2 = System.getSecurityManager();
        if (var2 != null) {
            Class[] var3 = var1;
            int var4 = var1.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                Class var6 = var3[var5];
                ClassLoader var7 = var6.getClassLoader();
                if (needsPackageAccessCheck(var0, var7)) {
                    checkPackageAccess(var6);
                }
            }
        }

    }

После проверки разрешений посмотрите, как получить метод getProxyClass0 прокси-класса.

 private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        //从缓存中获取,如果不存在就创建
        return proxyClassCache.get(loader, interfaces);
    }

Целью использования proxyClassCache для кэширования является повторное использование и предотвращение повторного создания нескольких потоков. Несколько карт используются для записи в классе weekCache, о котором мы подробно расскажем позже.

//获取或生成代理类 此处因为不是线程安全的做了多次判断
    public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);
        //删除过期条目
        expungeStaleEntries();
        //创建cacheKey
        Object cacheKey = CacheKey.valueOf(key, refQueue);

        //查看key是否已经存在valuemaps中
        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;
            }
        }
        //生成代理对象的key 为弱引用类型
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        //尝试从valuemap中获取
        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) {
                    //最终返回value
                    return value;
                }
            }
            // 不存在创建一个supplier factory实现了supplier
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }


            if (supplier == null) {
                //如果不存在则保存到valuemap中
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // 添加成功
                    supplier = factory;
                }
                // 创建的时候发现已经有了,尝试替换
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    //替换成功
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

Метод get сначала проверяет, есть ли срок действия кеша, и очищает его, если он существует.Если он не существует, попробуйте сгенерировать корреляцию между ключом и значением元数据,

Ниже описан метод генерации ключа. Метод KeyFactory.apply.

//根据接口个数的不同选择生成不同的key对象
  public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
      switch (interfaces.length) {
          case 1: return new Key1(interfaces[0]); // the most frequent
          case 2: return new Key2(interfaces[0], interfaces[1]);
          case 0: return key0;
          default: return new KeyX(interfaces);
      }
  }

Затем, когда судите, есть ли другие потоки, созданные в то же время, попробуйте сохранить добавленную информацию, если она уже есть, попробуйте заменить ее.supplier.get()способ получения,
Фактическая логика в концеsupplier.get()В методе давайте посмотрим на конкретный процесс

public synchronized V get() { // serialize access
     // 再次检查是否匹配
     Supplier<V> supplier = valuesMap.get(subKey);
     if (supplier != this) {
         //因为此方法调用之前有可能发生valuesMap.replace(subKey, supplier, factory)
         return null;
     }
     // 创建
     V value = null;
     try {
         //真正的逻辑,重点方法
         value = Objects.requireNonNull(valueFactory.apply(key, parameter));
     } finally {
         if (value == null) { 
             // 如果最终没能生成代理对象,从valuemap移除
             valuesMap.remove(subKey, this);
         }
     }
     // the only path to reach here is with non-null value
     assert value != null;

     //包装value为acacheValue
     CacheValue<V> cacheValue = new CacheValue<>(value);

     // 保存到reverseMap
     reverseMap.put(cacheValue, Boolean.TRUE);

     // 尝试这替换valuemap中的cacheValue
     if (!valuesMap.replace(subKey, this, cacheValue)) {
         throw new AssertionError("Should not reach here");
     }
     return value;
 }

Метод значения ProxyClassFactory.apply подробно описан ниже.

 //apply方法详解
  public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

      Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
      for (Class<?> intf : interfaces) {
          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");
          }
          //验证是否为接口
          if (!interfaceClass.isInterface()) {
              throw new IllegalArgumentException(
                      interfaceClass.getName() + " is not an interface");
          }
           //验证接口不是重复的
          if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
              throw new IllegalArgumentException(
                      "repeated interface: " + interfaceClass.getName());
          }
      }

      String proxyPkg = null;
      //修饰符
      int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

          /*
           * 验证接口的可见性
           * 如果不是public类型的接口又不在同一个包下抛出异常
           */
      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;
              }
              //如果不是public类型的接口又不在同一个包下抛出异常
              else if (!pkg.equals(proxyPkg)) {
                  throw new IllegalArgumentException(
                          "non-public interfaces from different packages");
              }
          }
      }

      if (proxyPkg == null) {
          // 没有包使用默认的包 com.sun.proxy
          proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
      }

          /*
           * 代理类的名称 按顺序递增 => $proxy0
           */
      long num = nextUniqueNumber.getAndIncrement();
      String proxyName = proxyPkg + proxyClassNamePrefix + num;

          /*
           * 生成代理类的字节数组
           */
      byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
              proxyName, interfaces, accessFlags);
      try {
          //调用native方法生成Class
          return defineClass0(loader, proxyName,
                  proxyClassFile, 0, proxyClassFile.length);
      } catch (ClassFormatError e) {
          throw new IllegalArgumentException(e.toString());
      }
  }
}

Основные шаги следующие

1. Попытка загрузить интерфейс с помощью существующего загрузчика классов, в случае успеха

2. Проверить, является ли это интерфейсом и повторяется ли интерфейс, в случае успеха

3. Подтвердить права доступа к интерфейсу, в случае успеха

4. Получите информацию о пакете и настройки имени класса,

5. Сгенерируйте байтовый массив прокси

6. Получите конкретный класс номера байта с помощью собственного метода defineClass0.

Здесь мы сосредоточимся на том, как создать массив байтов.

 //生成代理类
    public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        /**
         * 生成具体文件字节数组
         * 1.找到所有接口的方法
         * 2.添加object类的三个方法 tostring hashcode equils
         * 3.遍历生成具体的代理方法,代理方法的逻辑都想似,回调我们的代理类
         */
        final byte[] var4 = var3.generateClassFile();
        // private static final boolean saveGeneratedFiles = GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();
        //这就是我们为什么设置sun.misc.ProxyGenerator.saveGeneratedFiles = true的原因,设置后就会生成代理类的文件
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
                            //生成path 将.替换成系统文件分隔符
                            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;
    }

Основной метод заключается в создании массива байтов через объект ProxyGenerator.Конкретные шаги генерации могут быть следующими:

1.找到所有接口的方法
2.添加object类的三个方法 tostring hashcode equils
3.遍历生成具体的代理方法,代理方法的逻辑都想似,回调我们的代理类

Я могу думать об общем процессе с помощью метода прокси, показанного ранее.

 public final void hello(String var1) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

Основное отличие заключается в имени метода, параметрах метода и параметрах метода вызова. Все остальное почти одинаково. В этой статье не будет подробного введения в код.

Затем решите, генерировать ли прокси-файлы на диск по значению sun.misc.ProxyGenerator.saveGeneratedFiles.

При генерации генерируется информация о пакете, информация о классе, а затем массив байтов записывается в файл.По умолчанию и под проектом создается папка с именем пакета и прокси-файл $proxy+i.

тайник

Из нашего кода видно, что WeekCache использует несколько карт для записи.

//cachekey的引用队列,于JVM关系密切详细介绍请看这篇文章http://blog.csdn.net/u012332679/article/details/57489179
private final ReferenceQueue<K> refQueue
        = new ReferenceQueue<>();
    // 最外层map,key=>cacheKey,value => valueMap
    private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
        = new ConcurrentHashMap<>();
    //记录保存value的Supplier对象map   
    private final ConcurrentMap<Supplier<V>, Boolean> reverseMap
        = new ConcurrentHashMap<>();
     //key生成对象
    private final BiFunction<K, P, ?> subKeyFactory;
    //value生成对象
    private final BiFunction<K, P, V> valueFactory;

Ключ в карте — это cacheKey, который является объектом типа слабого приложения.
Ключ reverseMap — это cacheValue, который также является объектом типа слабого приложения.
Оба также являются основными объектами для восстановления памяти.При сбое ключа в карте метод expungeStaleEntries будет запущен при следующем выполнении методов get, containsValue и size, а затем значение будет очищено от reverseMap, и карта значений будет удалена из reverseMap.Очищена на карте.Переработка refQueue опрашивается ReferenceHandler в ссылке, которая будет переработана.Если она переработана, refQueue.poll будет успешно запущен, а затем я хочу очистить операция.

private void expungeStaleEntries() {
    CacheKey<K> cacheKey;
    while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) {
      cacheKey.expungeFrom(map, reverseMap);
    }
}
void expungeFrom(ConcurrentMap<?, ? extends ConcurrentMap<?, ?>> map,
                         ConcurrentMap<?, Boolean> reverseMap) {
  // removing just by key is always safe here because after a CacheKey
  // is cleared and enqueue-ed it is only equal to itself
  // (see equals method)...
  ConcurrentMap<?, ?> valuesMap = map.remove(this);
  // remove also from reverseMap if needed
  if (valuesMap != null) {
    for (Object cacheValue : valuesMap.values()) {
      //移除弱应用CacheValue
      reverseMap.remove(cacheValue);
    }
  }
}

valueMap предназначен для записи информации, связанной с реальным прокси-классом.
key => subKeyFactory.apply(ключ, параметр) состоит из classLoader и interface[]
значение=> поставщик=>Фабрика или CacheValue

Две формы значения в карте значений
1. Это фабричный объект, когда он только что создан
2. После выполнения метода factory.appplay он будет заменен на CacheValue, а CacheValue будет сохранен в reverseMap

резюме

Общая логика такова: при чтении исходного кода реализация динамического прокси в JDK становится намного понятнее. Кроме того, у нас есть более глубокое понимание процесса реализации динамического прокси, мы можем попытаться подумать о конкретной реализации Spring AOP в это время. В этой статье не анализируются и не обобщаются подробности создания кэша и прокси-сервера, поэтому необходимы дальнейшие исследования.

Эпилог

В связи с ограниченными возможностями отдельных лиц, в статье есть ошибки или ценные мнения, пожалуйста, оставьте комментарий.

Позже я напишу статьи о Spring AOP и динамическом прокси cglib.

Поделиться с тобой! !