Сервисы с provides/uses
Разделяйте компоненты с загрузчиком сервисов
«Сервисы с provides/uses» — бесплатный урок Java Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Развязка компонентов с помощью служб
Модули могут взаимодействовать без зависимости от реализаций друг друга, используя шаблон загрузчика служб.
Один модуль объявляет, что он uses интерфейс службы, а другой объявляет реализацию с помощью provides. JVM связывает их между собой.
Три роли
В службе участвуют три стороны:
- Интерфейс службы: контракт, расположенный в некотором модуле
- Поставщик: модуль, предоставляющий реализацию
- Потребитель: модуль, загружающий реализации во время выполнения
Интерфейс службы
Сначала определите контракт. Это обычный интерфейс в экспортируемом пакете, поэтому на него могут ссылаться и поставщики, и потребители.
package com.example.spi;
public interface Codec {
String name();
byte[] encode(String input);
}Потребитель объявляет использование
Модуль-потребитель объявляет, что он uses интерфейс. Это сообщает JPMS, что модуль будет искать реализации через ServiceLoader.
module com.example.app {
requires com.example.spi;
uses com.example.spi.Codec;
}Поставщик объявляет реализацию
Модуль-поставщик объявляет, какой интерфейс он реализует, с помощью записи provides ... with ..., указывая имя своего конкретного класса.
module com.example.base64 {
requires com.example.spi;
provides com.example.spi.Codec
with com.example.base64.Base64Codec;
}Реализация поставщика
Класс реализации — это обычный Java-класс. Он должен быть общедоступным и иметь либо общедоступный конструктор без аргументов, либо общедоступный статический метод provider().
package com.example.base64;
import com.example.spi.Codec;
import java.util.Base64;
public class Base64Codec implements Codec {
public String name() { return "base64"; }
public byte[] encode(String input) {
return Base64.getEncoder().encode(input.getBytes());
}
}Загрузка с помощью ServiceLoader
Во время выполнения потребитель запрашивает у ServiceLoader.load(Codec.class) всех доступных поставщиков и перебирает их, не имея связи на этапе компиляции ни с одной реализацией.
import com.example.spi.Codec;
import java.util.ServiceLoader;
public class App {
public static void main(String[] args) {
ServiceLoader<Codec> loader = ServiceLoader.load(Codec.class);
for (Codec codec : loader) {
System.out.println("Found codec: " + codec.name());
}
}
}Почему это мощный подход
Потребитель никогда не указывает имя класса поставщика. Добавьте новый модуль поставщика в путь модулей, и он будет обнаружен автоматически.
Это позволяет создавать архитектуры подключаемых компонентов и выполнять чистую инверсию зависимостей.
Фабрика поставщика
Если для создания экземпляра требуется логика, вместо конструктора без аргументов предоставьте общедоступный статический метод provider(). JPMS вызовет его, чтобы получить экземпляр.
package com.example.base64;
import com.example.spi.Codec;
public class CodecFactory {
public static Codec provider() {
return new Base64Codec();
}
}Несколько поставщиков
Многие модули могут предоставлять один и тот же интерфейс. ServiceLoader возвращает их все, позволяя потребителю выбрать поставщика по имени, приоритету или возможностям.
Добавляйте и удаляйте поставщиков, не изменяя код потребителя.
Поток поставщиков
Начиная с Java 9, ServiceLoader предлагает stream(), возвращая дескрипторы Provider, которые можно отфильтровать до создания экземпляров. Это полезно, когда создание экземпляра требует много ресурсов.
import com.example.spi.Codec;
import java.util.ServiceLoader;
public class App {
public static void main(String[] args) {
Codec chosen = ServiceLoader.load(Codec.class).stream()
.map(ServiceLoader.Provider::get)
.filter(c -> c.name().equals("base64"))
.findFirst()
.orElseThrow();
System.out.println("Using: " + chosen.name());
}
}Быстрая проверка
Вспомните, какую директиву использует каждая роль.
Итоги
Вы изучили шаблон службы модулей:
- Три роли: интерфейс службы, поставщик, потребитель
- Потребитель:
uses; поставщик:provides ... with - Реализации загружаются во время выполнения через
ServiceLoader - Поставщикам нужен общедоступный конструктор без аргументов или статический
provider()
Далее: перенос существующего проекта на модули.
Часто задаваемые вопросы
Урок «Сервисы с provides/uses» бесплатный?
Да — полный текст урока «Сервисы с provides/uses» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «Сервисы с provides/uses»?
Разделяйте компоненты с загрузчиком сервисов Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Сервисы с provides/uses»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- module-info.java
- requires и exports
- Сервисы с provides/uses
- Переход на модули