Spring Master's Road 14. Объясните простыми словами: применение механизма SPI в JDK и Spring Boot
Spring Master's Road 14. Объясните простыми словами: применение механизма SPI в JDK и Spring Boot

1. Интерпретация SPI: что такое SPI?

SPI (Service Provider Interface) Это механизм обнаружения служб, который позволяет сторонним поставщикам предоставлять реализации или расширения базовой библиотеки или основной платформы. Такая конструкция позволяет базовым библиотекам/платформам расширять функциональность за счет сторонних реализаций без изменения собственного кода.

  1. Нативный JDK изSPI
  • определение и открытиеJDKизSPIГлавным образом черезMETA-INF/services/Место конкретноеизфайл, чтобы указать, какие классы реализуют данныйиз Сервисный интерфейс。эти файлыиз Имя должно быть Интерфейс с полным именем,Содержимое — это полное имя класса, реализующего интерфейс.
  • механизм загрузкиServiceLoaderИспользование классаJavaиз Механизм загрузки классов изMETA-INF/services/Загрузка и создание экземпляров поставщиков услуг в каталоге。Например,ServiceLoader.load(MyServiceInterface.class)вернет реализациюMyServiceInterfaceизитератор экземпляра。
  • недостатокJDKРоднойизSPIкаждый проходServiceLoaderПри загрузке появляется новыйиз Пример,Класс из кеша не реализован,Расширенные функции, такие как синглтоны, также не рассматриваются.
  1. SpringизSPI
  • более гибкийSpringизSPIне толькодаобнаружение службы,Он предоставляет полный набор подключаемых механизмов. Например,может бытьSpringопределить новыеизPropertySource,ApplicationContextInitializerждать。
  • Интеграция с IoC:иJDKизSPIдругой,SpringизSPIВместоIoC (Inversion of Control) Интеграция контейнеров,так что вSPIМожно использовать при реализацииSpringиз Все функции,Например, внедрение зависимостей.
  • Соответствие условиямSpringПредоставляется на условияхиз Механизм согласования,Это позволяет загружать только определенныеизSPIвыполнить,Например,Вы можете выбрать, какой драйвер базы данных загрузить, в зависимости от текущей рабочей среды.
  • КонфигурацияSpringразрешено пройтиspring.factoriesФайл находится вMETA-INFПродолжить в каталоге Конфигурация,этотиJDKизSPIочень похоже,Но он предлагает больше возможностей и гибкости.

Приведите пример-аналогию:

 Представьте, что мы собираем телевизор.,SPIнравитьсятелевизорначальствоизодинUSBДжек。этотиндивидуальный Джек Различныйоборудование(НапримерUтарелка、геймпад、телевидение Большойждать),Но нас не волнует внутренняя работа этих устройств. Таким образом, вам нужно только предоставить стандартный интерфейс.,Другие компании(НапримерUтарелкапроизводитель)может бытьэтот Предоставляемый интерфейсвыполнить。так,Телевизор может использовать множество новых устройств без изменения собственного внутреннего кода.,Производители устройств также могут создавать совместимые устройства для различных телевизоров.

Суммируя,SPIда Что-то вроде Воляинтерфейсопределениеивыполнитьразделениеизшаблон проектирования,Он поощряет третьи стороны предоставлять плагины или реализации для основного продукта или платформы.,Это позволяет основному продукту легко расширять функциональность.

2. Примеры применения SPI в JDK

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

Все коды и шаги следующие:

Шаг 1:определениеодин Сервисный интерфейс,имя файла: MessageService.java

Язык кода:javascript
копировать
package com.example.demo.service;

public interface MessageService {
    String getMessage();
}

Шаг 2:Обеспечить реализацию сервисного интерфейса, здесь будут представлены два простых класса реализации.

HelloMessageService.java

Язык кода:javascript
копировать
package com.example.demo.service;

public class HelloMessageService implements MessageService {
    @Override
    public String getMessage() {
        return "Hello from HelloMessageService!";
    }
}

HiMessageService.java

Язык кода:javascript
копировать
package com.example.demo.service;

public class HiMessageService implements MessageService {
    @Override
    public String getMessage() {
        return "Hi from HiMessageService!";
    }
}

этотнекоторыйвыполнитьнравитьсядругойбрендилимодельизUтарелкаилидругойUSBоборудование。каждыйоборудование У каждого есть свойиз Функции и возможности,но все следуют одному и тому жеизUSBстандартный。

Шаг 3:Служба регистрациипоставщик

  существовать Каталог ресурсов(в целомдаsrc/main/resources/)Создано пододинназванныйMETA-INF/services/издокументпапка。существоватьэтотиндивидуальныйдокументпапкасередина,создаватьодинназванныйcom.example.demo.service.MessageServiceиздокумент(этотданасИнтерфейс с полным именем),Этот файл не имеет расширения,так Нет Хочу добавитьначальство.txtтакизсуффикс。документизсодержаниедолжно бытьнасиз两индивидуальныйвыполнитьдобрыйиз Полное имя,Каждое имя находится на отдельной строке:

