spring的spi
spi是什么?
SPI(service provider interface)机制是JDK内置的一种服务发现机制,可以动态的发现服务,即服务提供商,它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。目前这种大部分都利用SPI的机制进行服务提供,比如:dubbo、spring、JDBC、等;
spi解决了什么问题?
由于classLoader加载类的时候采用是【双亲委托模式】,意思是:首先委托父类去加载器获取,若父类加载器存在则直接返回,若加载器无法完成此加载任务,自己才去加载。该加载存在的弊端就是上层的类加载永远无法加载下层的类加载器所加载的类,所以通过spi解决了该问题。
spi是一种将服务接口与服务实现分离以达到解耦、大大提升了程序可扩展性的机制。引入服务提供者就是引入了spi接口的实现者,通过本地的注册发现获取到具体的实现类,轻松可插拔spi实现了动态加载,插件化,
弊端:
资源浪费:由于 spi 是通过循环加载实现类,会导致所有的类全部一起加载!
有哪些SPI机制?
jcl-over-slf4j-1.7.5.jar
dubbo
mysql-connector-java-5.1.35.jar
等等...
原因
简单的说,类加载器中的双亲委派模型的工作原理是对于类的加载,先交给父类加载器完成,如果父类加载器无法完成加载,子类加载器才进行加载。JDK中的一些基础类接口,例如 JDBC,如果按照双亲委派模型,JDBC只能用启动类加载器完成驱动的加载,但是驱动的实现是在用户目录的 jar 包实现,所以启动类加载器就无法加载用户的目录的驱动类了(下面可以从代码实例了解一下)
ServiceLoader 的源码中的介绍如下
代码下载:https://gitee.com/hong99/spring/issues/I1N1DF
代码实现
jdk spi实现
位置图
/**
*
* 功能描述:接口
*
* @param:
* @return:
* @auther: csh
* @date: 2020/8/17 15:13
*/
public interface Robot {
void sayHello();
}
package com.hong.spring.spi.service.impl;
import com.hong.spring.spi.service.Robot;
/**
*
* 功能描述: 大黄峰
*
* @param:
* @return:
* @auther: csh
* @date: 2020/8/17 15:14
*/
public class Bumblebee implements Robot {
@Override
public void sayHello() {
System.out.println("Hello, I am Bumblebee.");
}
}
package com.hong.spring.spi.service.impl;
import com.hong.spring.spi.service.Robot;
/**
*
* 功能描述: 擎天柱
*
* @param:
* @return:
* @auther: csh
* @date: 2020/8/17 15:14
*/
public class OptimusPrime implements Robot {
@Override
public void sayHello() {
System.out.println("Hello, I am Optimus Prime.");
}
}
META-INF/services/com.hong.spring.spi.service.Robot
com.hong.spring.spi.service.impl.Bumblebee
com.hong.spring.spi.service.impl.OptimusPrime
spi_test.JdkSpiTest
package spi_test;
import com.hong.spring.spi.service.Robot;
import sun.misc.Service;
import java.util.Iterator;
import java.util.ServiceLoader;
/**
* @Auther: csh
* @Date: 2020/8/17 14:53
* @Description:jdk的spi测试
*/
public class JdkSpiTest {
/**
*
* 功能描述:测试spi
*
* @param:
* @return:
* @auther: csh
* @date: 2020/8/17 15:16
*/
public static void main(String[] args) {
Iterator <Robot> providers = Service.providers(Robot.class);
ServiceLoader <Robot> load = ServiceLoader.load(Robot.class);
while (providers.hasNext()){
Robot next = providers.next();
next.sayHello();
}
System.out.println("==============================");
Iterator <Robot> iterator = load.iterator();
while (iterator.hasNext()){
Robot next = iterator.next();
next.sayHello();
}
}
}
结果
Hello, I am Bumblebee.
Hello, I am Optimus Prime.
==============================
Hello, I am Bumblebee.
Hello, I am Optimus Prime.
spring spi实现
spring的spi是通过SpringFactoriesLoader类loadFactories方法来加载的,包目录是:org.springframework.core.io.support
文件格式是:配置放在 META-INF/spring.factories中;
public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
Assert.notNull(factoryClass, "'factoryClass' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
}
List<T> result = new ArrayList<T>(factoryNames.size());
for (String factoryName : factoryNames) {
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
代码下载:https://gitee.com/hong99/spring/issues/I1N1DF
最后:
不管是jdk 的spi或者 spring spi都很好遵循了开闭原则,即对修改关闭,对拓展开放,其实看了很多开源的框架后都发现,很多框架都是遵循原则,并且在里面应用了大量的设计模式。而spi 的思想应用广泛就是应用了策略模式,不管是jdk/jdbc/spring/dubbo等框架都有用到,只是所标注的名字不同而且,实现原理大致相同。spi主要是解決是为了解决因为类上层加载器无法去加载下层加载器这个问题,当然也存在一个弊端就是扫描了所有类实现...,
参考文章:
https://www.cnblogs.com/helloz/p/10961026.html
jdK spi:https://blog.csdn.net/rzpy_qifengxiaoyue/article/details/107977448
双亲委托:https://www.cnblogs.com/hiyujie/p/wo-xueJava1ClassLoader-yu-shuang-qin-wei-tuo-mo-sh.html
https://www.cnblogs.com/hiyujie/p/wo-xueJava1ClassLoader-yu-shuang-qin-wei-tuo-mo-sh.html
https://learnku.com/articles/45733
dubbo官方:http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html
- 特殊的物化视图刷新 (r4笔记第77天)
- 通过单例模式模拟RAC连接 (r4笔记第76天)
- 网站上的验证码是怎么产生的?
- Java基础-17(01)总结,登录注册案例,Set集合,HashSet
- mongoDB初探第一篇(r4笔记第75天)
- 重温快速排序(r4笔记第73天)
- Java基础-17(01)总结,TreeSet,LinkHashSet
- 海量数据迁移之数据抽取流程 (r4笔记第72天)
- CSS Selectors Level 4新特性全面解析
- 巧用外部表避免大量的insert (r4笔记第71天)
- 如何用java语言实现C#中的ref关键字(按引用传递参数)的效果
- 16(01)总结List的子类,ArrayList,Vector,LinkedList
- 16(02)总结泛型
- 基于 React + Webpack 的音乐相册项目(上)
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- VBA解压缩ZIP文件01——实现的功能
- MyVBA加载宏——添加自定义菜单03——功能分析
- MyVBA加载宏——添加自定义菜单02——给按钮添加单击事件
- 科研猫小课堂:敲黑板!竞争风险模型应该如何分析?
- 常用功能加载宏——快速定位合并单元格
- 常用功能加载宏——调用微信截图
- 常用功能加载宏——一维表转二维表
- OpenCV与图像处理(八)
- 类模块
- Netty入门篇
- 面试官:谈谈类加载器吧,你有没有看过类加载器的源码?
- 数据维度爆炸怎么办?详解5大常用的特征选择方法
- 给卷积神经网络动动刀:加法网络探究
- 用Python网络爬虫来看看最近电影院都有哪些上映的电影
- 使用 Azure Blob Stoage 实现一个静态文件服务器