SPI (Interface Service Provider) est un service qui fournit le mécanisme de découverte JDK intégré.
Dans le développement normal, nous avons effectivement en contact avec, mais le développement général vraiment n'ont pas accès, comme jdbc, apache mécanisme de journalisation et ainsi utilisé SPI
L'interface SPI est fournie par les principales bibliothèques Java et le SPI est des cours de mise en œuvre sont inclus dans le chemin (CLASSPATH) que les applications Java dépendent package jar. Par exemple: JDBC est atteint dépend en travers mysql Maven.
La question est, l'interface SPI fait partie des bibliothèques de base Java, par la classe d'amorçage chargeur (Bootstrap Classloader) à charger. SPI est la réalisation par le chargeur de classe de système (ClassLoader System) à charger.
Bootstrap de chargeur de classe lors du chargement est incapable de trouver la classe d'implémentation de SPI, comme indiqué dans le modèle de délégation de parents, la classe chargeur d'amorçage BootstrapClassloader ne peut pas déléguer chargeur de classe système pour charger AppClassLoader. A cette époque, comment résoudre ce problème?
Discussion chargeur de classe de contexte né, il apparaît détruit également le chargeur classe parente modèle de délégation, de sorte que la procédure peut être le chargement des classes inverse.
De l'étude de cas JDBC:
Connection conn = java.sql.DriverManager.getConnection(url, "name", "password");
En général, dans la connexion de base de données ci-dessus, regardez alors la classe DriverManager
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
Il y a un bloc code statique classe DriverManager. De toute évidence, il appelle loadInitialDrivers, alors loadInitialDrivers façons de regarder
private static void loadInitialDrivers() {
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
// If the driver is packaged as a Service Provider, load it.
// Get all the drivers through the classloader
// exposed as a java.sql.Driver.class service.
// ServiceLoader.load() replaces the sun.misc.Providers()
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
/* Load these drivers, so that they can be instantiated.
* It may be the case that the driver class may not be there
* i.e. there may be a packaged driver with the service class
* as implementation of java.sql.Driver but the actual class
* may be missing. In that case a java.util.ServiceConfigurationError
* will be thrown at runtime by the VM trying to locate
* and load the service.
*
* Adding a try catch block to catch those runtime errors
* if driver not available in classpath but it's
* packaged as service and that service is there in classpath.
*/
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
println("DriverManager.initialize: jdbc.drivers = " + drivers);
if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}
Le message clé est que cette classe outil ServiceLoader, il est un outil pour fournir la classe de mise en œuvre de services Jdk trouver: java.util.ServiceLoader
. SPI utilisera le mécanisme qu'il utilise pour obtenir le service
java.sql.Driver du pilote au-dessus de paquet est fourni par la spécification JDK, qui est une interface. Que d'autres fournisseurs est de savoir comment atteindre?
mysql maven introduit il dépendra pot ajouté automatiquement au chemin de classe / CLASSPATH sous
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
Et il fournit un mécanisme pour le profil SPI (doit être META-INF / services chemin classpath)
Les services fournis pour atteindre juste que nous avons introduit les paquets mysql pilote
Les noms de fichiers et les interfaces requises nom de classe entièrement qualifié même java.sql.Driver
com.mysql.cj.jdbc.Driver besoin pour atteindre java.sql.Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver {}
Discussion chargeur de classe de contexte
Au début, lorsque la connexion de base de données, la méthode getConnection ():
// Worker method called by the public getConnection() methods.
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
// Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
// if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}
核心 点: Thread.currentThread () getContextClassLoader ().
Exemple simple:
ColorInterface nouvelle classe d'implémentation d'interface 2 BlackService, WhiteService ont été atteints ColorInterface
package com.shentb.hmb.spi;
public interface ColorInterface {
void sayColor();
}
package com.shentb.hmb.spi.impl;
import com.shentb.hmb.spi.ColorInterface;
public class BlackService implements ColorInterface {
@Override
public void sayColor() {
System.err.println("Black");
}
}
package com.shentb.hmb.spi.impl;
import com.shentb.hmb.spi.ColorInterface;
public class WhiteService implements ColorInterface {
@Override
public void sayColor() {
System.err.println("White");
}
}
Dans le cadre du nouveau projet classpath / répertoire META-INF / services, créer un nouveau fichier com.shentb.hmb.spi.ColorInterface de répertoire, les besoins de nom de fichier et ColorInterface nom complet de la classe de la même
Le contenu est le nom de classe complet de la mise en œuvre du service.