Язык кода:javascript
копировать
com.example.demo.service.HelloMessageService
com.example.demo.service.HiMessageService

META-INF/services/ да Java SPI (Service Provider Interface) Соглашения в механизме относительно конкретных каталогов. Он выбирает не случайно, а SPI четко определены в спецификации. Поэтому при использовании JDK из ServiceLoader Когда класс используется для загрузки поставщика услуг, он специально ищет файлы по этому пути.

Убедитесь, что файл имеет только одно имя в строке,и никаких лишних пробелов или скрытых символов,документиспользоватьUTF-8кодирование。

Шаг 4:использоватьServiceLoaderнагрузкаииспользовать Служить

Язык кода:javascript
копировать
package com.example.demo;

import com.example.demo.service.MessageService;

import java.util.ServiceLoader;

public class DemoApplication {

    public static void main(String[] args) {
        ServiceLoader<MessageService> loaders = ServiceLoader.load(MessageService.class);

        for (MessageService service : loaders) {
            System.out.println(service.getMessage());
        }
    }
}

Результаты бега следующие:

  этотиллюстрироватьServiceLoader成功地нагрузка ПонятнонасдляMessageServiceПредоставляемый интерфейсиз两индивидуальныйвыполнить,инас Можетсуществовать Нет ИсправлятьMainдобрыйизкодизслучай,добавив большеизвыполнитьдобрыйивозобновлятьMETA-INF/services/com.example.MessageServiceдокументрасширятьнасиз Служить。

Представьте себе, что вы покупаете высококачественный смарт-телевизор.,этотбашнятелевидениеначальствоиметьодинили多индивидуальныйHDMIпорт,Это интерфейс, который соединяет его с внешними устройствами.

  1. Определить сервисный интерфейс:этотнравитьсятелевидениеопределение ПонятноHDMIпортизстандартный。существоватьначальстволапшаизкодсередина,MessageServiceИнтерфейсдаэтотиндивидуальный“HDMIпорт”,Определяет способ связи с внешними устройствами.
  2. Обеспечить реализацию сервисного интерфейса:этотдобрыйпохоже напроизводительдляHDMIинтерфейс Производить различныеоборудование,Например, игровые консоли, проигрыватели Blu-ray или потоковые флешки. в коде,HelloMessageServiceиHiMessageServiceСразудаэтотнекоторый“HDMIоборудование”。каждыйоборудование/выполнить都иметь Что独特извыход,Но все они следуют единомуизHDMIстандартный(MessageServiceинтерфейс)。
  3. Служба регистрациипоставщик:когданас购买ПонятноодинHDMIоборудование,этов целомгородсуществоватьупаковочная коробканачальствочетко обозначенный“Применимо кHDMI”。этотнравитьсяодинлоготип,Сообщите пользователю, что он может подключиться к чему угодно с помощьюHDMIинтерфейсизтелевидение。существоватьSPIиз Пример,META-INF/services/Оглавлениеи Чтосерединаиздокументнравитьсяэтотиндивидуальный“Этикетка”,РассказыватьJDKКоторыйдобрыйдаMessageServiceизвыполнить。
  4. Используйте ServiceLoader для загрузки и использования сервисов:когдавставлятьодинHDMIоборудованиеприезжатьтелевидениеначальство,и переключитесь на правильный входной канал,Телевизор отобразит содержимое устройства. Сходным образом,В коде шага,ServiceLoaderнравитьсятелевидениеиз Функция выбора входа,способен обнаружитьииспользовать Местоиметь已连接изHDMIоборудование(Прямо сейчасMessageServiceиз Местоиметьвыполнить)。

3. Применение SPI в среде Spring

Springчиновниксуществовать Что文档иисточниккодсередина多次提приезжать ПонятноSPIService Provider Interface)изконцепция。Да,когданасобъяснять“SpringизSPI”час,в целомобратитесь киздаSpringФреймворк предоставляет разработчикамиз Набор расширяемыхизинтерфейсиабстрактныйдобрый,Разработчики могут реализовывать свои собственные версии на основе этих интерфейсов и абстрактных классов.

