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
- 超级账本项目:链码示例
- 我的第六个网页制作:table标签
- POJ 1163 The Triangle【dp+杨辉三角加强版(递归)】
- UVA 11039-Building designing【贪心+绝对值排序】 UVA11039-Building designing
- UVA 11636-Hello World!(水题,猜结论) UVA11636-Hello World!
- 百度某SDK设计缺陷导致手机敏感信息泄露(IMEI号和地理位置信息等)
- HDU 1004 Let the Balloon Rise【STL<map>】
- UVA 10881 - Piotr's Ants【模拟+思维】
- DFS中的奇偶剪枝学习笔记
- POJ 3154 Graveyard【多解,数论,贪心】
- HDU 1010 Tempter of the Bone【DFS经典题+奇偶剪枝详解】
- Ethereum - 以太坊项目
- COGS 144. [USACO Dec07] 魅力手镯【01背包复习】
- SQL Server 使用全文索引进行页面搜索
- 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 文档注释
- 【Linux】血泪教训 -- 动态链接库配置方法
- FTP文件管理项目(本地云)项目日报(八)
- FTP文件管理项目(本地云)项目日报(七)
- FTP文件管理项目(本地云)项目日报(六)
- Transformers Assemble(PART I)
- FTP文件管理项目(本地云)项目日报(五)
- 几个Python“小伎俩”
- FTP文件管理项目(本地云)项目日报(四)
- BERT源码分析(PART I)
- FTP文件管理项目(本地云)项目日报(二)
- FTP文件管理项目(本地云)项目日报(一)
- 【奇技淫巧】-- 朋友圈(并查集)
- 【奇技淫巧】-- 岛屿的最大面积
- LeetCode精选好题(三)
- 【LeetCode两题选手】算法类题目(7.29)