Java SPI mechanism and its application in JDBC

What is Java SPI?

The full name of SPI is: Service Provider Interface. In Java util. Serviceloader is described in detail in the documentation. Briefly summarize the idea of Java SPI mechanism. Each abstract module in our system often has many different implementation schemes, such as the scheme of log module, xml parsing module, jdbc module, etc. In object-oriented design, we generally recommend interface programming between modules, and no hard coding of implementation classes between modules. Once a specific implementation class is involved in the code, it violates the principle of pluggability. If you need to replace an implementation, you need to modify the code. In order to realize the module assembly without dynamic indication in the program, a service discovery mechanism is needed.
Java SPI provides such a mechanism: a mechanism to find a service implementation for an interface. Similar to the idea of IOC, it is to move the control of assembly outside the program. This mechanism is particularly important in modular design. The specific convention of Java SPI is: when the service provider provides an implementation of the service interface, create a file named after the service interface in the META-INF/services / directory of the jar package at the same time. This file is the specific implementation class that implements the service interface. When the external program assembles the module, you can find the specific implementation class name through the configuration file in the jar package META-INF/services /, load and instantiate to complete the module injection. Based on such a convention, we can find the implementation class of the service interface without making it in the code. jdk provides a tool class for service implementation lookup: Java util. ServiceLoader.

Java SPI usage demo

  1. Define an interface:
package com.hiwei.spi.demo;
public interface Animal {
    void speak();
}
  1. Create two implementation classes:
package com.hiwei.spi.demo;

public class Cat implements Animal {
    @Override
    public void speak() {
        System.out.println("Meow meow!");
    }
}
package com.hiwei.spi.demo;

public class Dog implements Animal {
    @Override
    public void speak() {
        System.out.println("Woof, woof!");
    }
}
  1. Create META-INF/services directory under resources Directory:

    Create a file named after the interface class path, and add the implementation class path to the file:
com.hiwei.spi.demo.Cat
com.hiwei.spi.demo.Dog
  1. use
package com.hiwei.spi;

import com.hiwei.spi.demo.Animal;
import java.sql.SQLException;
import java.util.ServiceLoader;

public class SpiDemoApplication {

    public static void main(String[] args){
    	//The corresponding implementation class will be found according to the file
        ServiceLoader<Animal> load = ServiceLoader.load(Animal.class);
        //Execute implementation class methods
        for (Animal animal : load) {
            animal.speak();
        }
    }
}

Execution results:

Thus, the program can pluggably execute the defined implementation classes.

Application of SPI in JDBC

With the latest mysql-connector-java-8.0.27 Jar as an example

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.27</version>
</dependency>

When using JDBC to connect to the database, you only need to use:

DriverManager.getConnection("url", "username", "password");

DriverManager has static methods:

    static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
    }

Take a look at the loadInitialDrivers() method, including:

AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
				//Get driver Class implementation class
                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;
            }
        });

As you can see, the driver will be obtained according to the java spi Class, which can be found in mysql-connector-java-8.0.27 Jar, see the defined file below:

The program will find the corresponding implementation class according to the file and connect to the database.

Keywords: Java JDBC SPI

Added by nigma on Sat, 01 Jan 2022 23:26:28 +0200