существоватьSpringсередина,SPIизконцепцияиSpring Bootиспользоватьизspring.factoriesдокументиз Механизм не совсем тот,Но все они воплощают идеи подключаемости и расширяемости.

  1. SpringизSPI
  • Springизосновнойрамка提供Понятно很多интерфейсиабстрактныйдобрый,нравитьсяBeanPostProcessor, PropertySource, ApplicationContextInitializerждать,этотнекоторый都можно рассматривать какдаSpringизSPI。Разработчик МожетвыполнитьэтотнекоторыйинтерфейсрасширятьSpringиз Функция。этотнекоторыйинтерфейс Разрешить разработчикамсуществоватьSpringконтейнеризжизненный циклиздругойэтапное вмешательство,Осознайте свою собственную логику.
  1. Механизм Spring Boot изspring.factories
  • spring.factoriesдаSpring Bootизодинхарактеристика,Разрешить разработчикам настраивать автоматические Конфигурация。проходитьspring.factoriesдокумент,Разработчики могут определять свои собственные классы автоматической конфигурации.,этотнекоторыйдобрыйсуществоватьSpring Bootзапускатьчасбудетавтоматическийнагрузка。
  • в этом случае,SpringFactoriesLoaderизиспользовать,особеннодапроходитьspring.factoriesдокумент Приходитьнагрузкаи Примеризменятьопределениеиздобрый,можно рассматривать какдаконкретныйизSPIвыполнить Способ,но это специфично дляSpring Boot

3.1 Идеи SPI в традиционной среде Spring

  существовать ТрадицияизSpringрамкасередина,虽然没иметьпрямойиспользоватьназванный"SPI"изтермин,но Чтоосновной Мысльвсе ещежитьсуществовать。SpringПредоставляет несколько точек расширения,Чтосерединанаиболее представительныйиз СразудаBeanPostProcessor。существовать Этот разделсередина,нас Воляпроходитьодин ПростойизMessageServiceинтерфейс及ЧтовыполнитьобсудитьнравитьсячтоиспользоватьSpringизBeanPostProcessorВариант точки расширенияSPIиз Мысль。

Предоставляет два простых класса реализации.

HelloMessageService.java

Язык кода:javascript
копировать
package com.example.demo.service;

public class HelloMessageService implements MessageService {
    @Override
    public String getMessage() {
        return "Hello from HelloMessageService!";
    }
}

HiMessageService.java

Язык кода:javascript
копировать
package com.example.demo.service;

public class HiMessageService implements MessageService {
    @Override
    public String getMessage() {
        return "Hi from HiMessageService!";
    }
}

определениеBeanPostProcessor

Язык кода:javascript
копировать
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MessageServicePostProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof MessageService) {
            return new MessageService() {
                @Override
                public String getMessage() {
                    return ((MessageService) bean).getMessage() + " [Processed by Spring SPI]";
                }
            };
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

ИсправлятьSpringКонфигурация

ВоляMessageServicePostProcessorдобавить вSpringКонфигурациясередина:

Язык кода:javascript
копировать
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MessageServiceConfig {
    
    @Bean
    public MessageService helloMessageService() {
        return new HelloMessageService();
    }

    @Bean
    public MessageService hiMessageService() {
        return new HiMessageService();
    }
    
    @Bean
    public MessageServicePostProcessor messageServicePostProcessor() {
        return new MessageServicePostProcessor();
    }
}

Выполнить программу

использоватьранее предоставленныйизDemoApplicationПримердобрый:

Язык кода:javascript
копировать
package com.example.demo;

import com.example.demo.configuration.MessageServiceConfig;
import com.example.demo.service.MessageService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DemoApplication {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(MessageServiceConfig.class);
        MessageService helloMessageService = context.getBean("helloMessageService", MessageService.class);
        MessageService hiMessageService = context.getBean("hiMessageService", MessageService.class);

        System.out.println(helloMessageService.getMessage());
        System.out.println(hiMessageService.getMessage());
    }
}

Результаты запуска:

Сейчас,КаждыйодинMessageServiceвыполнить Все былиBeanPostProcessorОбработано,добавлено дополнительноеизинформация“[Processed by Spring SPI]”。этот演示ПонятноSpringизSPIконцепция,проходитьBeanPostProcessorрасширятьили ИсправлятьSpringконтейнерсерединаизbean

Возможно, кто-то заметил здесь красное предупреждение,этотиндивидуальный之前существоватьговоритьBeanPostProcessorизчас候также提приезжать过,когдаBeanPostProcessorсамо существоодинили多индивидуальныйBeanPostProcessorиметь дело счас,Это произойдет. Проще говоря,потому чтоBeanPostProcessorнуждатьсясуществоватьдругойbeanинициализирован раньше,так что некоторыеBeanPostProcessorНевозможно обработать раннюю инициализациюизbean,включать КонфигурациядобрыйидругойBeanPostProcessor。Решение Сразудане ставьMessageServicePostProcessorпомещатьсуществовать Конфигурациядобрый初始изменять,Удалить в категории Конфигурация,сноваMessageServicePostProcessorдобавлятьначальство@Componentаннотация。

