Анализ исходного кода Dubbo (1) Механизм точки расширения Dubbo

задняя часть исходный код Dubbo

слова, написанные впереди

Пользуюсь Dubbo уже несколько лет и ни разу не читал исходники Dubbo Теперь буду читать исходники Dubbo, анализировать несколько ядер Dubbo и писать тему по исходникам Dubbo для записи обучения процесс для вашей справки.Если письмо не очень хорошо, добро пожаловать в Paizhuan
Тема разделена на следующие разделы:

PS: освойте следующие основы, прежде чем читать исходный код

  1. JDK-SPI
  2. Основы многопоточности/пула потоков в Java
  3. Основы Javasisit (динамическая компиляция)
  4. Основы Нетти
  5. Основы Zookeeper, клиентский API zkClient
  6. Заводской шаблон, декоративный шаблон, шаблонный шаблон, одноэлементный шаблон, динамический шаблон прокси
  7. Пользовательское расширение схемы Spring
  8. Сериализация

PS: Предложения перед чтением исходного кода

  1. Объем кода очень велик, и разные места связаны между собой, читайте медленно, не волнуйтесь, если не можете сделать один раз, сделайте это дважды, если не можете сделать это дважды, сделайте это три раза, там всегда будет время понять
  2. Посмотрите на проблему и подумайте, какова цель этого кода и какую проблему он решает.
  3. Читать по основной строке, код, не влияющий на ход процесса, можно пропустить

Точки расширения Дуббо

Зачем сначала читать точки расширения

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

Спецификация точки расширения Dubbo

  • Если вы хотите расширить пользовательский SPI, вы можете настроить три каталога в каталоге ресурсов, а именно: META-INF/dubbo/ или META-INF/services/ или META-INF/dubbo/internal/
  • Имя файла и имя интерфейса должны совпадать, а содержимое файла должно иметь вид key=vaule xxx=com.alibaba.xxx.XxxProtocol.
  • Например: если мы хотим расширить протокол Dubbo, добавьте в файл META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol строку собственного расширения: xxx=com.alibaba.xxx.XxxProtocol, в Dubbo Файл конфигурации , чтобы можно было реализовать собственный протокол Dubbo.

Разница между точкой расширения Dubbo и SPI JDK

В точку расширения Dubbo (Extension) внесены некоторые улучшения на основе идеи JDK SPI:

  • В дизайне Dubbo используется большое количество глобальных кешей, все Extensions кэшируются в cachedInstances, тип объекта ConcurrentMap
  • Точки расширения Dubbo поддерживают реализацию по умолчанию, например, @SPI("dubbo") в Protocol по умолчанию имеет значение DubboProtocol, а точку расширения по умолчанию можно получить с помощью ExtensionLoader.getExtensionLoader(Protocol.class).getDefaultExtension().
  • Точка расширения Dubbo может динамически получать объект расширения, например: ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName) для получения объекта расширения, я думаю, что это очень интересное место в дизайне точки расширения Dubbo, очень гибкий и удобный код Этот метод широко используется в
  • Точка расширения Dubbo обеспечивает функциональность АОП.В cachedWrapperClasses XxxxFilterWrapper XxxxListenerWrapper обернут исходным классом SPI.
  • Точка расширения Dubbo предоставляет функцию IOC, требуемый экземпляр внедряется через конструктор, а исходный код будет проанализирован позже.
  • Читать исходный код

    • Сначала подумайте, какова цель SPI Dubbo? Получить указанный объект, который нам нужен
    • Как получить его? ExtensionLoader.getExtension (имя строки)
      Начнем с этого кода.Функция этого кода — получение адаптивного класса расширения.Давайте посмотрим на весь процесс выполнения этого кода:
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

В методе getExtensionLoader передается протокол, посмотрим, как выглядит протокол

@SPI("dubbo")
public interface Protocol {
   //获取缺省端口,当用户没有配置端口时使用。
   int getDefaultPort();

