Java SPI的本质

SPI的目的

让调用者调用更加方便

SPI和Interface的本质区别

1 SPI的接口定义和接口实现是分开的(不同jar包),SPI就是一种调用场景(没别的)

2 Interface的接口定义和接口实现都在服务提供方

SPI如何实现

1 底层使用JDK的ServiceLoader

(1)配置文件:META-INF/services/接口名称

(2)配置文件内容为具体的实现类的全限定名

2 具体业务逻辑在SPI接口定义方实现

 没有SPI的时候(以com.mysql.jdbc.Driver为例)

1 没有SPI:需要手动实例化所需的Driver,新加入一种实现就需要再写一遍

2 有SPI:

Class.forName(jdbcClass);
Connection conn = DriverManager.getConnection(jdbcUrl, userName, password);

DriverManager源码

(1)用ServiceLoader加载所有Driver类,调用load方法只是清空了所有的providers

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

    private static void loadInitialDrivers() {
        ...
        ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
        Iterator<Driver> driversIterator = loadedDrivers.iterator();
            try{
                while(driversIterator.hasNext()) {
                    // 调用ServiceLoader的next()
                    driversIterator.next();
                }
            } catch(Throwable t) {
                // Do nothing
            }    
        ...
        
    }
}

(2)核心代码在ServiceLoader中的hasNextService和nextService。

     a) hasNextService会调用系统classLoader(AppClassLoader)去所有classpath中d的META-INF/services/ + 接口名文件下找到类名

     b) nextService会调用Class.forName加载类

public final class ServiceLoader<S>
    implements Iterable<S> {

    public S next() {
            if (acc == null) {
                return nextService();
            } else {
                PrivilegedAction<S> action = new PrivilegedAction<S>() {
                    public S run() { return nextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }


    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
        }


}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值