SPI
全称是 Service Provider Interface
,是一种 JDK
内置的动态加载实现扩展点的机制,通过 SPI
Technologie Wir können die Implementierungsklasse der Schnittstelle dynamisch erhalten, ohne sie selbst zu erstellen. Dabei handelt es sich nicht um eine spezielle Technologie, sondern lediglich um ein Designkonzept.
Java SPI ist eigentlich ein dynamischer Lademechanismus, der durch eine Kombination aus schnittstellenbasierter Programmierung + Strategiemodus + Konfigurationsdateien implementiert wird.
Für jede Abstraktion des Systemdesigns gibt es oft viele verschiedene Implementierungslösungen. Beim objektorientierten Design wird im Allgemeinen empfohlen, Module basierend auf Schnittstellen zu programmieren, und Implementierungsklassen sollten nicht zwischen Modulen fest codiert werden. Wenn im Code auf eine bestimmte Implementierungsklasse verwiesen wird, wird das Prinzip der Steckbarkeit verletzt. Um die Ersetzung zu implementieren, muss der Code geändert werden. Ein Diensterkennungsmechanismus ist erforderlich, um die Modulassemblierung zu ermöglichen, ohne sie dynamisch im Programm anzugeben.
Java SPI bietet einen Mechanismus zum Finden von Dienstimplementierungen, die sich auf eine bestimmte Schnittstelle beziehen. Im modularen Design wird häufig ein Mechanismus verwendet, der der IOC-Idee ähnelt, d. h. die Montagesteuerung von Komponenten wird außerhalb des Programms übertragen. Die Kernidee von SPI ist also die Entkopplung.
Der Aufrufer aktiviert, erweitert oder ersetzt die Implementierungsstrategie des Frameworks entsprechend den tatsächlichen Anforderungen
Im Folgenden sind einige Szenarien aufgeführt, in denen dieser Mechanismus verwendet wird: JDBC-Treiber, der Treiberklassen für verschiedene Datenbanken lädt
private static final String PREFIX = "META-INF/services/"; 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(); } /** * * 在调用该方法之后,迭代器方法的后续调用将延迟地从头开始查找和实例化提供程序,就像新创建的加载程序所做的 那样 */ public void reload() { providers.clear(); //清除此加载程序的提供程序缓存,以便重新加载所有提供程序。 lookupIterator = new LazyIterator(service, loader); } private class LazyIterator implements Iterator<S>{ Class<S> service; ClassLoader loader; Enumeration<URL> configs = null; Iterator<String> pending = null; String nextName = null; private boolean hasNextService() { if (nextName != null) { return true; } if (configs == null) { try { //找到配置文件 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; } } /** * * 通过反射 实例化配置文件中的具体实现类 */ private S nextService() { if (!hasNextService()) throw new NoSuchElementException(); String cn = nextName; nextName = null; Class<?> c = null; try { c = Class.forName(cn, false, loader); } catch (ClassNotFoundException x) { fail(service, "Provider " + cn + " not found"); } if (!service.isAssignableFrom(c)) { fail(service, "Provider " + cn + " not a subtype"); } try { S p = service.cast(c.newInstance()); providers.put(cn, p); return p; } catch (Throwable x) { fail(service, "Provider " + cn + " could not be instantiated", x); } throw new Error(); // This cannot happen }
Schritt 1
Erstellen Sie die folgende Klassepublic interface IService { /** * 获取价格 * @return */ String getPrice(); /** * 获取规格信息 * @return */ String getSpecifications(); }
public class GoodServiceImpl implements IService { @Override public String getPrice() { return "2000.00元"; } @Override public String getSpecifications() { return "200g/件"; } }
public class MedicalServiceImpl implements IService { @Override public String getPrice() { return "3022.12元"; } @Override public String getSpecifications() { return "30粒/盒"; } }
org.example.GoodServiceImpl
org.example.MedicalServiceImplSchritt 3: Verwenden Sie ServiceLoader, um die in angegebene Implementierung zu laden die Konfigurationsdatei.
public class Main { public static void main(String[] args) { final ServiceLoader<IService> serviceLoader = ServiceLoader.load(IService.class); serviceLoader.forEach(service -> { System.out.println(service.getPrice() + "=" + service.getSpecifications()); }); } }
Ausgabe:
2000,00 Yuan = 200g/Stück3022,12 Yuan = 30 Kapseln/Karton6: Vor- und Nachteile
6.1 Vorteile Durch die Entkopplung wird die Montage logisch gesteuert des Drittanbieter-Servicemoduls und Der Aufrufer Durch die Trennung von Geschäftscode statt dessen Kopplung können Anwendungen Framework-Erweiterungen ermöglichen oder Framework-Komponenten basierend auf tatsächlichen Geschäftsbedingungen ersetzen. Verglichen mit der Methode, ein Schnittstellen-JAR-Paket für Servicemodule von Drittanbietern zur Implementierung der Schnittstelle bereitzustellen, ermöglicht die SPI-Methode, dass sich das Quell-Framework nicht um den Pfad der Schnittstellenimplementierungsklasse kümmern muss
6.2 Nachteile
Allerdings ServiceLoader kann auch als verzögertes Laden angesehen werden. Grundsätzlich kann es jedoch nur durch Durchqueren erhalten werden, dh alle Implementierungsklassen der Schnittstelle werden geladen und instanziiert. Wenn einige Implementierungsklassen geladen und instanziiert werden, Sie sie aber nicht verwenden müssen, werden Ressourcen verschwendet. Die Möglichkeit, eine bestimmte Implementierungsklasse abzurufen, ist zu begrenzt. Sie kann nur in Form eines Iterators abgerufen werden, und die entsprechende Implementierungsklasse kann nicht basierend auf bestimmten Parametern abgerufen werden. Die Verwendung von Instanzen von mehreren gleichzeitigen Threads ist unsicher die ServiceLoader-Klasse.
Das obige ist der detaillierte Inhalt vonWas ist der SPI-Mechanismus in Java?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!