最近在看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
是的,这行代码已经多余了,去掉之后仍然可以正常建立数据库连接,爽
相关推荐
#### 四、Dubbo SPI与Java SPI的区别 1. **按需加载**:Dubbo SPI机制允许根据实际情况按需加载服务实现,避免了不必要的资源消耗。 2. **错误处理**:Dubbo SPI机制对于加载失败的情况有更好的容错性和错误提示,...
- **接口定义**:使用Java的接口和注解来定义服务,Dubbo支持JDBC、Hessian、JSON等多种数据交换格式。 - **服务暴露**:服务提供者通过配置文件或编程方式将服务注册到注册中心。 - **服务消费**:服务消费者...
SPI 机制也可以用于框架的扩展,例如 Dubbo 的 SPI 实现。 Java SPI 机制的优点是: 1. 解耦:SPI 机制将接口和实现分离,使得程序更加灵活和可扩展。 2. 可扩展性:SPI 机制允许第三方提供实现类,扩展程序的功能...
Dubbo SPI(Service Provider Interface)是阿里巴巴开源的Java服务发现框架,它允许开发者在运行时动态地查找和加载...此外,了解SPI还可以帮助开发者更好地理解和使用其他依赖于SPI的Java库,如Hadoop、JDBC驱动等。
常见的使用SPI机制的框架有JDBC、SLF4J、Spring、Dubbo等。以JDBC为例,JDBC定义了一套标准的数据库连接规范,各个数据库厂商只需要提供一个实现了JDBC接口的驱动程序即可。当使用JDBC连接某个数据库时,可以通过SPI...
这一机制为很多框架扩展提供了可能,比如在Dubbo、SpringBoot的自动装配,以及我们现在要介绍的JDBC都用到了SPI技术。 3. JDBC架构设计 JDBC架构设计的目的是为了解决操作数据库的差异,JDK推出了JDBC规范,来实现...
4. **Dubbo SPI**:Dubbo的SPI机制与JDK SPI略有不同,配置文件放在`META-INF/dubbo`目录下。 **问题重现与排查** 在实际开发中,遇到SPI配置不生效的情况,通常需要检查以下几个方面: - 配置文件是否按照规定路径...
空中漫步查明拉链框架开发,例如:dubbo,spring, arthas,jrebel等。 连接池 连接池 德鲁伊,C3P0, 光明CP 完毕 一个简单的连接池: Java JUC 完毕 Java CAS java-cas 完毕 Java SPI java-spi JDBC 完毕 ...
ShardingSphere作为Apache软件基金会的一员,积极参与了中国区的Apache项目,如Apache CarbonData、Dubbo、Eagle、Griffin、HAWQ、Kylin、RocketMQ等,共同推动开源技术的发展。 总结来说,Apache ShardingSphere是...
源码分析之JDBC实现原理与SPI机制 tomcat和dubbo对于JDK线程池的修改 源码分析之Java线程池ThreadPoolExecutor 常见工具 [源码分析之Guava RateLimiter源码分析](source_code/源码分析之Guava RateLimiter源码...