个人理解
SPI就是一种约定机制,通常情况下, 我们服务都会有一个接口和一个实现类,当服务提供者提供某个服务时,需要在/meta-INF/services/目录下创建以接口路径命名的文在这个文件中, 指定我们实现的服务com.demo.impl.UserServiceImpl;package com.tuling;public interface Product { void use();}public class Phone implements Product{ @Override public void use() { System.out.println("call"); }}package com.tuling;public class Food implements Product{ @Override public void use() { System.out.println("eat"); }}
在/meta-INF/services/创建一个com.demo.UserService文件
com.tuling.Phonecom.tuling.Food
JDK 的SPI调用机制
ServiceLoader
通过上面的简单例子, 差不多就可以理解SPI是个什么东西。
SPI机制的作用步入正题, 那么JDK提供SPI机制的作用是什么呢?
解耦与实现组件化,模块化;可以提供一套接口规范, 每个依赖包提供接口实现类,然后在/WEB-INF/services下的以接口路径命令的文件里, 指定包的实现类,项目启动的时候, 就会去扫描/WEB-INF/services下的文件,一起使用; SPI机制常见举例 举个例子: 我们开发使用的DB操作, 一般会有Mysql, Oracle等,那么JDBC中, 有一个DriverManger类, 用来获取数据库连接;
JDBC提供了一个java.sql.Driver 接口,当我们使用不同的数据库时,各大厂商(如Mysql、Oracle)会根据一个统一的规范(java.sql.Driver)开发各自的驱动实现逻辑,客户端使用jdbc时不需要去改变代码,mysql依赖包中 /WEB-INF/services/有一个java.sql.Driver文件, 内容指定了Driver的实现类是com.mysql.jdbc.Driver, oracle依赖包中/WEB-INF/services/也有一个java.sql.Driver,内容指定了Driver的实现类是oracle.jdbc.driver.OracleDriver;
package java.sql;import java.util.Iterator;import java.util.ServiceLoader;import java.security.AccessController;import java.security.PrivilegedAction;import java.util.concurrent.CopyOnWriteArrayList;import sun.reflect.CallerSensitive;import sun.reflect.Reflection;public class DriverManager { @CallerSensitive public static Connection getConnection(String url, java.util.Properties info) throws SQLException { return (getConnection(url, info, Reflection.getCallerClass())); } //...} // Worker method called by the public getConnection() methods. private static Connection getConnection( String url, java.util.Properties info, Class<?> caller) throws SQLException { ClassLoader callerCL = caller != null ? caller.getClassLoader() : null; synchronized(DriverManager.class) { // synchronize loading of the correct classloader. if (callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } } if(url == null) { throw new SQLException("The url cannot be null", "08001"); } for(DriverInfo aDriver : registeredDrivers) { // If the caller does not have permission to load the driver then // skip it. if(isDriverAllowed(aDriver.driver, callerCL)) { try { println(" trying " + aDriver.driver.getClass().getName()); Connection con = aDriver.driver.connect(url, info); if (con != null) { // Success! return (con); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } } else { println(" skipping: " + aDriver.getClass().getName()); } } }
for(DriverInfo aDriver : registeredDrivers) 循环中的registeredDrivers代表的就是所有的Driver实现类,再根据每个驱动Driver实现类去连接具体的数据库;
而我们项目中可以一次引入mysql,oracle等多种数据库, 只需要在Mybatis的配置文件中指定使用哪种数据库, 进而再获取连接的时候,创建相关数据库的连接;
优点:
插件化模块化缺点:
不够灵活;只可以获取所有的 规范实现, 做不到只获取部分规范实现;
项目可能使用10种数据库,就需要导入10种数据库包,我们短时间内只用到了3种 他就会将10种数据库的规范实现Driver全部扫描进来, 而不能只获取3种; 总结
Dubbo的SPI扩展机制就是对Java SPI机制的扩展, 使其更加灵活可用;