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