首页 / JAVA / Java、Spring中的SPI机制
Java、Spring中的SPI机制
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java、Spring中的SPI机制,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5553字,纯文字阅读大概需要8分钟。
内容图文
![Java、Spring中的SPI机制](/upload/InfoBanner/zyjiaocheng/619/63fdb89a0b784afc9fd2b1fcb51b06e6.jpg)
SPI机制简介为Service Provider Interface,简单的总结下java spi机制的思想。我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。
java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制整体机制如下图: SPI实际上就是基于接口的编程+策略模式+配置文件组合实现的动态加载机制系统设计的各个抽象,往往有很多不同的实现方案,在面向对象的设计里,一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可插拔原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能够在不同程序里动态指名,这就需要一种服务发现机制。
Java SPI机制就是提供这样一个机制:为某个接口寻找服务发现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设中这个机制尤为重要,所以SPI的核心思想,就是解耦。类加载机制双亲委派模型java中的类加载器负责加载来自文件系统、网络或者其他来源的类文件。jvm的类加载器默认使用的是双亲委派模式。类加载规则自上而下:尝试去加载类自下而上:检测类是否已加载具体描述:当一个类加载器收到类加载任务时,会先交给自己的父加载器去完成,因此最终加载任务都会传递到最顶层的BootstrapClassLoader,只有当父加载器无法完成加载任务时,才会尝试自己来加载。好处:使用双亲委派模式可以保证类加载的安全性,不管是哪个加载器加载这个类,最终都是委托给顶层的Bootstrap Classloader来加载的,只有父类无法加载才尝试加载,这样就可以保证任何类加载器最终得到的都是同样的一个Object对象。缺陷:子类加载器可以使用父类加载器已经加载的类,反过来则不行,这就意味着子类加载器要是无法加载类,父类更加没办法加载,这就意味着双亲委派模型并不能保证所有类的加载问题。例子:对于SPI接口,都是由核心类库提供的,但是却是由第三方类库去实现,这样就存在一个问题.
SPI的接口是由AppClassloader来加载,BootStrapClassLoader是无法找到实现的,因为它只加载java的核心类库,它也不能代理给AppClassloader,因为它是最顶层的类加载器。解决方案:使用线程上下文类加载器ContextClassloader加载类加载器Bootstrap Classloader 加载jdk自带的rt包下的类文件,是所有类加载的父类Extension Classloader 加载扩展类库jre/lib/etc或者java.ext.dirs系统目录下的类System Classloader 加载classpath环境变量中加载类文件ContextClassLoader线程上下文类加载器jdk1.2开始引入,通过Thread.getContextClassLoader()和setContextClassLoader()来获取和设置类加载器,由此来加载实现了SPI接口的实现类使用场景概括的说,适用于:调用者根据实际使用需要,启用、扩展、或者替换框架的实现策略比较常见的例子:数据库驱动加载接口实现类jdbc加载不同类型的数据库驱动日志门面接口实现类加载SLF4J加载不同提供商的日志实现类SpringDubbo使用介绍要使用java SPI,需要遵循以下约定:当服务提供者提供了接口一种具体实现后,在jar包的META-INF/services目录下创建一个以接口全限名为命名的文件,内容为实现类的全限定名接口实现类所在的jar包放在主程序的classpath中主程序提供java.uti.ServiceLoader动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVMSPI的实现类必须携带一个不带参数的构造方法示例代码1.定义顶级接口JdbcSpiInterfacepublic interface JdbcSpiInterface {
/**
* 获取数据库连接
*
* @param user 用户名
* @param password 密码
* @param driverClassName 驱动类
*/
void getJdbcConnection(String user, String password, String driverClassName);
}
123456789102.新增2个实现类MysqlJdbc、OracleJdbcpublic class OracleJdbc implements JdbcSpiInterface {
@Override
public void getJdbcConnection(String user, String password, String driverClassName) {
System.out.println("Oracle数据库驱动连接");
}
}
12345678public class MysqlJdbc implements JdbcSpiInterface {
@Override
public void getJdbcConnection(String user, String password, String driverClassName) {
System.out.println("Mysql数据库驱动连接");
}
}
123456783.编写测试方法public class Test {
public static void main(String[] args) {
ServiceLoader<JdbcSpiInterface> shouts = ServiceLoader.load(JdbcSpiInterface.class);
for (JdbcSpiInterface s : shouts) {
s.getJdbcConnection("user", "password", "cp.ds.xds.Class");
}
}
}
123456789测试结果[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KEyMicIo-1591170766365)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\1590026903115.png)]
Spring中SPI扩展机制简介在Springboot的自动装配过程中,最终会加载META-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的。从CLASSPATH下的每个jar包中搜索所有META-INF/spring.factories配置文件,其实这里不仅仅是去ClassPath路径下查询,而是会扫描所有路径下的jar包,只不过这个文件只会在ClassPath下的jar包中.源码public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”;
// spring.factories文件的格式为:key=value1,value2,value3
// 从所有的jar包中找到META-INF/spring.factories文件
// 然后从文件中解析出key=factoryClass类名称的所有value值
public static List loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
// 取得资源文件的URL
Enumeration urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List result = new ArrayList();
// 遍历所有的URL
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
// 根据资源文件URL解析properties文件,得到对应的一组@Configuration类
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
// 组装数据,并返回
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
内容总结
以上是互联网集市为您收集整理的Java、Spring中的SPI机制全部内容,希望文章能够帮你解决Java、Spring中的SPI机制所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。