  // 暴露远程服务:<br>
   @Adaptive
   <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

   //引用远程服务:<br>
   @Adaptive
   <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;

   //释放协议:<br>
   void destroy();

Это интерфейс протокола. В классе есть аннотация @SPI, значение по умолчанию dubbo и аннотация @Adaptive в методе. Каковы функции этих двух аннотаций? Продолжаем рассматривать метод getExtensionLoader:

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
·····
       //先从缓存中取值,为null,则去new一个ExtensionLoader
       ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
       if (loader == null) {
           EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
           loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
       }
       return loader;
   }

Продолжайте вводить новый метод ExtensionLoader(type):

private ExtensionLoader(Class<?> type) {
       this.type = type;
       objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
   }

Присвойте значение переменной типа и objectFactory. В настоящее время входящий тип — это протокол, и продолжайте выполнять метод ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension(). Здесь мы разделим его на два этапа:
Шаг 1: ExtensionLoader.getExtensionLoader(ExtensionFactory.class), в настоящее время тип ExtensionFactory.class, в этом коде мы получаем экземпляр ExtensionLoader, в этом экземпляре ExtensionLoader objetFactory имеет значение null, Шаг 2: метод getAdaptiveExtension(), присвойте значение cachedAdaptiveInstance, давайте посмотрим на этот метод:

  public T getAdaptiveExtension() {
       Object instance = cachedAdaptiveInstance.get();
       if (instance == null) {
           if (createAdaptiveInstanceError == null) {
               synchronized (cachedAdaptiveInstance) {
                   instance = cachedAdaptiveInstance.get();
                   if (instance == null) {
                       try {
                           instance = createAdaptiveExtension();
                           cachedAdaptiveInstance.set(instance);
                       } catch (Throwable t) {
                           createAdaptiveInstanceError = t;
                           throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                       }
                   }
               }
           } else {
               throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
           }
       }

Дважды проверьте блокировку, чтобы оценить кеш. Если нет, введите метод createAdaptiveExtension(). Этот метод состоит из двух частей: injectExtension и getAdaptiveExtensionClass().newInstance().

private T createAdaptiveExtension() {                                                                                            
 try {                                                                                                                        
     return injectExtension((T) getAdaptiveExtensionClass().newInstance());                                                   
 } catch (Exception e) {                                                                                                      
     throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);         
 }                                                                                                                            
}                                                                                                                                

Сначала посмотрите на getAdaptiveExtensionClass(), чтобы получить класс точки расширения адаптера.

private Class<?> getAdaptiveExtensionClass() {                                                      
 getExtensionClasses();                                                                          
 if (cachedAdaptiveClass != null) {                                                              
     return cachedAdaptiveClass;                                                                 
 }                                                                                               
 return cachedAdaptiveClass = createAdaptiveExtensionClass();                                    
}                                                                                                   

Здесь мы вводим метод getExtensionClasses(), дважды проверяем решение о блокировке, если нет, продолжаем

private Map<String, Class<?>> getExtensionClasses() {
     Map<String, Class<?>> classes = cachedClasses.get();
     if (classes == null) {
         synchronized (cachedClasses) {
             classes = cachedClasses.get();
             if (classes == null) {
                 classes = loadExtensionClasses();
                 cachedClasses.set(classes);
             }
         }
     }
     return classes;
 }

Введите метод loadExtensionClasses()

    // 此方法已经getExtensionClasses方法同步过。
    private Map<String, Class<?>> loadExtensionClasses() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if (value != null && (value = value.trim()).length() > 0) {
                String[] names = NAME_SEPARATOR.split(value);
                if (names.length > 1) {
                    throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                            + ": " + Arrays.toString(names));
                }
                if (names.length == 1) cachedDefaultName = names[0];
            }
        }

        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadFile(extensionClasses, DUBBO_DIRECTORY);
        loadFile(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }

