1、What is SPI???

  1. 1SPI{Service Procider Interface},是JDK内置的一种服务提供发现机制。
  2. 2SPI是一种动态替换发现的机制,比如有个接口,想运行时动态的给它添加实现,你只需要添加一个实现。
  3. 3、如下图中:接口对应的抽象SPI接口,实现放实现SPI接口,调用放依赖SPI接口。
  4. 4SPI接口定义在调用方,在概念上更依赖调用方。
  5. 4-1、当服务的提供者提供一个接口的实现之后,需要在classpath下的META-INF/service/目录下创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类。
  6. 4-2、当其他的程序需要这个服务的时候,就可以通过查找这个jar包(一般都是以jar包做依赖)的META-INF/services/中的配置文件,配置文件中有接口的具体实现类名,可以根据这个类名进行加载实例化,然后就可以使用该服务了。
  7. 4-3JDK中查找服务实现的工具类是:java.util.ServiceLoader.

1-1、SPI-概念示意图

image.png

1-2、SPI的用途-举例

  1. 1SPI机制应用:DriverManagerSpringConfigurableBeanFactory等。

1-2-1、SPI的用途-举例-DriverManager

  1. 1DriverManager jdbc 里管理和注册不同数据库 driver 的工具类。针对一个数据库,可能会存在着不同的数据库驱动实现。我们在使用特定的驱动实现时,不希望修改现有的代码,而希望通过一个简单的配置达到效果。
  2. 2Driver源码:
  3. public class Driver extends NonRegisteringDriver implements java.sql.Driver {
  4. public Driver() throws SQLException {
  5. }
  6. static {
  7. try {
  8. //静态代码块中,调用DriverManager的注册驱动方法new一个自己当参数传给驱动管理器
  9. DriverManager.registerDriver(new Driver());
  10. } catch (SQLException var1) {
  11. throw new RuntimeException("Can't register driver!");
  12. }
  13. }
  14. }
  15. 3Mysql DriverManager 源码:
  16. public class DriverManager {
  17. // List of registered JDBC drivers
  18. private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
  19. private DriverManager(){}
  20. static {
  21. loadInitialDrivers();
  22. println("JDBC DriverManager initialized");
  23. }
  24. //静态内部类,用到了SPI工具类ServiceLoader
  25. private static void loadInitialDrivers() {
  26. String drivers;
  27. try {
  28. drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
  29. public String run() {
  30. return System.getProperty("jdbc.drivers");
  31. }
  32. });
  33. } catch (Exception ex) {
  34. drivers = null;
  35. }
  36. AccessController.doPrivileged(new PrivilegedAction<Void>() {
  37. public Void run() {
  38. // 用到了SPI工具类ServiceLoader
  39. ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
  40. Iterator<Driver> driversIterator = loadedDrivers.iterator();
  41. //加载SPI扫描到的驱动来触发他们的初始化{触发static代码块}
  42. try{
  43. while(driversIterator.hasNext()) {
  44. driversIterator.next();
  45. }
  46. } catch(Throwable t) {
  47. // Do nothing
  48. }
  49. return null;
  50. }
  51. });
  52. println("DriverManager.initialize: jdbc.drivers = " + drivers);
  53. if (drivers == null || drivers.equals("")) {
  54. return;
  55. }
  56. String[] driversList = drivers.split(":");
  57. println("number of Drivers:" + driversList.length);
  58. for (String aDriver : driversList) {
  59. try {
  60. println("DriverManager.Initialize: loading " + aDriver);
  61. Class.forName(aDriver, true,
  62. ClassLoader.getSystemClassLoader());
  63. } catch (Exception ex) {
  64. println("DriverManager.Initialize: load failed: " + ex);
  65. }
  66. }
  67. }
  68. public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException {
  69. registerDriver(driver, null);
  70. }
  71. public static synchronized void registerDriver(java.sql.Driver driver,DriverAction da)throws SQLException {
  72. /* Register the driver if it has not already been added to our list */
  73. // 将自己注册大片驱动管理器的驱动列表中
  74. if(driver != null) {
  75. registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
  76. } else {
  77. // This is for compatibility with the original DriverManager
  78. throw new NullPointerException();
  79. }
  80. println("registerDriver: " + driver);
  81. }
  82. //当获取连接的时候调用驱动管理器的连接方法从列表中(之前处理的map)获取
  83. @CallerSensitive
  84. public static Connection getConnection(String url,
  85. java.util.Properties info) throws SQLException {
  86. return (getConnection(url, info, Reflection.getCallerClass()));
  87. }
  88. // Worker method called by the public getConnection() methods.
  89. private static Connection getConnection(
  90. String url, java.util.Properties info, Class<?> caller) throws SQLException {
  91. /*
  92. * When callerCl is null, we should check the application's
  93. * (which is invoking this class indirectly)
  94. * classloader, so that the JDBC driver class outside rt.jar
  95. * can be loaded from here.
  96. */
  97. ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
  98. synchronized(DriverManager.class) {
  99. // synchronize loading of the correct classloader.
  100. if (callerCL == null) {
  101. callerCL = Thread.currentThread().getContextClassLoader();
  102. }
  103. }
  104. if(url == null) {
  105. throw new SQLException("The url cannot be null", "08001");
  106. }
  107. println("DriverManager.getConnection(\"" + url + "\")");
  108. // Walk through the loaded registeredDrivers attempting to make a connection.
  109. // Remember the first exception that gets raised so we can reraise it.
  110. SQLException reason = null;
  111. for(DriverInfo aDriver : registeredDrivers) {
  112. // If the caller does not have permission to load the driver then
  113. // skip it.
  114. if(isDriverAllowed(aDriver.driver, callerCL)) {
  115. try {
  116. println(" trying " + aDriver.driver.getClass().getName());
  117. Connection con = aDriver.driver.connect(url, info);
  118. if (con != null) {
  119. // Success!
  120. println("getConnection returning " + aDriver.driver.getClass().getName());
  121. return (con);
  122. }
  123. } catch (SQLException ex) {
  124. if (reason == null) {
  125. reason = ex;
  126. }
  127. }
  128. } else {
  129. println(" skipping: " + aDriver.getClass().getName());
  130. }
  131. }
  132. // if we got here nobody could connect.
  133. if (reason != null) {
  134. println("getConnection failed: " + reason);
  135. throw reason;
  136. }
  137. println("getConnection: no suitable driver found for "+ url);
  138. throw new SQLException("No suitable driver found for "+ url, "08001");
  139. }
  140. }
  141. 4ServiceLoader源码:
  142. public final class ServiceLoader<S> implements Iterable<S> {
  143. private static final String PREFIX = "META-INF/services/";
  144. private final Class<S> service;
  145. private final ClassLoader loader;
  146. private final AccessControlContext acc;
  147. private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
  148. private LazyIterator lookupIterator;
  149. //构造方法
  150. private ServiceLoader(Class<S> svc, ClassLoader cl) {
  151. service = Objects.requireNonNull(svc, "Service interface cannot be null");
  152. loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
  153. acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
  154. reload();
  155. }
  156. public void reload() {
  157. providers.clear();
  158. lookupIterator = new LazyIterator(service, loader);
  159. }
  160. //内部类
  161. private class LazyIterator implements Iterator<S> {
  162. Class<S> service;
  163. ClassLoader loader;
  164. Enumeration<URL> configs = null;
  165. Iterator<String> pending = null;
  166. String nextName = null;
  167. private LazyIterator(Class<S> service, ClassLoader loader) {
  168. this.service = service;
  169. this.loader = loader;
  170. }
  171. private boolean hasNextService() {
  172. if (nextName != null) {
  173. return true;
  174. }
  175. if (configs == null) {
  176. try {
  177. //加载META-INF/services/文件夹下类名为文件名的子类,将其加载到虚拟机
  178. String fullName = PREFIX + service.getName();
  179. if (loader == null)
  180. configs = ClassLoader.getSystemResources(fullName);
  181. else
  182. configs = loader.getResources(fullName);
  183. } catch (IOException x) {
  184. fail(service, "Error locating configuration files", x);
  185. }
  186. }
  187. while ((pending == null) || !pending.hasNext()) {
  188. if (!configs.hasMoreElements()) {
  189. return false;
  190. }
  191. pending = parse(service, configs.nextElement());
  192. }
  193. nextName = pending.next();
  194. return true;
  195. }
  196. private S nextService() {
  197. if (!hasNextService())
  198. throw new NoSuchElementException();
  199. String cn = nextName;
  200. nextName = null;
  201. Class<?> c = null;
  202. try {
  203. c = Class.forName(cn, false, loader);
  204. } catch (ClassNotFoundException x) {
  205. fail(service,
  206. "Provider " + cn + " not found");
  207. }
  208. if (!service.isAssignableFrom(c)) {
  209. fail(service,
  210. "Provider " + cn + " not a subtype");
  211. }
  212. try {
  213. S p = service.cast(c.newInstance());
  214. providers.put(cn, p);
  215. return p;
  216. } catch (Throwable x) {
  217. fail(service,
  218. "Provider " + cn + " could not be instantiated",
  219. x);
  220. }
  221. throw new Error(); // This cannot happen
  222. }
  223. public boolean hasNext() {
  224. if (acc == null) {
  225. return hasNextService();
  226. } else {
  227. PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
  228. public Boolean run() { return hasNextService(); }
  229. };
  230. return AccessController.doPrivileged(action, acc);
  231. }
  232. }
  233. public S next() {
  234. if (acc == null) {
  235. return nextService();
  236. } else {
  237. PrivilegedAction<S> action = new PrivilegedAction<S>() {
  238. public S run() { return nextService(); }
  239. };
  240. return AccessController.doPrivileged(action, acc);
  241. }
  242. }
  243. public void remove() {
  244. throw new UnsupportedOperationException();
  245. }
  246. }
  247. }
  1. @CallerSensitive
  2. public static Connection getConnection(String url,
  3. String user, String password) throws SQLException {
  4. java.util.Properties info = new java.util.Properties();
  5. if (user != null) {
  6. info.put("user", user);
  7. }
  8. if (password != null) {
  9. info.put("password", password);
  10. }
  11. return (getConnection(url, info, Reflection.getCallerClass()));
  12. }