目录

​1.什么是SPI​

​2.为什么要使用SPI思想​

​3.SPI 调用机制​

​4.SPI 具体应用案例​

​4.1 JDK​

​4.2 DriverManager​

​4.3 Spring 自定义标签​



1.什么是SPI

        SPI:Service Provider Interface,是Java提供的一套被第三方实现或者扩展的接口,用来启用框架扩展和替换组件。

2.为什么要使用SPI思想

         为了降低耦合性、提高系统可扩展性。一般来说对于未知的实现或者对扩展开放的系统,通常会把一些逻辑抽成各个模块,这些模块往往有很多不同的实现方案,比如对接不同第三方日志系统、对接不同数据库驱动、编程软件需要加载一些用户开发的插件等。总结来说,SPI思想适用于调用者根据实际使用需要,启用、扩展、或者替换框架的实现策略。

3.SPI 调用机制

Java SPI 插件化开发_java

4.SPI 具体应用案例

4.1 JDK

Java SPI 插件化开发_spring_02        

非常简单的四个类

Leads:线索接口

public interface Leads {
/**
* 接收线索分发器下发的线索
* @return 线索名称
*/
String receiveLeads();
}

LeadsManager:线索管理类

public class LeadsManager {

public static synchronized void initLeadsManager() {
loadInitialParsers();
}

private static void loadInitialParsers() {
System.out.println("LeadsManager initialized");
ServiceLoader<Leads> loadedParsers = ServiceLoader.load(Leads.class);

for (Leads lead : loadedParsers) {
System.out.println(lead.receiveLeads());
}
}
}

BmwLeads:宝马线索实现类

public class BmwLeads implements Leads {

@Override
public String receiveLeads() {
return "宝马线索下发成功";
}
}

BenzLeads:奔驰线索实现类

public class BenzLeads implements Leads {

@Override
public String receiveLeads() {
return "奔驰线索下发成功";
}
}

 APP:测试类

public class App {
public static void main(String[] args) {
LeadsManager.initLeadsManager();
}
}

另外还有两个重要的配置文件: 

Java SPI 插件化开发_mysql_03

Java SPI 插件化开发_java_04

注: 1.需要在resources目录下新建META-INF/services目录

        2.在resources/META-INF/services新建一个文件,命名与需扩展接口的全限定名一致

        3.在这个文件中写入接口的实现类的全限定名

        4.接口实现类所在的jar包放在主程序的classpath中

        5.主程序通过java.util.ServiceLoder动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM中

        6.SPI的实现类必须携带一个不带参数的构造方法

Java SPI 插件化开发_mysql_05

4.2 DriverManager

       在加载MySql驱动的时,我们都调用了这样一行函数Class.forName("com.mysql.jdbc.Driver"),但为什么调用这样一行代码就可以加载mysql驱动了呢?其实这里也运用了SPI的思想。SDK提供了这样一个接口:java.sql.Driver

Java SPI 插件化开发_SPI_06

        而不同的数据库厂商又对这个接口进行了不同的实现,比如mysql的Driver:

Java SPI 插件化开发_java_07

        继续跟进JDK提供的DriverManager类:

Java SPI 插件化开发_spring_08

Java SPI 插件化开发_java_09

         见到了我们熟悉的代码ServiceLoader.load(XXX.class);同时在loadInitialDrivers()方法中也有 Class.forName()。

Java SPI 插件化开发_mysql_10

         最后去看下mysql的驱动包是否满足java SPI加载条件:

Java SPI 插件化开发_SPI_11

Java SPI 插件化开发_java_12

4.3 Spring 自定义标签

        扩展Spring自定义标签配置需要以下几个步骤

  1. 创建需要扩展的组件
  2. 定义XSD文件描述组件内容
  3. 创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义
  4. 创建Handler文件,扩展字NamespaceHandlerSupport,目的是将组件注册到Spring容器
  5. 编写Spring.handlers和Spring.schemas文件

        Spring为我们定义好了创建标签的流程,而无需了解我们具体的实现,只需要我们通过Spring提供的加载方式把插件加载进来,体现了spi的思想。