Существует type.getAnnotation(SPI.class), этот тип передается при повторной инициализации ExtensionLoader, давайте сначала рассмотрим ситуацию type=ExtensionFactory.class, интерфейсный класс ExtensionFactory имеет аннотацию @SPI, но значение пусто, Затем трижды вызовите метод loadFile, соответствующий трем путям файла конфигурации точки расширения Dubbo.В исходном коде мы можем найти файл, соответствующий ExtensionFactory.

Через метод loadFile extensionClasses возвращает SpringExtensionFactory и SpiExtensionFactory для кэширования в cachedClasses. Почему возвращаются только 2 класса? Почему AdaptiveExtensionFactory не возвращается? Поскольку в loadFile AdaptiveExtensionFactory имеет аннотацию @Adaptive для класса, поэтому он напрямую кэшируется в cachedAdaptiveClass ( На этом этапе мы должны подумать о разнице между аннотациями @Adaptive для классов и методов), давайте посмотрим на код ключа в loadFile

private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
····
       // 1.判断当前class类上面有没有Adaptive注解,如果有,则直接赋值给cachedAdaptiveClass
       if (clazz.isAnnotationPresent(Adaptive.class)) {
           if (cachedAdaptiveClass == null) {
               cachedAdaptiveClass = clazz;
           } else if (!cachedAdaptiveClass.equals(clazz)) {
               throw new IllegalStateException("More than 1 adaptive class found: "
                       + cachedAdaptiveClass.getClass().getName()
                       + ", " + clazz.getClass().getName());
           }
       } else {
           //2.如果没有类注解,那么判断该class中没有参数是type的构造方法,如果有,则把calss放入cachedWrapperClasses中
           try {
               clazz.getConstructor(type);
               Set<Class<?>> wrappers = cachedWrapperClasses;
               if (wrappers == null) {
                   cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                   wrappers = cachedWrapperClasses;
               }
               wrappers.add(clazz);
           } catch (NoSuchMethodException e) {
               //3.判断是否有默认构造方法
               clazz.getConstructor();
               if (name == null || name.length() == 0) {
                   name = findAnnotationName(clazz);
                   if (name == null || name.length() == 0) {
                       if (clazz.getSimpleName().length() > type.getSimpleName().length()
                               && clazz.getSimpleName().endsWith(type.getSimpleName())) {
                           name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
                       } else {
                           throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
                       }
                   }
               }
               String[] names = NAME_SEPARATOR.split(name);
               if (names != null && names.length > 0) {
                   //4.判断class是否有@Activate注解,如果有,则放入cachedActivates
                   Activate activate = clazz.getAnnotation(Activate.class);
                   if (activate != null) {
                       cachedActivates.put(names[0], activate);
                   }
                   for (String n : names) {
                       //5.缓存calss到cachedNames中
                       if (!cachedNames.containsKey(clazz)) {
                           cachedNames.put(clazz, n);
                       }
                       Class<?> c = extensionClasses.get(n);
                       if (c == null) {
                           extensionClasses.put(n, clazz);
                       } else if (c != clazz) {
                           throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
                       }
                   }
               }
           }
       }
   }
   }

На данный момент мы получили extensionClasses, кэшировали их в cachedClasses и вернулись к методу getAdaptiveExtensionClass().

private Class<?> getAdaptiveExtensionClass() {
       getExtensionClasses();
       if (cachedAdaptiveClass != null) {
           return cachedAdaptiveClass;
       }
       return cachedAdaptiveClass = createAdaptiveExtensionClass();
   }

