java学习-原生服务发现-SPI
SPI : service provider interface, 其实际是java提供的一套用于被第三方实现或扩展的API
其体现了 java 基于接口编程的思想, 可以搭配策略模式以及配置文件实现自定义扩展
例如 :数据库驱动,日志,spring,以及dubbo/druid等
关于SPI 相关配置文件
根据 java.util.ServiceLoader 中的注释, 首先需要创建一个 待扩展类全名称的文件, 对于 待扩展类建议要求使用 interface / abstract class ,并不推荐使用 concrete class;
对于文件中的内容要求为当前服务提供者的子级(继承/实现),且要求必须存在一个公共的无参构造函数支持实例化
对于文件所处路径必须为 META-INF/services 路径中
具体使用
使用 java.util.ServiceLoader#load(java.lang.Class<S>), 其还存在一个重载方法支持传递自定义classLoader
通过传递serviceProvider 类型来创建一个ServiceLoader对象, 通过获取其迭代器,遍历迭代器来实现扩展服务加载;
对于服务实现类的加载是在调用 迭代时生成的, 第一次加载时主要是利用到了反射, 当第一次加载完成后会存储到本地缓存中
代码示例
自定义接口作为 serviceProvider
/* * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved */ package com.xingguo.spi; /** * Animal * * @author guoxing * @date 2020/12/20 12:17 PM * @since */ public interface Animal { void eat(); }
自定义相关实现类
/* * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved */ package com.xingguo.spi; /** * Cat * * @author guoxing * @date 2020/12/20 12:18 PM * @since */ public class Cat implements Animal { @Override public void eat() { System.out.println("cat eat fish!!!"); } }
/* * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved */ package com.xingguo.spi; /** * Human * * @author guoxing * @date 2020/12/20 12:19 PM * @since */ public class Human implements Animal { @Override public void eat() { System.out.println("human eat anything what they can eat"); } }
自定义spi文件(无后缀名)
文件路径 META-INF/services/com.xingguo.spi.Animal 文件中的内容, 使用"#"表示注释 #当前文件中也可以不存在任何内容 #要求当前concrete class 必须和当前文件名称所对应的类 存在层级关系,否则运行时会抛出错误 #且要求实现类中必须存在一个公共的无参构造函数 com.xingguo.spi.Cat com.xingguo.spi.Human
测试类
/* * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved */ package com.xingguo.spi; import java.util.Iterator; import java.util.ServiceLoader; /** * AnimalSPIDemo * {@link ServiceLoader} * {@link Animal src/META-INF/services/com.xingguo.spi.Animal} * * @author guoxing * @date 2020/12/20 12:20 PM * @since */ public class AnimalSPIDemo { public static void main(String[] args) { ServiceLoader<Animal> animals = ServiceLoader.load(Animal.class, Thread.currentThread().getContextClassLoader()); Iterator<Animal> iterator = animals.iterator(); while (iterator.hasNext()) { Animal next = iterator.next(); next.eat(); } } }
利用SPI实现 数据库 驱动加载
通过spi的方式来替代直接使用 Class.forName 加载的方式
要求工程引入相关数据库厂商的驱动jar包
spi文件定义
文件路径 META-INF/services/java.sql.Driver 文件中的内容 # mysql driver com.mysql.cj.jdbc.Driver
相关测试类
/* * Copyright (c) 2020, guoxing, Co,. Ltd. All Rights Reserved */ package com.xingguo.spi.driver; import java.sql.Driver; import java.util.Iterator; import java.util.ServiceLoader; /** * SqlDriverDemo * * @author guoxing * @date 2020/12/20 1:04 PM * @since */ public class SqlDriverDemo { public static void main(String[] args) { ServiceLoader<Driver> load = ServiceLoader.load(Driver.class); Iterator<Driver> iterator = load.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); // 输出的结果为 com.mysql.cj.jdbc.Driver@对象内存地址 } } }

浙公网安备 33010602011771号