Я не понимаю механизм Java SPI, как войти в большую фабрику

задняя часть
Я не понимаю механизм Java SPI, как войти в большую фабрику

Публичный номер: Технические примечания Му Фэн

У настоящего мастера всегда сердце ученика

введение

В ежедневной разработке проектов, чтобы улучшить масштабируемость программы, мы часто используем для программирования идеи интерфейсно-ориентированного программирования. Это не только отражает принцип программирования, согласно которому программирование закрыто для модификации и открыто для расширения, но также реализует подключаемость программы. Тогда механизм SPI, описанный в этой статье, является воплощением этой идеи программирования. Давай поговорим с тобой сегодняSPIЧто за чертовщина. Давайте посмотрим со всеми вамиSeataКак использовать фреймворкSPIМеханизм достижения расширения фреймворка.

image.png

Что такое СПИ

В общей логике разработки поставщик службы определяет интерфейс и реализует различные реализации, а вызывающая служба передаетAPIспособ завершить деловой звонок. Однако этому методу не хватает гибкости для вызывающих службу, и он не может загружать различные реализации в соответствии с их собственными потребностями. Итак, существует ли механизм, дающий звонящему больше полномочий для принятия решений? В это время состоится грандиозный дебют сегодняшнего главного героя SPI.

SPI(Service Provider Interface), интерфейс поставщика услуг. Это звучало немного расплывчато, и я не знал, что это значит. Насколько я понимаю, это механизм обнаружения службы. Суть его в том, чтобы отделить интерфейс от реализации. Отличается от определения интерфейса, предоставленного разработчиком службы.APIСпособ,SPIВызывающий службу должен сделать объявление интерфейса, а конкретная реализация реализуется третьей стороной. Проще говоря,SPIЭто партия А в жизни, вы, партия Б, должны работать в соответствии с моими требованиями, если вы хотите сотрудничать со мной. Таким образом, вызывающий объект имеет больше гибкости и может загрузить квалифицированную реализацию в соответствии со своими фактическими потребностями. Следовательно, улучшается масштабируемость программы, и поставщик услуг может запрограммировать интерфейс.

Как использовать такой замечательный механизм расширения? нам просто нужноjarупаковкаMETA-INF/services/В каталоге также создается файл с именем интерфейса службы. Этот файл является именем конкретного класса реализации, реализующего интерфейс службы. Когда внешняя программа собирает этот модуль, она может передатьjarСумкаMETA-INF/services/В файле конфигурации найдите конкретное имя класса реализации и загрузите экземпляр, чтобы завершить загрузку и внедрение класса реализации.

Пользователь предоставляет описание правила, а фактический поставщик услуг завершает конкретную реализацию. На самом деле эта идея иSpringСканирование компонентов аналогично, сначала указываются все правила, а поставщик услуг позволяет платформе автоматически выполнять обнаружение сервисов в соответствии со спецификацией.

image.png

Ключевой момент здесь, точка знаний здесь, и по доске стучат. С тех пор мы можем обнаружить, что если в этой статье говорится оSPI,ещеSpringBootПринцип автоматической настройки на самом деле является идеей разработки, что соглашение больше, чем конфигурация.Конкретная реализация осуществляется через предварительно согласованный контент, тем самым улучшая масштабируемость программы. Поэтому я надеюсь, что когда вы смотрите на технологию, помимо внимания к техническим деталям и вертикальному пониманию, вы также должны обращать внимание на горизонтальное сравнение технологий, чтобы найти общие черты этих технологий и понять лежащие в их основе дизайнерские идеи. Я всегда чувствовал, что это очень важно, ведь движения всегда меняются, но развитие внутренней силы важнее. Вероятно, это происходит в истории Йитяня и Убийцы драконов.

Анализ реализации SPI

1. Использование СПИ

кMysqlВозьмите загрузку драйвера в качестве примера, сначала определите интерфейс шаблона, который необходимо расширить, т.е.java.sql.Driverинтерфейс. Каждый производитель базы данных может разработать соответствующий драйвер в соответствии с характеристиками своей собственной базы данных, но должен следовать этому шаблону интерфейса.

image.png

В стороннем пакете драйвера Mysql в егоClasspath 路径下的В каталоге META-INF/services/ создайте файл с полным именем интерфейса службы. Содержимое, сохраненное в этом файле, представляет собой полное имя конкретного класса реализации интерфейса шаблона.

image.png

