1:SPI是什么
首先,SPI
是jdk制定的一种规范 ,满足服务使用者在符合开闭原则
基础上来编写代码,而能能够实现使用不同的服务提供方提供的服务,既然是规范肯定就需要有接口,而后还要有接口的具体实现,以及具体实现如何暴露给使用者,最后使用者如何使用这些具体实现,都会在SPI
中进行制定。如数据库驱动,日志等,有些抽象,接下里使用一个实例来进行说明其具体的使用方法。
2:实例-搭建项目
本例子会采用maven父子项目方式创建5
个项目(模块),分别如下:
1:spiparent,作为父项目。
2:spiinterface,指定spi规范的接口,简单定义一个sayHi的方法。
3:spiconcrete-english,实现spiinterface中的接口,实现方式为使用英语sayHi。
4:spiconcrete-chinese,实现spiinterface的接口,实现方式为使用汉语sayHi。
5:spi-invoker,spiconcrete-english和spiconcrete-chinese的具体使用者。
先看最终项目创建完毕结构:
2.1:创建spiparent
- new->project
- 选择
maven
,不勾选Create from archetype
- 输入
GAV
信息 - next,输入项目名称
一般和Artifactid
保持一致,因此可不做修改。 - finish
- 删除src
因为作为父项目存在,所以只需要保留pom.xml
,不需要源代码文件和资源文件等,因此直接删除src
。
2.2:创建spiinterface
- 鼠标选中
spiparent
- new->module
- 选择
maven
,不选择Create from archetype
- 输入
ArtifactId为spiinterface
- next->finish
2.3:创建spiconcrete-english
同2.2
,只不过ArtifactId=spiconcrete-english
,设置完如下:
2.4:创建spiconcrete-chinese
同2.2
,只不过ArtifactId=spiconcrete-chinese
,设置完如下:
2.5:创建spi-invoker
同2.2
,只不过ArtifactId=spi-invoker
,设置完如下:
3:实例-写代码
3.1:在spiinterface
定义接口
public interface SayHi {
void sayHi();
}
3.2:在spiconcrete-chinese
实现接口
- 引入依赖项目
spiinterface
<dependencies>
<dependency>
<groupId>dongshi.daddy</groupId>
<artifactId>spiinterface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
- 实现接口
public class ChineseSayHi implements SayHi {
public void sayHi() {
System.out.println("你好,欢迎来到叶寨村!!!");
}
}
- 定义services
做法为在资源文件夹下创建META-INF/services
文件夹,然后创建名称为SPI接口全限定名
的文件,并将ChineseSayHi全限定名
写到文件中,如下:
dongshi.daddy.ChineseSayHi
3.3:在spiconcrete-english
实现接口
- 引入依赖项目
spiinterface
<dependencies>
<dependency>
<groupId>dongshi.daddy</groupId>
<artifactId>spiinterface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
- 实现接口
public class EnglishSayHi implements SayHi {
public void sayHi() {
System.out.println("welcome to yezhaicun country!!!");
}
}
- 定义services
做法为在资源文件夹下创建META-INF/services
文件夹,然后创建名称为SPI接口全限定名
的文件,并将EnglishSayHi全限定名
写到文件中,如下:
dongshi.daddy.EnglishSayHi
4:实例-测试
具体使用会用到SPI规范中提供的工具类java.util.ServiceLoader
,该工具类专门用来从classpath下的META-INF/services
下加载对应的具体服务的实现。
4.1:在spi-invoker
编写测试类
- 引入依赖(先使用
spiconcrete-chinese
)
<dependencies>
<dependency>
<groupId>dongshi.daddy</groupId>
<artifactId>spiinterface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>dongshi.daddy</groupId>
<artifactId>spiconcrete-chinese</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
- 测试代码
public class SayHiTestMain {
public static void main(String[] args) {
ServiceLoader<SayHi> load = ServiceLoader.load(SayHi.class);
for (SayHi sayHi : load) {
sayHi.sayHi();
}
}
}
- 运行
你好,欢迎来到叶寨村!!!
如果此时我们想要使用英语来sayHi,怎么办呢?只需要将spiconcrete-chinese
换为spiconcrete-english
的依赖即可,如下:
<dependencies>
<dependency>
<groupId>dongshi.daddy</groupId>
<artifactId>spiinterface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>dongshi.daddy</groupId>
<artifactId>spiconcrete-english</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
- 再次运行
welcome to yezhaicun country!!!
满足了开闭原则
。
5:源码
这里。
6:在数据库驱动中应用
数据库在计算机领域具有非常重要的地位,因此对于数据相关的驱动则无疑是更加重要的,因为我们使用操作数据库必须依赖于这些驱动程序,因此驱动领域中的规范
就显的尤为的重要了,对于此,jdk大佬们基于SPI的规范定义了数据库驱动的规范接口,该接口是java.sql.Driver
,具体定义如下:
package java.sql;
public interface Driver {
}
接下来以mysql驱动程序为例进行说明。如下是MySQL数据库驱动包截图,可以看到里面定义了META-INF/services/java.sql.Driver
文件,内容为该类的具体实现类:
com.mysql.jdbc.Driver
com.mysql.jdbc.Driver
类定义如下:
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!");
}
}
}
可以看到其实现了数据库驱动SPI规范接口java.sql.Driver
。