Если cachedAdaptiveClass не пуст, то возвращается cachedAdaptiveClass. Как мы только что сказали в методе loadFile(), аннотация @Adaptive находится в классе, тогда он будет кэширован в cachedAdaptiveClass. В это время cachedAdaptiveClass имеет значение, которое является AdaptiveExtensionFactory, поэтому оно возвращается непосредственно здесь. AdaptiveExtensionFactory, продолжайте возвращаться к методу createAdaptiveExtension(). Мы только что закончили часть метода createAdaptiveExtension() и метод injectExtension. Что делает этот метод? процесс type=ExtensionFactory.class, функция этого метода не отражена.Не смотрим сначала на injectExtension, давайте посмотрим на него в следующем процессе, а затем продолжаем возвращаться к методу getAdaptiveExtension, кешируем экземпляр AdaptiveExtensionFactory в cachedAdaptiveInstance , и продолжайте возвращаться к методу ExtensionLoader

private ExtensionLoader(Class<?> type) {
       this.type = type;
       objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
   }

В это время objectFactory уже имеет значение AdaptiveExtensionFactory и продолжает возвращаться к методу getExtensionLoader.

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
       ····
       //EXTENSION_LOADERS判断是否有type,ConcurrentMap<Class<?>, ExtensionLoader<?>>
       ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
       if (loader == null) {
           EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
           loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
       }
       return loader;
   }

Мы кэшируем возвращенный экземпляр ExtensionLoader в EXTENSION_LOADERS, в это время type=Protocol

ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

На данный момент мы выполнили ExtensionLoader.getExtensionLoader(Protocol.class), получили экземпляр ExtensionLoader и продолжаем выполнять метод getAdaptiveExtension(). Этот метод был проанализирован выше. Давайте посмотрим на разницу между ним и type=ExtensionFactory. .Сначала посмотрите точки расширения в файле com.alibaba.dubbo.rpc.Protocol (этот файл разбросан по исходникам и его можно найти в jar-пакете Dubbo, который слит в jar-пакет)

Всего точек расширения 13, из них классов-оболочек Wrapper 2. Непосредственно смотрим на метод loadFile, и extensionClasses возвращает 11 записей.

В это время посмотрите данные в текущей памяти

В cachedNames есть 11 элементов, 2 элемента в cachedWrapperClasses — это ProtocolListenerWrapper и ProtocolFilterWrapper, а в cachedClasses — 11 элементов. Вернитесь к методу getAdaptiveExtensionClass(). Как мы уже говорили выше, в протоколе есть аннотации @SPI («dubbo»). классы. , есть аннотации @Adaptive для экспорта и ссылки, поэтому cachedAdaptiveClass в настоящее время имеет значение null, , введите метод createAdaptiveExtensionClass(), целью этого метода является автоматическое создание и компиляция класса адаптера динамического прокси с именем Protocol$Adaptive, и здесь используется точка расширения Compile. Вы можете видеть, что ExtensionLoader.getExtensionLoader(com. alibaba .dubbo.common.compiler.Compiler.class).getAdaptiveExtension(), некоторые из вас хорошо знакомы с ним, вот AdaptiveCompiler (потому что в классе AdaptiveCompiler есть аннотация @Adaptive)

private Class<?> createAdaptiveExtensionClass() {
   //生成字节码文件
   String code = createAdaptiveExtensionClassCode(); 
   //获得类加载器
   ClassLoader classLoader = findClassLoader(); 
   com.alibaba.dubbo.common.compiler.Compiler compiler = 
   ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
   //动态编译
   return compiler.compile(code, classLoader);                         
}                                                                                                                                           

Выполните компилятор.compile(код, classLoader), сначала посмотрите на класс AdaptiveCompiler

@Adaptive
public class AdaptiveCompiler implements Compiler {

   private static volatile String DEFAULT_COMPILER;

   public static void setDefaultCompiler(String compiler) {
       DEFAULT_COMPILER = compiler;
   }

   public Class<?> compile(String code, ClassLoader classLoader) {
       Compiler compiler;
       ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
       String name = DEFAULT_COMPILER; // copy reference
       if (name != null && name.length() > 0) {
           compiler = loader.getExtension(name);
       } else {
           compiler = loader.getDefaultExtension();
       }
       return compiler.compile(code, classLoader);
   }
}

