简介
SPI全称Service Provider Interface,顾名思义,就是接口提供服务。
一般做框架才会用到SPI,就是做抽象层、做标准流程,但是具体服务层要可定制,依赖与具体的业务,就可以使用SPI。
例如,数据驱动,JDK中的java.sql包中基本都是抽象层的东西,不和具体的数据库相关,它怎么实现,就是使用SPI。
当然,要做SPI的实现,也需要对SPI有所了解才行。
简单总结一下:
我定义抽象接口,并面向接口编程提供相应的工具类,提供服务的人来具体实现,其他人使用我的接口,我再使用具体的实现
SPI的优势
-
统一标准,可以使用共同的抽象层,否则每一种数据库的每一种驱动都有自己的标准和实现,这样大家学习成本就高了。
-
标准统一了,做工具的就能使用统一标准,这样又可以方便丰富生态,不用每个渠道都去做一套适配。
以java.sql.Driver为例,JDK定义了java.sql.Driver,告诉大家要做什么,如果大家这样这样做了,用户就可以直接使用JDK提供的java.sql包中的工具就可以操作数据库了,而不用关心MySQL的这种驱动到底怎样实现,MongoDB那种驱动怎么实现。
有点向设计模式中中介模式,都找中介,不用关心其他。
SPI接口实现流程
-
classpath路径中添加一个/META-INF/services目录,一般具体那个classpath目录无所谓,放在JDK的lib目录下都行,不过为了让其他人也能使用你的jar包,最好放在自己的jar包中。
-
在/META-INF/services目录下添加一个要实现的接口的全限定名称的文件。例如,java.sql.Driver,文件中的内容是你实现这个接口的全限定类名,例如,com.mysql.cj.jdbc.Driver。如果有多个实现类,每行一个。
-
实现对应的接口
具体的处理逻辑在ServiceLoader这个类中:
ServiceLoader中的LazyIterator内部类的hasNextService方法,是加载配置资源文件的实现。
ServiceLoader的parseLine方法是具体解析配置文件的实现。
SPI接口提供流程
给定SPI接口,提供相应的工具类,可以参考Java的DriverManager的loadInitialDrivers方法。
dubbo与SPI
dubbo没有使用JDK的ServiceLoader,而是自己写了一个ExtensionLoader来实现相应的逻辑。
反思
我们真的有必要使用SPI么?直接使用接口,提供注册方式它不香吗?
当然这个得具体问题,具体分析。
JDK的java.sql.Driver处理数据库驱动,使用SPI方式比较恰当,因为可以屏蔽用户与三方驱动的关系,减少用户学习成本。
Dubbo使用SPI,倒不是不可以,只是个人觉得没有必要。
比如Dubbo的Filter,Filter这种非常个性化的一般都是特定实现,就是说我用我才会自己去实现,难道有第三方专门提供一些Filter,然后打个jar包给大家用?
不要说Filter,有多少大家使用的Dubbo扩展第三方jar包?
我都自己实现Filter了,当然就不用谈学习成本了,结果还非要我按规则来弄个配置文件,给个注册Filter的接口它不香么?使用注解,给一个注解工具类,它不方便么?