Реализуйте определенные классы в соответствующих каталогах, и эти классы реализации будут реализованыjava.sql.Driverинтерфейс.

image.png

Конкретный код реализуетсяServiceLoaderЗагрузите соответствующий класс реализации и завершите создание экземпляра класса. Конечно, этот ServiceLoader также может быть определен сам по себе, напримерDubbo,SeataТакие фреймворки определяют свои собственные загрузчики классов.


public final class ServiceLoader<S>
    implements Iterable<S>
{
  
   private static final String PREFIX = "META-INF/services/";
     ...
   public static <S> ServiceLoader<S> load(Class<S> service,
                                            ClassLoader loader)
{
        return new ServiceLoader<>(service, loader);
    }

  public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

   private ServiceLoader(Class<S> svc, ClassLoader cl) {
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        reload();
    }
    ...
}

Давайте проанализируем рабочий процесс этого сервисного загрузчика, сначалаServiceLoader.load()загрузить. Сначала получите текущую привязку потокаClassLoader, если текущий поток связанClassLoaderзаnull, затем используйте SystemClassLoaderзаменить, а затем очиститьproviderкэш и, наконец, создать LazyIterator.LazyIteratorЧасть исходного кода выглядит следующим образом:

private class LazyIterator implements Iterator<S>
    {

        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        Iterator<String> pending = null;
        String nextName = null;


  ...
  
   public boolean hasNext() {
            if (acc == null) {
                return hasNextService();
            } else {
                PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                    public Boolean run() { return hasNextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }
  ...
  
  private boolean hasNextService() {
            if (nextName != null) {
                return true;
            }
            if (configs == null) {
                try {
                  //key:获取完全限定名
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x);
                }
            }
            while ((pending == null) || !pending.hasNext()) {
                if (!configs.hasMoreElements()) {
                    return false;
                }
                pending = parse(service, configs.nextElement());
            }
            nextName = pending.next();
            return true;
        }
        ...


}

key: укажите конкретный адрес класса через предопределенный адрес каталога и имя класса, и загрузчик класса загрузит конкретный класс реализации в соответствии с этим адресом.

Примерный процесс загрузки SPI выглядит следующим образом:

image.png

Как Seata использует SPI

SeataЭто фреймворк распределенных транзакций. Конкретное использование не будет здесь повторяться. Если у вас есть время, вы можете написать статью, посвященную этому. В этом разделе основное внимание уделяется тому, как Seata использует SPI для расширения возможностей платформы.

существуетSeataиспользуется в кадреEnhancedServiceLoaderРеализовать загрузку службы, мы можем знать по имени, что это расширеннаяServiceLoader. Затем относительно самого JDKServiceLoader, где он силен?

Как видно из рисунка ниже,EnhancedServiceLoader не только поддерживаетJavaСобственный каталог обнаружения служб также поддерживает ваши собственныеMETA-INF/seata/содержание.

image.png

Кроме того, существуют аннотации @LoadLevel для определенных классов реализации интерфейса.Если загружено несколько классов реализации центра конфигурации, то можно использовать атрибуты соответствующих аннотаций.orderприводить в порядок. Загрузите класс с наивысшим фактическим приоритетом.

image.png

Все мы знаем, что реестр является незаменимым базовым компонентом микросервисной системы, в который записывается адресная информация поставщика услуг. затем вSeataсередина,Seataклиенты, такие как менеджеры транзакцийTM, диспетчеру ресурсов RM необходимо связаться с координатором транзакций.TCДля связи необходимо получить адресную информацию сервера через центр регистрации.SeataРеестр поддерживает несколько сторонних реестров, таких какConsul,Apollo,Etcd3Ждать. Давайте взглянемSeataКак использовать механизм SPI для реализации расширенной поддержки нескольких реестров.

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

image.png

Затем в соответствующем пакетеMETA-INF/services/Определите конкретный класс реализации в , как здесьConsulопределяется в центре конфигурацииConsulConfigurationProvider.

image.png

Мы видим, чтоConsulConfigurationProviderДостигнутоConfigurationProviderИнтерфейс.

image.png


Я Му Фенг, спасибо за ваши лайки, избранное и комментарии, статьи будут обновляться и до встречи в следующем выпуске!

Поиск в WeChat:Технические примечания Му Фэн, Качественные статьи постоянно обновляются.У нас есть группа для обучения и перфокарт, которые могут втянуть вас и усердно работать, чтобы поразить большие заводы.Кроме того, у нас есть много обучающих материалов и материалов для интервью для всех.