Здесь значением DEFAULT_COMPILER является JavassistCompiler, выполните loader.getExtension(name), этот метод здесь временно не раскрывается, результатом является получение экземпляра JavassistCompiler, вот дизайн режима оформления и, наконец, вызов JavassistCompiler.compile() метод получения Protocol$Adpative,

Вернемся к точке входа нашего исходного кода

ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

Окончательный результат этого кода — Protocol$Adpative, давайте взглянем на этот прокси-класс.

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
  public void destroy() {throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
  }
  public int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
  }
  public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
      if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
      if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
      String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
      if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
      com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
      return extension.export(arg0);
  }
  public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
      if (arg1 == null) throw new IllegalArgumentException("url == null");
      com.alibaba.dubbo.common.URL url = arg1;
      String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
      if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
      com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
      return extension.refer(arg0, arg1);
  }
}

В настоящее время, если выполняется метод Protocol$Adpative.export, давайте взглянем на метод export() в этом прокси-классе адаптера и получим extName через URL-адрес, чтобы Dubbo управлялся на основе URL-адреса, Увидев метод Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName), знаком ли вам этот метод? Далее давайте проанализируем метод getExtension(String name), предполагая, что extName на данный момент =dubbo

public T getExtension(String name) {
        if (name == null || name.length() == 0)
            throw new IllegalArgumentException("Extension name == null");
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<Object>());
            holder = cachedInstances.get(name);
        }
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

Введите метод createExtension()

private T createExtension(String name) {
        //1.通过name获取ExtensionClasses,此时为DubboProtocol
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
            //2.获取DubboProtocol实例
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
                EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            //3.dubbo的IOC反转控制,就是从spi和spring里面提取对象赋值。
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (wrapperClasses != null && wrapperClasses.size() > 0) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    //4.如果是包装类
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }

Третий шаг injectExtension(instance), посмотрите на код:

private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
                //1.拿到所有的方法
                for (Method method : instance.getClass().getMethods()) {
                    //判断是否是set方法
                    if (method.getName().startsWith("set")
                            && method.getParameterTypes().length == 1
                            && Modifier.isPublic(method.getModifiers())) {
                        Class<?> pt = method.getParameterTypes()[0];
                        try {
                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                            //从objectFactory中获取所需要注入的实例
                            Object object = objectFactory.getExtension(pt, property);
                            if (object != null) {
                                method.invoke(instance, object);
                            }
                        } catch (Exception e) {
                            logger.error("fail to inject via method " + method.getName()
                                    + " of interface " + type.getName() + ": " + e.getMessage(), e);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }

В этом методе Dubbo завершает внедрение зависимостей, здесь завершается анализ кода механизма точки расширения Dubbo.

Суммировать

  • Почему адаптивный дизайн?
    Adaptive предназначен для идентификации фиксированных известных классов и расширенных неизвестных классов.
  • В чем разница между аннотациями к классам и аннотациями к методам?
    1. Аннотация к классу: представляет собой искусственную реализацию, реализует класс оформления (режим оформления в режиме разработки), который в основном действует на фиксированные известные классы, В настоящее время во всей системе есть только две системы: AdaptiveCompiler и AdaptiveExtensionFactory.
    • Почему этот класс AdaptiveCompiler известен постоянно? Потому что весь фреймворк поддерживает только Javassist и JdkCompiler.
    • Почему этот класс AdaptiveExtensionFactory постоянно известен? Поскольку весь фреймворк поддерживает только 2 objFactory, один — spi, а другой — spring.
      2. Аннотация к методу: он представляет собой автоматическую генерацию и компиляцию динамического класса Adpative, который в основном используется для SPI, поскольку класс spi является нефиксированным и неизвестным классом расширения, поэтому разработан динамический класс XXX$ Adaptive. . Например, класс spi протокола имеет прослушиватель фильтра реестра injvm dubbo и т. д. Многие расширенные неизвестные классы, Он разрабатывает класс Protocol$Adaptive и извлекает объекты с помощью ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(spi class);