Java SPI的原理和实践

1. Java SPI的概念和术语

  • SPI:全称是Service Provider Interface,是一种基于接口的动态扩展机制Java SPI相当于Java里面提供了一套接口,然后第三方可以实现这个接口来完成功能的扩展
  • 简答来说,我们可以定义一个标准接口,然后第三方的库可以实现这个接口,那么,程序在运行时,会根据配置信息动态加载第三方实现的类,从而完成功能的动态扩展。
  • 它是从Java 6开始引入的,是一种基于ClassLoader发现并加载服务的机制。
  • 一个标准的SPI,由3个组件构成,分别是:
  1. Service - 服务接口 / 扩展接口:是一个公开的接口或抽象类,作为扩展的标准,定义了一个抽象的功能模块。
  2. Service Provider - 服务实现:是Service接口的一个实现类。
  3. ServiceLoader - 服务发现&加载:是SPI机制中的核心组件,对应的是JDK中的ServiceLoader类,调用它的load()方法,根据接口名称就可以找到claapath目录下的所有的扩展实现,我们就可以在运行时发现并加载ServiceProvider,负责在运行时发现并加载Service Provider。

Java SPI的原理和实践_第1张图片

2. Java SPI在JDBC中的应用 

JDBC,全称是Java DataBase Connectivity

  • 简单来说JDBC就是使用Java语言来访问数据库的一套API
  • 每个数据库厂商会提供各自的JDBC实现,这些实现就是我们通常说的"数据库驱动Driver"
Java SPI的原理和实践_第2张图片 JDBC的架构

最上层是Java应用程序,会调用JDBC的API,然后再映射到对应的数据库驱动,最后通过驱动去访问数据库。

Java SPI的原理和实践_第3张图片

如何加载数据库驱动的?

Java SPI的原理和实践_第4张图片为什么Class.forName就能加载数据库驱动呢?

  • 因为JDBC要求Driver实现类在类加载的时候,就需要将自身的实例对象自动注册到DriverManager中,从而加载数据库驱动。
  • JDK里面定义了数据库驱动类 java.sql.Driver是一个接口,JDK并没有提供具体实现,具体实现是由第三方的数据库厂商来完成的。

看到上面这些都是硬编码的类名,一个有素养的程序员,自然而言就会想到这些类名是不是可以写到配置文件里,这样当我更换数据库驱动时,就可以不用修改代码了。

不过,这好像还是不够完美,我还需要记住不同的数据库厂商提供的Driver的类名,能不能和数据库厂商商量一下,干脆让他们把配置文件也一并提供得了,这样一来,程序员省事,数据库厂商也省事,程序员不用了解具体的驱动类名,而厂商也可以轻松升级驱动。

Java SPI的原理和实践_第5张图片

问题来了:如果由数据库厂商来提供配置文件,我们如何去读取它呢?

  • 还记得类ClassLoader吗,它除了可以加载类之外,还提供了方法getResource() / getResources(),可以根据指定的路径,读取classpath中对应的文件。我们可以用它来读取厂商放在Jar包中的配置文件,当然我们要时间约定好配置文件的路径和格式就行。

 你还真是个天才,这套机制,我们就叫它"SPI"吧。

Java SPI的原理和实践_第6张图片

最终,JDBC使用Java SPI来加载数据库驱动,这样的好处就是自己只需引入所需数据库的JAR包即可。

我们来看一下SPI出现之后,我们是如何使用JDBC的?

Java SPI的原理和实践_第7张图片

从示例代码可以看到,程序员再也不用调用Class.forName了,而且如果需要更换数据库驱动,只需要更换驱动所依赖的Jar包即可。

3. 总结 - Java SPI的三大规范要素

  1. 规范的配置文件:从文件路径、文件名称以及文件内容都有标准的要求!
  • 文件路径:要求必须在Jar包中的META-INF/Services目录下
  • 文件名称:Service接口的全限定名,以接口的全限定名命名配置文件
  • 文件内容:Service接口的实现类(即Service Provider类)的全限定名。如果有多个实现类,那么每一个实现类在文件中单独占据一行。

Java SPI的原理和实践_第8张图片

我们以MySQL为例,将mysql-connect-java的Jar包解压后,可以看到目录META_INF/services下存在一个名为java.sql.Driver的配置文件,而文件内容则是MySQL的数据库驱动类。

2. Service Provider类必须具备无参的默认构造方法:

  • Service接口的实现类,即Service Provider类,必须具备无参的默认构造方法。因此随后通过反射技术实例化它,是不带参数的。

3. 要保证能通过ClassLoader加载到配置文件和Service Provider类,一般来说,将Jar包放在          classpath目录中就行。

4. 总结一下Java  SPI

作用:

  • 提供了一种组件发现和注册的方式,可以用于实现各种插件,或者灵活替换框架所使用的组件。

优点:

  • 基于面向接口编程,优雅的实现模块之间的解耦

设计思想:

  • 面向接口 + 配置文件 + 反射技术! 

应用场景:

  • JDBC + SLF4J + Servlet容器初始化 + SpringFactoriesLoader 

5. Java SPI的设计思想与SpringBoot自动配置的核心实现

SpringBoot自动配置,即大名鼎鼎的Auto-Configuration:

  • 它是指基于你引入的依赖Jar包,对SpringBoot应用进行自动配置
  • 提供了自动配置功能的Jar包,通常被称为starter,例如:mybatis-spring-boot-starter等等
  • Auto-Configuration机制:基于你引入的依赖Jar包,对SpringBoot应用进行自动配置,换言之,就是自动的jji将其它Jar包中的配置类注入到Spring的IOC容器当中。

Java SPI的原理和实践_第9张图片Java SPI的原理和实践_第10张图片 

Java SPI的原理和实践_第11张图片

6. SpringFactories机制 

Java SPI的原理和实践_第12张图片

你可能感兴趣的:(java,开发语言)