Пример начала статьи-аналогии:

  1. ТВ и USB-розетка: существоватьэтотиндивидуальный新из Примерсередина,телевизорвсе ещедаосновнойизSpringприложение,КонкретнодаDemoApplicationдобрый。этотиндивидуальныйосновнойприложениенуждатьсяот某индивидуальный Служить(Прямо сейчасMessageService)Получите и распечатайтеинформация。
  2. USB-разъем: и То же, что и раньше,MessageServiceИнтерфейсдаэтотиндивидуальный"USBДжек"。этотелевизор提供Понятноодинстандартныйизменятьизинтерфейс,Прямо сейчасgetMessage()метод,Но конкретного регламента, как это реализовать, не существует.
  3. Производители оборудования и их продукция: здесь,насиметь两добрыйоборудованиепроизводительилитретья сторонапоставщик:HelloMessageServiceиHiMessageService。Они есть"USBДжек"(Прямо сейчасMessageServiceинтерфейс)提供Понятнодругойизоборудованиеиливыполнить。одинпоказывать“Hello from HelloMessageService!”,Другойодинпоказывать“Hi from HiMessageService!”
  4. BeanPostProcessor: этотдаодинособенныйиз“волшебный ящик”,Может Воля Что视дляодинспособен перехватить и Исправлятьтелевизор Показать контентизразумныйоборудование。когдавставлятьUSBоборудование(Прямо сейчасMessageServiceизвыполнить)и попробуйотсерединаполучатьинформациячас,Этот «волшебный ящик» вмешается,идля Каждый条информациядобавить в“[Processed by Spring SPI]”
  5. Контекст SpringКонфигурация: этотвсе ещедателевизоризиспользоватьиллюстрировать书,но Сейчасдаиспользовать Понятнона основеJavaиз Конфигурация Способ,Прямо сейчасMessageServiceConfigдобрый。этотиндивидуальный“использоватьиллюстрировать书”гидSpringконтейнернравитьсячтосоздаватьи管理MessageServiceиз Пример,ивозвращатьсягидэтонравитьсячтоиспользовать“волшебный ящик”(Прямо сейчасMessageServicePostProcessor)Приходитьиметь дело синформация。

В общем,По сравнению с предыдущим примером,Этот новый пример обеспечивает более динамичную сцену.,ЧтосерединаSpringизBeanPostProcessorточки расширения позволяютнасперехватить и Исправлятьbeanиз Поведение,Как интеллектуальное устройство, которое может вмешиваться и изменять отображаемый контент.

3.2 Идея SPI в Spring Boot

Spring BootиметьодиниSPIсходствоизмеханизм,ноэтои Нетполностьюждать То же, чтоJavaизстандартныйSPI

Spring Bootизавтоматический Конфигурациямеханизмв основном зависит отspring.factoriesдокумент。этотиндивидуальныйдокумент Можетсуществовать多индивидуальныйjarсерединажитьсуществовать,иSpring Bootзагрузит все видимоеизspring.factoriesдокумент。нас Можетсуществоватьэтотиндивидуальныйдокументсерединаобъявить сериюиз Класс автоматической настройки,Таким образом, при выполнении определенных условий,этотнекоторый КонфигурациядобрыйвстречаавтоматическийодеялоSpring Bootприложение。

будет показано следующимSpring SPIМысльизхороший пример,ДаэтоиSpring Bootтесно связанный。

Определить интерфейс

Язык кода:javascript
копировать
package com.example.demo.service;

public interface MessageService {
    String getMessage();
}

Здесь будут представлены два простых класса реализации.

HelloMessageService.java

Язык кода:javascript
копировать
package com.example.demo.service;

public class HelloMessageService implements MessageService {
    @Override
    public String getMessage() {
        return "Hello from HelloMessageService!";
    }
}

HiMessageService.java

Язык кода:javascript
копировать
package com.example.demo.service;

public class HiMessageService implements MessageService {
    @Override
    public String getMessage() {
        return "Hi from HiMessageService!";
    }
}

Служба регистрации

существоватьresources/META-INFСоздано пододинимя файладляspring.factories。этотиндивидуальныйдокументвнутри,Можно зарегистрироватьсяMessageServiceвыполнитьдобрый。

Язык кода:javascript
копировать
com.example.demo.service.MessageService=com.example.demo.service.HelloMessageService,com.example.demo.service.HiMessageService

  Уведомлениеэтотвнутриcom.example.demo.service.MessageServiceдаинтерфейсизполный путь,иcom.example.demo.service.HelloMessageService,com.example.demo.service.HiMessageServiceдавыполнитьдобрыйизполный путь。нравиться果иметь多индивидуальныйвыполнитьдобрый,Их следует разделять запятыми.

spring.factoriesдокументсерединаизключ входаимежду ценностями Нет能иметь换行,Прямо сейчасkey=valueформаизструктура должнасуществовать Начните с той же строки。Да,Если необходимо указать несколько значений (например, несколько классов реализации),И эти значения да разделены запятой,那么Можетиспользоватьобратная косая черта(\)обернуть。spring.factories из Название да условно из. Если вы попытаетесь использовать другое имя файла, то Spring Boot Механизм автоматической настройки не распознает его.

