java - SPI机制

发布时间 2023-07-20 17:49:08作者: 默宁

https://blog.csdn.net/blueheartstone/article/details/128005322

基本概念

Service Provider Interface,服务  提供者  接口。核心思想是:使用者定义接口,服务者实现接口。该思想被大量应用在插件式开发中,轻松实现组件集成。

 

jdk集成mysql的场景示例

1、jdk定义java.sql.Driver接口,明确自己的需求(如:connect(String, Properties) )

 

 

2、各服务方自己实现Driver类(如:com.mysql.jdbc.Driver)

 

 

3、jdk定义DriverManager类来确保java.sql.Driver的实现类可以被无缝集成进来(maven引入即可,不需要任何配置)

// 加载并初始化所有Driver的实现类
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;
    }
     
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
            // !!!这里是核心!!!
            // ServiceLoader.load通过获取当前线程的类加载器,来加载Driver的实现类(这里破坏了jdk的“双亲委派”加载模型)
            // 代码实现为:ClassLoader cl = Thread.currentThread().getContextClassLoader();
            ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
            Iterator<Driver> driversIterator = loadedDrivers.iterator();
            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);
        }
    }
}

扩展:jdk双亲委派模型

jdk使用双亲委派机制是为了实现class的去重加载,避免开发者自定义一个java.xx.yy类后被加载到jvm中、覆盖jdk的原生类

 

而SPI机制中,因为Bootstrap ClassLoader不会去加载com.mysql.jdbc.Driver,但又要使用它,所以jdk提供了 DriverManager 类来作弊,DriverManager会通过Thread.currentThread()来获取加载器 (默认使用Application ClassLoader),让Application ClassLoader去加载com.mysql.jdbc.Driver

 

 

 

 

扩展:策略模式

// TODO