Usługi z provides/uses
Rozdzielanie komponentów za pomocą service loadera
Usługi z provides/uses to bezpłatna lekcja Java Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Java Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Java Academy zawiera 4 lekcji w sumie.
Rozdzielanie zależności za pomocą usług
Moduły mogą współpracować bez zależności od wzajemnych implementacji, korzystając ze wzorca ładowania usług.
Jeden moduł deklaruje, że uses interfejsu usługi, a inny deklaruje, że provides jego implementację. JVM łączy je ze sobą.
Trzy role
Usługa ma trzech uczestników:
- Interfejs usługi: kontrakt znajdujący się w jednym z modułów
- Dostawca: moduł dostarczający implementację
- Konsument: moduł, który ładuje implementacje w czasie działania
Interfejs usługi
Najpierw należy zdefiniować kontrakt. Jest to zwykły interfejs w eksportowanym pakiecie, aby zarówno dostawcy, jak i konsumenci mogli się do niego odwoływać.
package com.example.spi;
public interface Codec {
String name();
byte[] encode(String input);
}Konsument deklaruje uses
Moduł konsumenta deklaruje, że uses interfejsu. Informuje to JPMS, że moduł będzie wyszukiwać implementacje za pomocą ServiceLoader.
module com.example.app {
requires com.example.spi;
uses com.example.spi.Codec;
}Dostawca deklaruje provides
Moduł dostawcy deklaruje, który interfejs implementuje, za pomocą provides ... with ..., podając nazwę swojej konkretnej klasy.
module com.example.base64 {
requires com.example.spi;
provides com.example.spi.Codec
with com.example.base64.Base64Codec;
}Implementacja dostawcy
Klasa implementacji jest zwykłą klasą Javy. Musi być publiczna i mieć albo publiczny konstruktor bezargumentowy, albo publiczną statyczną metodę 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());
}
}Ładowanie za pomocą ServiceLoader
W czasie działania konsument wywołuje ServiceLoader.load(Codec.class), aby uzyskać wszystkich dostępnych dostawców, a następnie iteruje po nich bez bezpośredniego połączenia z żadną implementacją na etapie kompilacji.
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());
}
}
}Dlaczego to jest tak potężne
Konsument nigdy nie odwołuje się do nazwy klasy dostawcy. Wystarczy umieścić nowy moduł dostawcy na ścieżce modułów, aby został automatycznie wykryty.
Umożliwia to tworzenie architektur wtyczek i przejrzyste odwracanie zależności.
Fabryka provider()
Jeśli tworzenie obiektu wymaga dodatkowej logiki, należy zamiast konstruktora bezargumentowego udostępnić publiczną statyczną metodę provider(). JPMS wywoła ją, aby uzyskać instancję.
package com.example.base64;
import com.example.spi.Codec;
public class CodecFactory {
public static Codec provider() {
return new Base64Codec();
}
}Wielu dostawców
Wiele modułów może dostarczać tę samą implementację interfejsu. ServiceLoader zwraca je wszystkie, dzięki czemu konsument może wybrać dostawcę według nazwy, priorytetu lub możliwości.
Dostawców można dodawać i usuwać bez modyfikowania kodu konsumenta.
Strumień dostawców
Od wersji Java 9 ServiceLoader udostępnia metodę stream(), która zwraca uchwyty Provider. Można je filtrować przed utworzeniem instancji, co jest przydatne, gdy tworzenie obiektów jest kosztowne.
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());
}
}Szybkie sprawdzenie
Proszę przypomnieć sobie, której dyrektywy używa każda z ról.
Podsumowanie
Poznali Państwo wzorzec usług modułowych:
- Trzy role: interfejs usługi, dostawca i konsument
- Konsument:
uses; dostawca:provides ... with - Implementacje są ładowane w czasie działania za pomocą
ServiceLoader - Dostawcy potrzebują publicznego konstruktora bezargumentowego lub statycznej metody
provider()
Następnie: migracja istniejącego projektu do modułów.
Często zadawane pytania
Czy lekcja „Usługi z provides/uses” jest bezpłatna?
Tak — pełny tekst „Usługi z provides/uses” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Java Academy, przejdź na CoddyKit PRO. Kurs Java Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Usługi z provides/uses”?
Rozdzielanie komponentów za pomocą service loadera Ćwiczysz Java Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Java Academy?
Nie wymagamy żadnego doświadczenia. Java Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Usługi z provides/uses”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Java Academy?
Tak. Każda lekcja Java Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- module-info.java
- requires i exports
- Usługi z provides/uses
- Migracja do modułów