java SPI function sharing
1. What is SPI
SPI, Service Provider Interface, is a set of API s provided by java to be implemented or extended by third parties. Its essence is to realize dynamic loading through interface based programming + policy mode + configuration file. It is mainly used by framework developers, such as JDBC driver Java sql. Driver interface. Different database manufacturers complete the operation of the database by implementing the secondary interface. mysql and other databases have different implementation classes for users, while Java SPI mechanism can find specific implementation classes for an interface.
2. Several conventions for realizing SPI
1. After the service provider provides a specific implementation of the interface, create a file named "fully qualified name of the interface" in the META-INF/services directory of the jar package, and the content is the fully qualified name of the implementation class;
2. The jar package where the interface implementation class is located is placed in the classpath of the main program;
3. The main program passes Java util. Serviceloder dynamically loads the implementation module, which finds the fully qualified name of the implementation class by scanning the configuration file in the META-INF/services directory, and loads the class into the JVM;
4. The implementation class of SPI must carry a construction method without parameters;
3. Examples of SPI implementation
Step 1: define the interface
public interface LoadBalance { String selectServiceAddress(List<String> serviceAddresses); }
Step 2: define the implementation class
public class RandomLoadBalance implements LoadBalance { @Override public String selectServiceAddress(List<String> serviceAddresses) { Random random = new Random(); return serviceAddresses.get(random.nextInt(serviceAddresses.size())); } }
Step 3: add configuration file
Add the META-INF/services / directory under the resources file directory to create a file named after the service interface. The content of this file is the specific implementation class of the interface. The contents are as follows:
com.spi.javaspi.loadbalance.RandomLoadBalance
Step 4: use ServiceLoader to load the implementation class
public static void main(String[] args) { ServiceLoader<LoadBalance> loadBalances = ServiceLoader.load(LoadBalance.class); Iterator<LoadBalance> matcherIter = loadBalances.iterator(); while (matcherIter.hasNext()) { LoadBalance loadBalance = matcherIter.next(); System.out.println(loadBalance.getClass().getName()); System.out.println(loadBalance.selectServiceAddress(Arrays.asList("172.30.30.231", "172.30.30.232", "172.30.30.233"))); } }
4. Analysis of SPI usage in JDBC
JDBC (java Data Base Connectivity) is a Java API for executing SQL statements. It can provide unified access to a variety of relational databases. It is composed of a group of classes and interfaces written in java language.

JDBC operation database demo:
Connection con; public Connection getConnection() { try { con = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/shop?characterEncoding=UTF-8", "root", "123456789"); System.out.println("Database connection succeeded"); } catch (SQLException e) { e.printStackTrace(); } return con; } public static void main(String[] args) throws Exception { JDBCTest c = new JDBCTest(); Connection connection = c.getConnection(); PreparedStatement statement = connection.prepareStatement("select * from Product"); ResultSet resultSet = statement.executeQuery(); while (resultSet.next()) { String productName = resultSet.getString("product_name"); System.out.println("productName: " + productName); } }
Correlation class analysis -- DriverManager
Static code block:
static { loadInitialDrivers(); println("JDBC DriverManager initialized"); }
loadInitialDrivers method:
private static void loadInitialDrivers() { String drivers; drivers = AccessController.doPrivileged(new PrivilegedAction<String>() { public String run() { return System.getProperty("jdbc.drivers"); } }); AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator<Driver> driversIterator = loadedDrivers.iterator(); while (driversIterator.hasNext()) { driversIterator.next(); } return null; } }); String[] driversList = drivers.split(":"); for (String aDriver : driversList) { Class.forName(aDriver, true, ClassLoader.getSystemClassLoader()); } }
Register driver method:
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>(); public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException { if(driver != null) { registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); } else { throw new NullPointerException(); } }
Driver implementation class in mysql
public class Driver extends NonRegisteringDriver implements java.sql.Driver { static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } public Driver() throws SQLException { // Required for Class.forName().newInstance() } }
Get connection method:
private static Connection getConnection( String url, java.util.Properties info, Class<?> caller) throws SQLException { ClassLoader callerCL = caller != null ? caller.getClassLoader() : null; synchronized(DriverManager.class) { if (callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } } SQLException reason = null; for(DriverInfo aDriver : registeredDrivers) { if(isDriverAllowed(aDriver.driver, callerCL)) { try { Connection con = aDriver.driver.connect(url, info); if (con != null) { return (con); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } } } }
5. Analysis of advantages and disadvantages
Advantages: decoupling is realized through SPI, and expansion can be realized without changing the source code
Disadvantages: the SPI of the JDK standard instantiates all implementations of the extension point at one time. If there is an extension implementation, the initialization takes a long time (for example, the initialization of static code blocks takes a long time). If it is not used, it will waste resources
6. Other applications of SPI mechanism
Dubbo, spring, log4j and other frameworks also make extensive use of SPI mechanism