最近在看Dubbo源码,dubbo的功能是基于扩展点(Extension)的,如果想要修改哪个模块,可以很方便的进行扩展替换。
这种扩展点就是借鉴的spi的思想,但是dubbo并没有使用jdk原生的serviceLoader,而是自己实现了ExtensionLoader来加载扩展点,支持键值对,更为灵活,遵循的规范基本相同。这是题外话。
什么是SPI?SPI能干什么?这里有篇介绍文章--链接
最初接触到SPI的时候有些困惑,查资料发现很多文章都拿jdbc作为SPI的典型例子。
回忆当初我刚上大学的时候,hibernate、mybatis这类框架还没大火,只能自己写jdbc链接数据库的代码,是这样的
try { Class.forName("com.mysql.jdbc.Driver");//1 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/EScore", "root", "root");//2 pst = conn.prepareStatement("SELECT COUNT(1) FROM score"); ResultSet resultSet = pst.executeQuery(); resultSet.next(); System.out.println(resultSet.getInt(1)); } catch (Exception e) { e.printStackTrace(); }
这个Class.forName("com.mysql.jdbc.Driver")作用就是加载数据库驱动类com.mysql.jdbc.Driver,代码如下
package com.mysql.jdbc; import java.sql.DriverManager; import java.sql.SQLException; public class Driver extends NonRegisteringDriver implements java.sql.Driver { public Driver() throws SQLException { } static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } } }
加载时会先执行上面代码中的静态代码块,通过
DriverManager.registerDriver(new Driver());
com.mysql.jdbc.driver new了一个自己的实例,并注册给了DriverManager,这样DriverManager就能够使用驱动程序去获得数据库连接。
到此为止,驱动已经加载完毕,没有任何关于SPI的应用,我也是一头雾水,不明白SPI跟jdbc有啥关系。但是一想,如果jdbc使用到了spi,那么在DriverManager中一定会有相应实现,继续看DriverManager代码,有静态块如下
static { loadInitialDrivers(); println("JDBC DriverManager initialized"); }
loadInitialDrivers()方法代码如下
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<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);
}
}
}
注意到这个方法里调用了ServiceLoader,来加载驱动文件
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
终于找到了SPI的身影,继续看会发现驱动类的加载是在遍历的时候进行的
Iterator<Driver> driversIterator = loadedDrivers.iterator(); try{ while(driversIterator.hasNext()) { driversIterator.next(); } } catch(Throwable t) { // Do nothing }
loadedDrivers.iterator()返回的是
private LazyIterator lookupIterator;
LazyIterator 是ServiceLoader的内部私有类,实现了terator接口,代码如下
// Private inner class implementing fully-lazy provider lookup // private class LazyIterator implements Iterator<S> { Class<S> service; ClassLoader loader; Enumeration<URL> configs = null; Iterator<String> pending = null; String nextName = null; private LazyIterator(Class<S> service, ClassLoader loader) { this.service = service; this.loader = loader; } 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 } public boolean hasNext() { if (acc == null) { return hasNextService(); } else { PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() { public Boolean run() { return hasNextService(); } }; return AccessController.doPrivileged(action, acc); } } 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); } } public void remove() { throw new UnsupportedOperationException(); } }
可以看到nextService是会用Class.forName去加载驱动类,然后执行驱动类中静态块,然后DriverManager注册一个实例。。。。那么既然DriverManager使用类spi的机制去自动加载所有驱动类,我们在写代码的时候就无需再去
Class.forName("com.mysql.jdbc.Driver");//1
是的,这行代码已经多余了,去掉之后仍然可以正常建立数据库连接,爽
相关推荐
SPI 全称为 Service Provider Interface,是一种服务发现机制。
1.为什么dubbo要自己设计一套SPI.txt
dubbo spi源理代码,从源理的角度去理解dubbo spi的设计原理
本文深入剖析了Dubbo的可扩展机制SPI的源码,带领读者从理论到实践全面理解其工作原理和应用方法。Dubbo是阿里巴巴开源的一款高性能、轻量级的Java RPC框架,其可扩展机制SPI是其核心特性之一,能够帮助开发者轻松...
dubbo的核心思想就是SPI思想,想读懂dubbo源码应该认真的看下,详细的讲解了dubbo的SPI思想,从为什么,是什么,怎么做的来分析了dubbo的spi思想
dubbo spi可扩展机制源码解析
首先,从知识层面对Dubbo有一个了解和认识,请看《Dubbo培训与实战.pptx》,然后把Dubbo用到实际项目中来,请看实例代码《Dubbo实例代码(Sping+Dubbo+Maven).zip》,里面包括dubboDemoProvide和dubboDemoConsumer...
深度解析Dubbo的可扩展机制SPI源码:从理论到实践,打造高效、稳定的分布式服务框架
dubbo服务注册到eureka
Dubbo服务注册与发现.doc,这份文档讲解Dubbo服务注册与发现的例子
dubbo资源 dubbo-admin dubbo demo
构建dubbo项目的时候会遇到: Multiple annotations found at this line: - cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'dubbo:application'. - ...
我感觉很不错的宝贝,现在和大家分享,希望能够帮到大家,如果你需要可以下载看看,很适合喜欢研究技术的人员
dubbo示例代码dubbo-sample
dubbo提供与调用方配置文件,及web配置,涉及主要用于dubbo
incubator-dubbo-dubbo-2.6.1
本套视频从分布式系统的基本概念出发,由浅入深,讲解了RPC原理,Dubbo基本使用,Dubbo高可用场景以及Dubbo原理,涉及了分布式系统中服务注册、服务发现、负载均衡、灰度发布、集群容错、服务降级等核心概念的讲解及...