Jaka jest różnica między interfejsem dostawcy usług (SPI) a interfejsem programowania aplikacji (API) ?
A dokładniej, w przypadku bibliotek Java, co czyni je API i / lub SPI?
Jaka jest różnica między interfejsem dostawcy usług (SPI) a interfejsem programowania aplikacji (API) ?
A dokładniej, w przypadku bibliotek Java, co czyni je API i / lub SPI?
Odpowiedzi:
Innymi słowy, API mówi ci, co konkretna klasa / metoda robi dla ciebie, a SPI mówi, co musisz zrobić, aby się dostosować.
Zwykle API i SPI są osobne. Na przykład, w JDBC klasa jest częścią SPI: Jeśli po prostu chcesz używać JDBC, nie trzeba go używać bezpośrednio, ale każdy, kto realizuje sterownik JDBC musi implementować tej klasy.Driver
Czasami jednak się pokrywają. Interfejs jest zarówno SPI i API go używać rutynowo podczas korzystania ze sterownika JDBC i musi być realizowany przez dewelopera sterownika JDBC.Connection
@SomeAnnotation
do swojej klasy, aby została podniesiona przez jakiś framework, czy ta klasa adnotacji SomeAnnotation.class
byłaby uważana za część SPI, nawet jeśli technicznie jej nie rozszerzam ani nie implementuję?
From Effective Java, 2nd Edition :
Struktura dostawcy usług to system, w którym wielu dostawców usług wdraża usługę, a system udostępnia implementacje swoim klientom, oddzielając je od implementacji.
Istnieją trzy podstawowe elementy struktury dostawcy usług: interfejs usługi, który dostawcy wdrażają; interfejs API rejestracji dostawcy, którego system używa do rejestrowania implementacji, umożliwiając klientom dostęp do nich; oraz interfejs API dostępu do usługi, którego klienci używają do uzyskania wystąpienia usługi. Interfejs API dostępu do usługi zazwyczaj pozwala, ale nie wymaga od klienta określenia pewnych kryteriów wyboru dostawcy. W przypadku braku takiej specyfikacji interfejs API zwraca instancję domyślnej implementacji. Interfejs API dostępu do usług to „elastyczna fabryka statyczna”, która stanowi podstawę struktury usługodawcy.
Opcjonalnym czwartym komponentem struktury dostawcy usług jest interfejs dostawcy usług, który dostawcy implementują w celu tworzenia instancji ich implementacji usług. W przypadku braku interfejsu usługodawcy implementacje są rejestrowane według nazwy klasy i tworzone w sposób refleksyjny (pozycja 53). W przypadku JDBC połączenie odgrywa rolę interfejsu usługi, DriverManager.registerDriver to interfejs API rejestracji dostawcy, DriverManager.getConnection to interfejs API dostępu do usługi, a Driver to interfejs dostawcy usług.
Istnieje wiele wariantów wzorca struktury usługodawcy. Na przykład interfejs API dostępu do usługi może zwrócić bogatszy interfejs usługi niż ten wymagany przez dostawcę, przy użyciu wzorca adaptera [Gamma95, str. 139]. Oto prosta implementacja z interfejsem dostawcy usług i domyślnym dostawcą:
// Service provider framework sketch
// Service interface
public interface Service {
... // Service-specific methods go here
}
// Service provider interface
public interface Provider {
Service newService();
}
// Noninstantiable class for service registration and access
public class Services {
private Services() { } // Prevents instantiation (Item 4)
// Maps service names to services
private static final Map<String, Provider> providers =
new ConcurrentHashMap<String, Provider>();
public static final String DEFAULT_PROVIDER_NAME = "<def>";
// Provider registration API
public static void registerDefaultProvider(Provider p) {
registerProvider(DEFAULT_PROVIDER_NAME, p);
}
public static void registerProvider(String name, Provider p){
providers.put(name, p);
}
// Service access API
public static Service newInstance() {
return newInstance(DEFAULT_PROVIDER_NAME);
}
public static Service newInstance(String name) {
Provider p = providers.get(name);
if (p == null)
throw new IllegalArgumentException(
"No provider registered with name: " + name);
return p.newService();
}
}
Różnica między interfejsem API a interfejsem SPI pojawia się, gdy interfejs API dodatkowo zapewnia pewne konkretne implementacje. W takim przypadku dostawca usług musi zaimplementować kilka interfejsów API (zwanych SPI)
Przykładem jest JNDI:
JNDI zapewnia interfejsy i niektóre klasy do wyszukiwania kontekstowego. Domyślny sposób wyszukiwania kontekstu jest dostępny w IntialContext. Ta klasa wewnętrznie będzie używać interfejsów SPI (przy użyciu NamingManager) do implementacji specyficznych dla dostawcy.
Zobacz poniżej architekturę JNDI dla lepszego zrozumienia.
API oznacza Application Programming Interface, gdzie API jest środkiem dostępu do usługi / funkcji zapewnianej przez oprogramowanie lub platformę.
SPI oznacza Service Provider Interface, gdzie SPI jest sposobem na wstrzyknięcie, rozszerzenie lub zmianę zachowania oprogramowania lub platformy.
Interfejs API jest zwykle docelowy dla klientów w celu uzyskania dostępu do usługi i ma następujące właściwości:
-> API to programowy sposób uzyskiwania dostępu do usługi w celu osiągnięcia określonego zachowania lub wyniku
-> Z punktu widzenia ewolucji API dodawanie nie stanowi żadnego problemu dla klientów
-> Ale API raz używane przez klientów nie może (i nie powinno) być zmieniane / usuwane, chyba że istnieje odpowiednia komunikacja, ponieważ jest to całkowite pogorszenie oczekiwań klienta
Z drugiej strony SPI są skierowane do dostawców i mają następujące właściwości:
-> SPI to sposób na rozszerzenie / zmianę zachowania oprogramowania lub platformy (programowalny vs. programowy)
-> Ewolucja SPI różni się od ewolucji API, w usuwaniu SPI nie ma problemu
-> Dodanie interfejsów SPI spowoduje problemy i może przerwać istniejące implementacje
Aby uzyskać więcej wyjaśnień, kliknij tutaj: Interfejs usługodawcy
FAQ NetBeans: Co to jest SPI? Czym różni się od interfejsu API?
API jest terminem ogólnym - akronimem interfejsu programowania aplikacji - oznacza coś (w Javie, zwykle niektóre klasy Java), na które narażone jest oprogramowanie, które pozwala innym programom na komunikację z nim.
SPI to skrót od Service Provider Interface. Jest to podzbiór wszystkich rzeczy, które mogą być specyficzne dla interfejsu API w sytuacjach, w których biblioteka udostępnia klasy wywoływane przez aplikację (lub bibliotekę API) i które zwykle zmieniają możliwości aplikacji.
Klasycznym przykładem jest JavaMail. Jego interfejs API ma dwie strony:
- Strona API - do której dzwonisz, jeśli piszesz klienta poczty lub chcesz przeczytać skrzynkę pocztową
- Strona SPI, jeśli udostępniasz moduł obsługi protokołu przewodowego, który umożliwia JavaMailowi komunikowanie się z nowym rodzajem serwera, takim jak serwer wiadomości lub serwer IMAP
Użytkownicy interfejsu API rzadko muszą zobaczyć klasy SPI lub porozmawiać z nimi i na odwrót.
W NetBeans, gdy widzisz termin SPI, zwykle mówi się o klasach, które moduł może wstrzykiwać w czasie wykonywania, które pozwalają NetBeans robić nowe rzeczy. Na przykład istnieje ogólny SPI do wdrażania systemów kontroli wersji. Różne moduły zapewniają implementacje tego SPI dla CVS, Subversion, Mercurial i innych systemów kontroli wersji. Jednak kod zajmujący się plikami (po stronie API) nie musi dbać o to, czy istnieje system kontroli wersji, czy co to jest.
Jest jeden aspekt, który nie wydaje się być zbytnio podkreślany, ale jest bardzo ważny, aby zrozumieć powody istnienia podziału API / SPI.
Podział API / SPI jest wymagany tylko wtedy, gdy oczekuje się ewolucji platformy. Jeśli napiszesz interfejs API i „wiesz”, że nie będzie on wymagał żadnych przyszłych ulepszeń, nie ma prawdziwych powodów, aby podzielić kod na dwie części (oprócz stworzenia czystego projektu obiektu).
Ale prawie nigdy tak nie jest i ludzie muszą mieć swobodę ewolucji API wraz z przyszłymi wymaganiami - w sposób kompatybilny wstecz.
Zauważ, że wszystkie powyższe założenia zakładają, że budujesz platformę, z której korzystają inni użytkownicy i / lub je rozbudowujesz, a nie własny interfejs API, w którym masz cały kod klienta pod kontrolą, a zatem możesz go refaktoryzować w dowolny sposób.
Pokażmy to na jednym ze znanych obiektów Java Collection
i Collections
.
API: Collections
to zestaw statycznych metod narzędziowych. Często klasy reprezentujące obiekt API są definiowane, ponieważ final
zapewniają (w czasie kompilacji), że żaden klient nigdy nie może „zaimplementować” tego obiektu i mogą polegać na „wywołaniu” jego metod statycznych, np.
Collections.emptySet();
Ponieważ wszyscy klienci „dzwonią”, ale nie „implementują” , autorzy JDK mogą dodawać nowe metody do Collections
obiektu w przyszłej wersji JDK. Mogą być pewni, że nie złamie żadnego klienta, nawet jeśli prawdopodobnie istnieją miliony zastosowań.
SPI: Collection
jest interfejsem, który sugeruje, że każdy może zaimplementować swoją własną wersję. Dlatego autorzy JDK nie mogą dodawać do niego nowych metod, ponieważ spowodowałoby to uszkodzenie wszystkich klientów, którzy napisali własną Collection
implementację (*).
Zazwyczaj, gdy wymagana jest dodatkowa metoda, należy utworzyć nowy interfejs, np. Collection2
Rozszerzający poprzednią. Klient SPI może następnie zdecydować, czy przeprowadzić migrację do nowej wersji SPI i wdrożyć dodatkową metodę, czy też trzymać się starszej.
Być może już widziałeś sens. Jeśli połączysz oba elementy razem w jedną klasę, interfejs API zostanie zablokowany przed wszelkimi dodatkami. To jest również powód, dla którego dobre interfejsy API i platformy Java nie ujawniają się, abstract class
ponieważ blokowałyby ich przyszłą ewolucję pod względem kompatybilności wstecznej.
Jeśli coś jest nadal niejasne, polecam sprawdzić tę stronę, która wyjaśnia to bardziej szczegółowo.
(*) Zauważ, że jest to prawdą tylko do Java 1.8, która wprowadza koncepcję default
metod zdefiniowanych w interfejsie.
Przypuszczam, że sloty SPI w większym systemie są implementowane przez niektóre funkcje API, a następnie rejestrują się jako dostępne za pośrednictwem mechanizmów wyszukiwania usług. Interfejs API jest używany bezpośrednio przez kod aplikacji użytkownika końcowego, ale może integrować komponenty SPI. To różnica między enkapsulacją a bezpośrednim użyciem.
Interfejs usługodawcy to interfejs usług, który wszyscy dostawcy muszą wdrożyć. Jeśli żadna z istniejących implementacji dostawcy nie działa dla Ciebie, musisz napisać własnego usługodawcę (implementując interfejs usługi) i gdzieś się zarejestrować (zobacz przydatny post Roman).
Jeśli ponownie wykorzystujesz istniejącą implementację interfejsu usługi dla dostawcy, w zasadzie używasz interfejsu API tego konkretnego dostawcy, który obejmuje wszystkie metody interfejsu usługi oraz kilka własnych metod publicznych. Jeśli używasz metod interfejsu API dostawcy poza interfejsem SPI, korzystasz z funkcji specyficznych dla dostawcy.
W świecie Java różne technologie mają być modułowe i „podłączane” do serwera aplikacji. Jest wtedy różnica między
Dwa przykłady takich technologii to JTA (menedżer transakcji) i JCA (adapter do JMS lub bazy danych). Ale są też inni.
Wdrożenie takiej technologii wtykowej musi następnie zaimplementować interfejs SPI, aby można ją było podłączyć w aplikacji. serwer i zapewnić interfejs API do użycia przez aplikację użytkownika końcowego. Przykładem JCA jest interfejs ManagedConnection , który jest częścią SPI, i połączenie, które jest częścią interfejsu API użytkownika końcowego.