этотвнутриspring.factoriesЭто также можно записать как

Язык кода:javascript
копировать
com.example.demo.service.MessageService=com.example.demo.service.HelloMessageService,\
  com.example.demo.service.HiMessageService

прямойсуществовать逗号后лапша回车IDEAвстречаавтоматический补Полныйобратная косая черта,Просто убедитесь, что между ключами и значениями нет разрывов строк.

использоватьSpringFactoriesLoaderПриходитьнагрузка Служить

Язык кода:javascript
копировать
package com.example.demo;

import com.example.demo.service.MessageService;
import org.springframework.core.io.support.SpringFactoriesLoader;

import java.util.List;

public class DemoApplication {

    public static void main(String[] args) {
        List<MessageService> services = SpringFactoriesLoader.loadFactories(MessageService.class, null);
        for (MessageService service : services) {
            System.out.println(service.getMessage());
        }
    }
}

SpringFactoriesLoader.loadFactoriesизвторой параметрдадобрыйнагрузкаустройство,Здесь мы используем загрузчик классов по умолчанию,так что проходиnull

Результаты запуска:

  этотдобрый Способиспользовать ПонятноSpringизSpringFactoriesLoader,Это позволяет разработчикам предоставлять несколько реализаций интерфейса.,ипроходитьspring.factoriesдокумент Приходитьзарегистрироватьсяэто们。этотиJDKизSPIМысль Оченьсходство,Толькодасуществоватьвыполнитьдетальначальствоиметь Местодругой。этоттакжедаSpring Bootнравитьсячтоавтоматический Конфигурацияиз База,Он будет искать различныеspring.factoriesдокумент,根据Чтосерединаопределениеиздобрый Приходить初始изменятьи Конфигурацияbean

Давайте продолжим использовать пример телевизора, чтобы объяснить:

  1. телевизор: этотданасизSpringприложение,нравитьсяDemoApplication。телевизорда Проверятьдругой信号источникилирядизоборудование,Наше приложение да реализовано для запуска и использования различных сервисов.
  2. USB-разъем: этотпредставлятьнасизMessageServiceинтерфейс。USBДжекдаодинстандартныйизинтерфейс,Позволяет подключать различные устройства,нравитьсяMessageServiceинтерфейспозволятьиметь多добрыйвыполнить Способ。
  3. USB-устройство (например, USB-накопитель или мобильный жесткий диск): этотпредставлятьнасиз Служитьвыполнить,НапримерHelloMessageServiceиHiMessageService。каждыйUSBоборудованиесуществоватьвставлятьтелевизор Есть конкретныеизсодержаниеили Функция,Это похоже на то, что каждая реализация нашего сервиса возвращает разные сообщения.
  4. телевизориз Каталог USB-устройств: этотдаspring.factoriesдокумент。когданас ВоляUSBоборудованиевставлятьтелевизорчас,Телевизор проверит устройство на наличие информации или контента,spring.factoriesдокумент РассказыватьSpring BootКоторый Служитьвыполнитьда Доступныйиз,нравитьсятелевизорзнаю, какиеUSBоборудованиеодеяловставлять。
  5. телевизор с функцией сканирования USB: этот СразудаSpringFactoriesLoader。когданасхотетьоттелевизорначальство ПроверятьUSBсодержаниечас,Телевизор просканирует и отобразит содержимое. такой же,когдаDemoApplicationбегатьчас,SpringFactoriesLoaderвстреча查找инагрузкасуществоватьspring.factoriesдокументсерединасписокиз Служитьвыполнить。

Упрощенное объяснение:

  • когдавставлятьUSBоборудованиеприезжатьтелевизор,Ожидается, что телевизор сможет распознавать и отображать контент устройства.
  • В нашем примере,USBоборудованиеизсодержание СразудаотMessageServiceвыполнитьдобрыйвозвращатьсяизинформация。
  • spring.factoriesдокументнравитьсятелевизориз Встроенный каталог,Рассказыватьтелевизор КоторыйUSBоборудованиедаизвестныйизи Можетиспользоватьиз。
  • когданасизDemoApplication(телевизор)бегатьчас,этоиспользоватьSpringFactoriesLoaderUSBсканирование Функция)приди и проверь Который Служить(USBоборудование)да Доступныйиз,ивыходсоответствующийизинформация(показыватьUSBсодержание)。

  Подвести итог:существоватьэтотиндивидуальныйSpring BootизSPIПример,насвыставка ПонятноосновнойSpringприложениенравитьсячтоавтоматическийидентифицироватьииспользоватьspring.factoriesдокументсерединазарегистрироватьсяизвыполнить,этотителевизоравтоматическийидентифицироватьииспользовать МестоиметьвставлятьизUSBоборудованиеиметьсходствоместо。

