ServiceLoad中的spi

1、简介
  • JDK1.6引入的特性,用来实现SPI(Service Provider Interface),一种服务发现机制。
2、JDBC举例
2.1、引入mysql依赖jar
        <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version></dependency>

2、前置了解:
  • JDBC:Java DataBase Connectivity,是sun公司提供的一套操作数据库的标准规范
  • java.sql.Driver 接口是所有 JDBC 驱动程序需要实现的接口(规范)
  • 这个接口是提供给数据库厂商使用的,不同数据库厂商提供不同的实现(mysql、Oracle)
  • mysql的Driver也需要实现这个接口,以下是mysql的具体实现类
package com.mysql.jdbc;
import java.sql.DriverManager;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!");}}
}
  • mysql的jar中把具体实现的类Driver,放在META-INF/services中接口全路径file即java.sql.Driver中,内容为具体的实现类全路径com.mysql.jdbc.Driver

2.3、数据库连接过程(这里重点分析一中的spi)
        String url = "jdbc:mysql://localhost:3306/test?user=root&password=root";  //定义连接数据库的url//一、获取mysql的数据库连接对象Connection conn = DriverManager.getConnection(url);//二、获取SQL语句执行对象Statement statement = conn.createStatement();//三、执行SQL语句int result = statement.executeUpdate("sql语句");
2.4、分析过程

将服务中所有.jar包下META-INF/services/java.sql.Driver文件中的实现类(eg:mysql的com.mysql.jdbc.Driver)加载到内存list中,并在getConnection中从list中获取实例对象

public class DriverManager {static {loadInitialDrivers();}public static Connection getConnection(String url)///}}

2.4.1、通过spi,获取所有Driver接口实现类对象,放入list

1)、DriverManager.getConnection(url),因为getConnection是静态方法,会触发DriverManager类的首次主动使用,会调用()即调用static{}中loadInitialDrivers

