Java服务提供框架

本文探讨Effective Java E3 CH2中提到的服务提供框架,引申点有JDBC实现、SPI机制等。
原文

There are three essential components in a service provider framework: a service interface, which represents an implementation; a provider registration API, which providers use to register implementations; and a service access API, which clients use to obtain instances of the service. The service access API may allow clients to specify criteria for choosing an implementation. In the absence of such criteria, the API returns an instance of a default implementation, or allows the client to cycle through all available implementations. The service access API is the flexible static factory that forms the basis of the service provider framework.
An optional fourth component of a service provider framework is a service provider interface, which describes a factory object that produce instances of the service interface. In the absence of a service provider interface, implementations must be instantiated reflectively (Item 65). In the case of JDBC, Connection plays the part of the service interface, DriverManager.registerDriver is the provider registration API, DriverManager.getConnection is the service access API, and Driver is the service provider interface.
解释及思考
服务提供框架的三个必要组成部分: 服务接口(Service Interface)、服务提供者注册API( Provider Registration API)、服务获取API(Service Access API)
客户端通过调用Service Interface实现功能调用,服务获取API帮助客户端获取服务接口实例,服务获取API通常为静态方法,客户端可以自由选择不同的服务实现。
Provider Registration API注册Service Implementation(什么是注册)
还有一个可选择的组成部分:服务提供接口(Service Provider Interface,SPI)
在日常使用中,SPI提及频率也很高,在没有SPI的情况下,service implementation必须通过反射的方式实例化(why)。
【Java服务提供框架】回顾JDBC的使用方法:
static{ try { //1.加载驱动程序 Class.forName("com.mysql.jdbc.Driver"); //2.获得数据库的连接 conn = DriverManager.getConnection(URL, NAME, PASSWORD); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } }

客户端程序通过Connection对象操作数据库,则Connection相当于service API, DriverManager.getConnection相当于Service Access API. Class.forName("com.mysql.jdbc.Driver") 这个步骤相当于Provider注册,加载mysql驱动之后provider提供的connection实现才是客户端所需的实例; Class.forName("com.mysql.jdbc.Driver")是将Driver类加载到内存中,加载至内存中以后Driver会调用DriverMananger的rigisteDriver方法。
考虑JDBC的实例,总结service interface, provider registration API, Service Access API三者之间的关系和简单叙述为:
  • provider registation API: 调用者告诉提供者,provider做一些服务准备工作(多为provider的必要类加载)。
  • service interface: 调用者直接调用用以获取提供者提供的功能
  • service access API: Service Interface有不同的实现,调用者通过service access API获取所需的interface实例。
    三者关系如图所示:

    Java服务提供框架
    文章图片
    服务提供框架结构图
现在可以继续思考一个问题,在缺席SPI的情况下,provider是如何注册的?原文中有一段说:
An optional fourth component of a service provider framework is a service provider interface, which describes a factory object that produce instances of the service interface. In the absence of a service provider interface, implementations must be instantiated reflectively (Item 65)
原文中说,在没有SPI的情况下,provider只能通过反射的方式实例化service interface, Service Interface实现为新的实例,在运行过程中初始化新的实例只能通过反射的方法。在JDBC API实现中,注册过程使用反射获取新的Diver实例,service interface通过调用Driver实例获取。
SPI是什么,SPI又是如何通过接口的方法避免使用反射的?SPI不与使用反射方法冲突,Java Service Loader中同样应用了反射。原文定义SPI为提供service interface实例的静态工厂。
目前对SPI的理解为:提供caller调用provider的接口,用于provider注册。
扩展
上述模式称之为service provider framework pattern(服务提供者模式),服务提供者模式的变体如
桥接模式,依赖注入框架可以视为service provider。
参考
  • java servicveLoader https://juejin.im/post/5af952fdf265da0b9e652de3
  • Java SPI机制源码分析 https://cxis.me/2017/04/17/Java%E4%B8%ADSPI%E6%9C%BA%E5%88%B6%E6%B7%B1%E5%85%A5%E5%8F%8A%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
  • 桥接模式 https://www.jianshu.com/p/c71562c98258

    推荐阅读