4. Применение SPI при загрузке драйвера JDBC

  Управление базой данныхизSPIосновная часть СейчасJDBCводить машинуизавтоматический Обнаружитьмеханизмсередина。JDBC 4.0 引入Понятноодинхарактеристика,позволятьводить машинуавтоматическийзарегистрироватьсяприезжатьDriverManager。этотдапроходитьиспользоватьJavaизSPIПриходитьвыполнитьиз。водить машинуjar包内встречаиметьодинMETA-INF/services/java.sql.Driverдокумент,этотдокументсередина包含Понятно该водить машинуизDriverвыполнитьдобрыйиз Полныйдобрыйимя。так,когдадобрыйпутьсерединаиметьJDBCводить машинуизjarдокументчас,Javaприложение Можетавтоматический ОбнаружитьинагрузкаJDBCводить машину,Без явной загрузки класса драйвера.

  этот意味着任что数据Библиотека供应商都Может编写Что自己изJDBCводить машинупрограмма,ТолькохотетьэтоследоватьJDBCводить машинупрограммаизSPI,это Сразу Можетодеяло任чтоиспользоватьJDBCизJavaприложение Местоиспользовать。

когданасиспользоватьDriverManager.getConnection()Получить подключение к базе данныхчас,За кулисамидаиспользоватьSPIмеханизмнагрузка合适изводить машинупрограмма。

нижедаSPIмеханизмиз Конкретная работа Способ:

  1. Определить сервисный интерфейс

здесь,интерфейсужеJava平башняопределение,Прямо сейчасjava.sql.Driver

  1. Предоставить реализацию интерфейса

Крупнейшие поставщики баз данных(нравитьсяOracle, MySQL, PostgreSQLждать)предоставил для своей базы данныхJDBCводить машинупрограмма,это们都выполнить Понятноjava.sql.Driverинтерфейс。Например,MySQLизводить машинупрограммасерединаиметьодиндобрыйпохоже нанижеиздобрый:

Язык кода:javascript
копировать
public class com.mysql.cj.jdbc.Driver implements java.sql.Driver {
    // Реализация методов интерфейса...
}

Прямо над картинкой:

  1. Служба регистрациипоставщик

дляMySQLизводить машинупрограмма,Можетсуществовать ЧтоJARдокументизMETA-INF/servicesОглавление下找приезжатьодинназванныйjava.sql.Driverиздокумент,Содержимое файла следующее:

Язык кода:javascript
копировать
com.mysql.cj.jdbc.Driver

Прямо над картинкой:

看приезжатьэтотвнутрида Нетда Обнаружитьи Нет.2модерацияизJDK SPIиз Тот же пример?Испытайте это。

  1. Используйте SPI для загрузки и использования сервисов

  когданасвызовDriverManager.getConnection(jdbcUrl, username, password)час,DriverManagerвстречаиспользоватьServiceLoaderнайти всех зарегистрированныхизjava.sql.Driverвыполнить。Затем,Он будет пробовать каждый драйвер,直приезжать找приезжатьодин Можетиметь дело с给定jdbcUrlизводить машинупрограмма。

Ниже приведен простой пример,выставканравитьсячтоиспользоватьJDBC SPIПолучить подключение к базе данных:

Язык кода:javascript
копировать
import java.sql.Connection;
import java.sql.DriverManager;