private static void loadInitialDrivers() {// 一、loadServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);Iterator<Driver> driversIterator = loadedDrivers.iterator();// 二、hasNextwhile(driversIterator.hasNext()) {// 三、nextdriversIterator.next();}}

2)、load():指定上下文类加载器加载作为Driver接口的具体实现类com.mysql.jdbc.Driver的加载器

    public static <S> ServiceLoader<S> load(Class<S> service) {ClassLoader cl = Thread.currentThread().getContextClassLoader();//上下文加载器,去加载Deiver接口的实现类return ServiceLoader.load(service, cl);}
打破双亲委派机制:

SPI接口实现类的加载需要破坏双亲委派模型。

  • java.sql.Driver是由根类加载器加载(因为其在rt.jar下)

  • 而不同厂商具体的实现(mysql、Oracle)的jar放在classpath(com|META-INF)下,根类加载器无法加载classpath路径下的类

  • 通过Thread.currentThread().setContextClassLoader,设置系统类加载器来加载(线程上下文中默认存放的系统类加载器)

3)、hasNext():按行读取所有.jar中含有"META-INF/services/com.sql.Driver"的file文件内容,将实现类全路径加入Iterator

        private boolean hasNextService() {//1.首次加载为nullif (configs == null) {// 2.这里的fullName = "META-INF/services/" + com.sql.DriverString fullName = PREFIX + service.getName();if (loader == null)configs = ClassLoader.getSystemResources(fullName);else// 3.获取url(不重要)configs = loader.getResources(fullName);}while ((pending == null) || !pending.hasNext()) {// 4.会读取所有.jar,含有"META-INF/services/com.sql.Driver"的file文件// 一行一行的读取file的内容,比如读取com.msql.jdbc.Driver,将Diver接口所有的实现类的全路径放IteratorIterator<String> pending = parse(service, configs.nextElement());//parseLine(service,names)}nextName = pending.next();return true;}

4)、next():创建Driver接口实现类的实例对象,并将实例对象放入list,便于后续getConnection时从list中获取实例对象

      private S nextService() {// 1.Diver接口实现类的全路径,比如com.mysql.jdbc.DriverString cn = nextName;nextName = null;Class<?> c = null;try {// 2.使用上下文加载器,创建class(com.mysql.jdbc.Driver)类对象c = Class.forName(cn, false, loader);} try {// 3.生成Driver实现类的实例对象(重要)S p = service.cast(c.newInstance());providers.put(cn, p);return p;} }// 这里特别重要的一个点是c.newInstance(),即对实现类Diver创建实例
根据类的首次主动使用原则,会触发com.mysql.jdbc.Driver类的static{}
public class Driver extends NonRegisteringDriver implements java.sql.Driver {static {try {DriverManager.registerDriver(new Driver());} }
}//registerDriver方法,会将com.mysql.jdbc.Driver实例,加入list中,便于后续getConnection的获取
public static synchronized void registerDriver(java.sql.Driver driver){/* Register the driver if it has not already been added to our list */if(driver != null) {//CopyOnWriteArrayList<DriverInfo> registeredDrivers registeredDrivers.addIfAbsent(new DriverInfo(driver, da));}
}
  1. 4.2 getConnection
    public static Connection getConnection(String url)return (getConnection(url, info, Reflection.getCallerClass()));}
private static Connection getConnection(String url, java.util.Properties info, Class<?> caller) throws SQLException {// CopyOnWriteArrayList<DriverInfo> registeredDrivers// 这里遍历上述2.4.1中存入list的所有Driver接口的实现类对象,然后connect,这里以com.mysql.jdbc.Driver的connect// 为例for(DriverInfo aDriver : registeredDrivers) {Connection con = aDriver.driver.connect(url, info);if (con != null) {println("getConnection returning " + aDriver.driver.getClass().getName());return (con);}      }
public class NoRegisterDriver{public Connection connect(String url, Properties info) throws SQLException {// 1.一般我们都会使用.properties将数据库连接的url、port、user、password等属性放在文件中使用Properties加载Properties props = null;// 2.创建mysql的数据库连接(localhost即ip地址信息、port端口信息等,都可以从String url = "jdbc:mysql://localhost:3306/test?user=root&password=root"中解析)com.mysql.jdbc.Connection newConn = ConnectionImpl.getInstance(this.host(props), this.port(props), props, this.database(props), url);return newConn;}}}
}protected static Connection getInstance(String hostToConnectTo, int portToConnectTo, Properties info,              String databaseToConnectTo, String url)  {// 底层还是通过反射,Constructor ctor.newInstance(args)创建的JDBC4对应的mysql的con对象}

至此,就得到了mysql数据库的con连接对象

3、自定义操作

3.1 定义client模块,内含自定义接口Myservice以及其实现类MyServiceImpl

public interface MyService {public void show();
}public class MyServiceImpl implements MyService {@Overridepublic void show() {System.out.println("load MyServiceImpl spi");}
}

3.2 将自定义接口实现类全路径,作为内容写在清单文件file(自定义接口全路径)中

  • 创建清单文件:resources/META-INF/services/创建file,file的名称为自定义接口的全路径:com.mjp.service.MyService
  • file清单文件内容自定义接口实现类的全路径:com.mjp.service.impl.MyServiceImpl(可以在多行写多个实现类)
  • install模块client生成jar,后续在service中pom引入此jar

3.3 定义service模块

  • pom中依赖client模块
    <dependencies><dependency><groupId>CodeBetter</groupId><artifactId>com.mjp.client</artifactId><version>1.0.0-SNAPSHOT</version></dependency></dependencies>
  • 编写ServiceLoader
    public static void main(String[] args) {ServiceLoader<MyService> services = ServiceLoader.load(MyService.class);for (MyService service : services) {service.show();//load MyServiceImpl spi}}

4、作用

spi服务发现机制,更具有插拔性。

  • 每当接口有新的实现类时,只要实现类遵循file文件的存放目录以及命名和内容,则ServiceLoader.load就帮你把所有的实现类都加载到ServiceLoader<接口> loadedServices中,程序员无需感知,可以直接在代码中使用新增的实现类对象,而不是在硬编程的方式,自己再new实现类对象。

SPI(Service Provider Interface)相关推荐

  1. Service Provider Interface(SPI)

    目录 1.什么是SPI 2.SPI的使用 3.源码分析: 4.应用 1.什么是SPI SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口 ...

  2. Java基础学习总结(145)——Java SPI(Service Provider Interface)简介

    SPI 简介 SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制.一个服务(Service)通常指的是已知的接口或者抽象类,服务提供方就是对 ...

  3. SPI(Service Provider Interface)详解

    介绍 SPI 全称为 (Service Provider Interface) ,是JVM内置的一种服务提供发现机制.Java在语言层面为我们提供了一种方便地创建可扩展应用的途径.我们只需要按照SPI ...

  4. Java中的SPI(Service Provider Interface介绍及示例

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 一个服务 ...

  5. SNE(Service Network Engine)

    1 背景 随着NGN.IMS等以IP为承载核心的网络逐步投入建设和商用,以及3G.WiFi.WiMAX等无线接入技术的不断涌现,电信业务超向于: 融合 用户可以通过多种终端.多种接入手段随时随地享受多 ...

  6. CFI(Common Flash Interface)详解

    原文链接:https://blog.csdn.net/emma3slp/article/details/5591086 [什么是CFI] CFI(Common Flash Interface),是JE ...

  7. 【转】JNI(Java Native Interface)的简介

    因为刚刚接触android和java平台,对JNI到底是什么,还不是很了解,所以从CSDN转载了一篇文章,以便自己对JNI有一个认识,也跟大家分享一下. JNI是Java Native Interfa ...

  8. SCA (Service Component Architecture)

    http://www.ibm.com/developerworks/cn/webservices/lp/sca/ SCA (Service Component Architecture)是为实现 SO ...

  9. Android JNI(Java Native Interface)技术介绍

    Android平台上的JNI技术介绍 JUL 15TH, 2013 | COMMENTS NDK简介 Android是由Google领导开发的操作系统,Android依靠其开放性,迅速普及,成为目前最 ...

最新文章

  1. c语言程序设计日历推后几天是星期几,C语言程序设计: 输入年月日 然后输出是星期几...
  2. python读文件每一行、并把这行替换-python基础--文件操作实现全文或单行替换
  3. RT-Thread 学习笔记(五)—— RTGUI代码解读
  4. java8新特性-lambda表达式和stream API的简单使用
  5. jenkins pipeline、用户权限管理、插件下载地址更改、凭证管理、自由风格项目构建、maven项目构建、常用的构建触发器、邮件发送、SonarQube代码审查
  6. codeforces1552 D. Array Differentiation(思维+暴力)
  7. oracle中join另一个表后会查询不出一些数据_面试必备 | 8个Hive数据仓工具面试题锦集!...
  8. matlab 投票法_张量投票算法及其使用并分析.pdf
  9. access如何保存小数点后_如何把示波器当记录仪用
  10. Android.mk宏定义demo【转】
  11. java数据结构队列杨辉三角_数据结构之队列——输出杨辉三角形
  12. Sql Server2008中自定义函数调用存储过程解决方案
  13. Ensembl数据库简介
  14. python课程结课感悟_python听课的感悟与建议
  15. 全键盘 linux 手机,当年青葱的岁月:10佳全键盘智能手机盘点
  16. MySQL Deamon少量解读
  17. [VS2010]逸雨清风 校园网视频,网吧视频(光音网视)下载器 V0.26
  18. msvcr120.dll丢失怎样修复?msvcr120.dll文件修复办法
  19. 批处理---findstr命令详解
  20. Oracle 数据库转码 US7ASCII ZHS16GBK

热门文章

  1. 如何从数据库中筛选出达成指定里程碑节点的项目_复盘|项目管理实战经验总结...
  2. android 声音大小修改器,吃鸡音效修改器游戏
  3. js中的slice方法(开始索引,结束索引-不包含该索引元素)-截取和splice方法-删除(开始索引,删除个数)和插入-(开始索引,删除个数,插入内容)
  4. 可以在电脑上面看电视了
  5. C++筑器 引用生死唯一
  6. CentOs 下SSH 免密码登陆
  7. 58-Vue高级实战
  8. android 关于HOME键
  9. 打造特色文化夜游项目及探索科技创新
  10. 来自知乎 linux的一些玩法