public class JdbcExample {
    public static void main(String[] args) {
        String jdbcUrl = "jdbc:mysql://localhost:3306/mydatabase";
        String username = "root";
        String password = "password";

        try {
            Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
            System.out.println("Connected to the database!");
            connection.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 В приведенном выше коде,нас没иметь明确обратитесь к定использовать哪индивидуальныйJDBCводить машинупрограмма,потому чтоDriverManagerвстречаавтоматическийдлянас Выберите подходящийизводить машинупрограмма。

Этот модульный и подключаемый механизм позволяет нам легко переключать драйверы для разных баз данных,Тольконуждаться更改JDBC URLи обеспечить соответственноизводить машинупрограммаJARсуществоватьдобрыйпутьначальство Прямо сейчас Может。

  существоватьSpring Bootсередина,Разработчикв целом НетвстречапрямойиJDBCизSPIмеханизмвзаимодействие Приходить Получить подключение к базе данных。Spring Bootизавтоматический Конфигурациямеханизм隐藏Понятно许多底层деталь,Упрощает настройку и работу с базами данных.

一般встречасуществоватьapplication.propertiesилиapplication.ymlсередина Конфигурация Информация о подключении к базе данных。

Например:

Язык кода:javascript
копировать
spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

 В вышеуказанных шагах,Spring Bootизавтоматический Конфигурациямеханизмвстреча根据提供изполагатьсяи Конфигурация信息Приходить初始изменятьи КонфигурацияDataSourceобъект,Этот объект управляет подключениями к базе данных. на самом деле,добавить вJDBCводить машинуполагатьсячас,Spring BootвстречаиспользоватьJDKизSPIмеханизм(существоватьJDBCспецификациясерединаприложение)найти и загрузить соответствующийиз Управление базой данных。Разработчик虽然НетпрямойиJDKизSPIвзаимодействие,носуществоватьпозадиSpring BootДействительноиспользовать ПонятноJDK SPIмеханизм Приходить Получить подключение к базе данных。

5. Как понять идеи SPI с помощью автоматической настройки Spring Boot

  этотдобрыймеханизмиметь点добрыйпохоже наJavaизSPI,потому чтоэтопозволятьтретья сторона Библиотека提供一некоторый默认из Конфигурация。ноэто比JavaизSPI更для强大игибкий,потому чтоSpring BootПредоставлено большое количествоизаннотация(нравиться@ConditionalOnClass@ConditionalOnProperty@ConditionalOnMissingBeanждать)контролировать Класс автоматической настройкида否应该одеялонагрузкаиприложение。

В общем,Spring Bootизspring.factoriesмеханизмиJavaизSPIсуществоватьконцепцияначальстводасходствоиз,Но они различаются деталями реализации и использования.

Давайте создадим упрощенный практический пример,гипотезанасхотетьдлядругойизинформация Служить(нравитьсяSMSиEmail)создаватьавтоматический Конфигурация。

Интерфейс службы сообщений

Язык кода:javascript
копировать
package com.example.demo.service;

public interface MessageService {
    void send(String message);
}

Реализация смс-сервиса

Язык кода:javascript
копировать
package com.example.demo.service.impl;

import com.example.demo.service.MessageService;

public class SmsService implements MessageService {
    @Override
    public void send(String message) {
        System.out.println("Sending SMS: " + message);
    }
}

Реализация почтового сервиса

Язык кода:javascript
копировать
package com.example.demo.service.impl;

import com.example.demo.service.MessageService;

public class EmailService implements MessageService {
    @Override
    public void send(String message) {
        System.out.println("Sending Email: " + message);
    }
}

Класс автоматической настройки

Язык кода:javascript
копировать
package com.example.demo.configuration;

import com.example.demo.service.EmailService;
import com.example.demo.service.MessageService;
import com.example.demo.service.SmsService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MessageAutoConfiguration {

    @Bean
    @ConditionalOnProperty(name = "message.type", havingValue = "sms")
    public MessageService smsService() {
        return new SmsService();
    }

    @Bean
    @ConditionalOnProperty(name = "message.type", havingValue = "email")
    public MessageService emailService() {
        return new EmailService();
    }
}

  этотиндивидуальныйдобрый提供两индивидуальный条件性изbeans(компоненты),соответственнодаSmsServiceиEmailService。этотнекоторыйbeansиз Творение зависит отapplication.propertiesдокументсерединаидентификацияиззначение атрибута。

  • @ConditionalOnProperty(name = "message.type", havingValue = "sms")

  когдаapplication.propertiesилиapplication.ymlсерединаопределениеизсвойствоmessage.typeиз Значениеsmsчас,Это условиеtrue。этотчас,smsService()метод Воляодеяловызов,отисоздаватьодинSmsServiceизbean

  • @ConditionalOnProperty(name = "message.type", havingValue = "email")

  когдаapplication.propertiesилиapplication.ymlсерединаопределениеизсвойствоmessage.typeиз Значениеemailчас,Это условиеtrue。этотчас,emailService()метод Воляодеяловызов,отисоздаватьодинEmailServiceизbean

файл Spring.factories

существоватьsrc/main/resources/META-INFОглавление Создано пододинspring.factoriesдокумент,Содержание следующее:

Язык кода:javascript
копировать
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.demo.configuration.MessageAutoConfiguration

файл application.properties

Язык кода:javascript
копировать
message.type=sms

Компонент MessageTester

Язык кода:javascript
копировать
package com.example.demo;

import com.example.demo.service.MessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
public class MessageTester {

    @Autowired
    private MessageService messageService;

    @PostConstruct
    public void init() {
        messageService.send("Hello World");
    }
}

Основная программа DemoApplication

Язык кода:javascript
копировать
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

Результаты запуска:

 В приведенном выше примере,нассоздавать ПонятноодинMessageServiceинтерфейси两индивидуальныйвыполнить(SmsServiceиEmailService)。Затем,Мы создали класс автоматической конфигурации.,Чтосередина包含两индивидуальныйbeanопределение,этот两индивидуальныйbeanопределениесоответственнона основеapplication.propertiesсерединаиззначение атрибута条件性地создавать。существоватьspring.factoriesдокументсередина,Мы объявляем этот класс автоматической конфигурации.,так чтоSpring Bootсуществоватьзапускатьчасможетавтоматическийнагрузкаэто。

Здесь продолжайте использовать пример телевизора, чтобы сублимировать понимание.

аналогия с ТВ

  1. общая концепция
  • гипотезателевизор(TV)даодинJavaприложение。
  • телевизор Различные слоты,нравитьсяHDMIUSBVGAждать,можно рассматривать какприложениесерединаизSPIинтерфейс。
  • вставлятьэтотнекоторыйслотизоборудование(нравитьсяDVDигрок、игровая консоль、USBводить машинуустройствождать)можно рассматривать какSPIизвыполнить。
  1. JavaизSPI
  • Когда мы покупаем телевизор,Не уверен, какое устройство будет подключено,возможныйдаDVDигрок,Также возможна даигровая консоль。
  • Да,Толькохотетьэтотнекоторыйоборудованиеследовать Понятнослотизстандартный(Например,HDMIстандартный),Просто подключите его и заставьте его работать.
  • этотнравитьсяJavaизSPIмеханизм:для Понятно能让多индивидуальный供应商提供выполнить,Javaопределение Понятноодининтерфейс,Поставщики предоставляют конкретные реализации.
  1. Автоконфигурация Spring Boot
  • Сейчас,Представьте себе современный смарт-телевизор. При подключении устройства,телевизор не только идентифицирует его,Также возможна автоматическая настройка параметров в зависимости от типа подключенного устройства.,Например Выберите правильныйизисточник входного сигнала、Оптимизация качества изображения и многое другое.
  • этотнравитьсяSpring Bootизавтоматический Конфигурация:когдаSpring Bootприложениезапускатьчас,оно проверитclasspathначальствоиз Библиотека,И автоматически применяется Конфигурация на основе существующей библиотеки.
  • телевизоризавтоматическийнастраивать Можетдобрый比дляSpring Bootсерединаизspring.factoriesи各добрый@Conditional...аннотация。это们决定существовать什么条件下进行哪добрый Конфигурация。
  1. Масштабируемость
  • Если производитель телевизора хочет разработать телевизор для нового слота или технологии подключения,Он может легко добавлять новые слоты в свой телевизор.
  • Так же,использоватьSpring Boot,Если вы хотите добавить новые функции или библиотеки в свое приложение,Просто добавьте соответствующие зависимости,ЗатемSpring Bootвстречаавтоматический识别и Конфигурацияэтотнекоторый新Функция。

 По этой аналогии,телевизоризслотиавтоматическийнастраивать Функциядлянас提供Понятноодин Интуитивныйиз Способ Приходить理解JavaизSPIмеханизмиSpring Bootизавтоматический Конфигурациянравитьсячто工作,и как они помогают разработчикам приложений.

6. Сводка SPI (интерфейс поставщика услуг)

SPI,то есть интерфейс поставщика услуг,да Особый шаблон проектирования. Это позволяет платформе или базовой библиотеке предоставлять предопределенный интерфейс сторонним разработчикам.,Это позволяет им предоставлять собственные реализации или расширения платформы.

основные цели:

  1. развязка:SPIмеханизм让рамкаизосновной Вместо Расширения остаютсяразвязка,Сделайте основной код независимым от конкретной реализации.
  2. динамическая нагрузка:系统можетпроходитьидентификацияизмеханизм(нравитьсяJavaизServiceLoader)动态地Обнаружитьинагрузка Место需извыполнить。
  3. Гибкость: пользователи платформы могут выбирать, заменять или добавлять новые реализации в соответствии со своими потребностями.,без изменения основного кода. Подключаемый: сторонние службы или реализации можно легко добавлять в систему или удалять из нее.,Нет необходимости менять существующую структуру кода.

ценить:

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

SPI и «Принцип открытия и закрытия»

 Принцип «открыто-закрытости» предполагает, что программные объекты должны быть открыты для расширений.,Но закрыт для модификации. То есть без изменения существующего кода,Добавляйте новые функции через расширения.

Как SPI воплощает в себе «принцип открытия-закрытия»:

  1. открыт для расширения:SPI提供Понятно Что-то вродестандартныйизменятьиз Способ,Позволяет сторонним разработчикам предоставлять новые реализации или функции для существующих систем.
  2. Закрыто для модификации: при добавлении новых функций или функций исходный код платформы или библиотеки не требует изменения.
  3. независимое развитие:рамка ВместоSPIвыполнить Может独立地进изменятьиразвивать,Не влияйте друг на друга.

Суммируя,SPIда Что-то вроде使软件рамкаили Библиотека更добавлять模块изменять、Может扩展и Может维护изиметь效метод。проходитьследовать“принцип открытия-закрытия”,SPIобеспечил системуизстабильностьигибкий性,Таким образом, удовлетворяя постоянно меняющиеся потребности бизнеса.

Добро пожаловать в тройное соединение в один клик~

Если у вас есть какие-либо вопросы, оставьте сообщение, и давайте обсудим и поучимся вместе.

----------------------Talk is cheap, show me the code-----------------------

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose