ServiceLoader和DriverManager的前世今身 JDBC获取数据库连接的方式 ServiceLoader 源码分析 核心方法 小结 DriverManager loadInitialDrivers 因为DriverManager实现主要依靠了ServiceLoader来完成,因此这里先来看看ServiceLoader干了什么: 在Java应用中存在着很多服务提供者接口(Service Provider 下面就来看看ServiceLoader是如何加载的吧。 ClassLoader loader) { //返回一个 ServiceLoader实例,当然这里不算是单例模式 return new ServiceLoader< 按照SPI规范去寻找所有可能存在的驱动实现类 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class
ServiceLoader主要的功能是用来完成对SPI的provider的加载。 先看下它的成员:
public final class ServiceLoader
implements Iterable {
private static final String 其构造方法是一个private方法,不对外提供,在使用时我们需要调用其静态的load方法,由其自身产生ServiceLoader对象:
public static ServiceLoader<S 其中fail方法时ServiceLoader的静态方法,用于异常的处理,后面给出。
在configs初始化完成后,还需要完成pending的初始化或者添加。 ServiceLoader源码分析到此全部结束。
前提
紧接着上一篇《通过源码浅析JDK中的资源加载》,ServiceLoader是SPI(Service Provider Interface)中的服务类加载的核心类,也就是,这篇文章先介绍ServiceLoader ServiceLoader的使用
这里先列举一个经典的例子,MySQL的Java驱动就是通过ServiceLoader加载的,先引入mysql-connector-java的依赖:
<dependency 4、使用ServiceLoader加载接口类,获取接口的实现的实例迭代器。 ServiceLoader源码分析
上面通过一个经典例子和一个实例介绍了ServiceLoader的使用方式,接着我们深入分析ServiceLoader的源码。 接着看ServiceLoader提供的静态方法:
public static ServiceLoader load(Class service, ClassLoader loader
使用案例
通常情况下,使用ServiceLoader来实现SPI机制。SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。 <IRepository> serviceLoader = ServiceLoader.load(IRepository.class);
Iterator<IRepository> it = serviceLoader.iterator();
while (it ! ().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
public static ServiceLoader 但是实际上联系待实现接口和实现接口的类之间的关系并不只是在构造ServiceLoader类的过程中完成的,而是在迭代器的方法hasNext()中实现的。
----
二、ServiceLoader类的内部实现逻辑
Service类的构造方法是私有的,所以我们只能通过掉用静态方法的方式来返回一个ServiceLoader的实例:
方法的参数为被实现结构的Class ServiceLoader<HelloInterface> loaders = ServiceLoader.load(HelloInterface.class);
其内部实现逻辑如所示,不妨按调用步骤来分步讲述 <>(service, loader);
}
完成的工作:
调用了类ServiceLoader的私有构造器
3.私有构造器的源代码:
private ServiceLoader(Class ,如果要注册了多个接口的实现类,那么显得效率不高;
虽然通过静态方法返回,但是每一次Service.load方法的调用都会产生一个ServiceLoader实例,不属于单例设计模式;
ServiceLoader 、相关引用
ServiceLoader使用及原理分析
Create Extensible Applications using Java ServiceLoader
ServiceLoader是SPI的是一种实现,所谓SPI,即Service Provider Interface,用于一些服务提供给第三方实现或者扩展,可以增强框架的扩展或者替换一些组件。 ExternalClusterManager] = { val loader = Utils.getContextOrSparkClassLoader val serviceLoaders = ServiceLoader.load ; } } 测试 package bigdata.spark.services; import java.util.ServiceLoader; public class test { static ServiceLoader<DoSomething> loader = ServiceLoader.load(DoSomething.class); public static void
JDK ServiceLoader 的使用步骤 在分析 ServiceLoader 的使用原理之前,我们先来介绍下 ServiceLoader 的使用步骤。 这个方法内部通过 ServiceLoader 提供的迭代器 Iterator遍历了所有驱动实现类。那么,ServiceLoader 是如何实例化 Driver 接口的实现类的呢? ServiceLoader 源码解析 3.1 ServiceLoader 入口方法 ServiceLoader 提供了三个静态泛型工厂方法,内部最终将调用 ServiceLoader.load(Class 继续往下看~ 3.4 包装迭代器 其实 ServiceLoader 本身就是实现 Iterable 接口的: ServiceLoader.java public final class ServiceLoader ServiceLoader 源码分析总结 理解 ServiceLoader 源码之后,我们总结要点如下: 4.1 约束 1、服务实现类必须实现服务接口 S( if (!
SPI机制基本思路是通过JDK提供的java.util.ServiceLoader类去主动发现服务,不需要硬编码具体的类。 > serviceLoader = ServiceLoader.load(WorkerService.class);
WorkerService service = null; Process finished with exit code 0
ServiceLoader源码分析
ServiceLoader是一个final类,不能被继承,实现了Iterable接口,可以遍历,如下 :
public final class ServiceLoader implements Iterable
ServiceLoader属性如下:
private static final new ServiceLoader<>(service, loader);
}
public static ServiceLoader load(Class service
来加载所有可用的Logger实现: ServiceLoader<Logger> serviceLoader = ServiceLoader.load(Logger.class); for (Logger <Logger> serviceLoader = ServiceLoader.load(Logger.class); List<Logger> loggers = new ArrayList<>(); 加载工厂类,并通过工厂方法创建实现类的实例: ServiceLoader<LoggerFactory> serviceLoader = ServiceLoader.load(LoggerFactory.class <Logger> serviceLoader = ServiceLoader.load(Logger.class); return serviceLoader.iterator().next 例如,可以选择最新版本的实现: ServiceLoader<Logger> serviceLoader = ServiceLoader.load(Logger.class); Logger selectedLogger
当ServiceLoader尝试加载服务实现类但无法找到或解析服务的配置文件时,会抛出此异常。典型的场景包括: 使用ServiceLoader来动态加载某些接口的实现类,例如在插件化开发中。 以下是一个典型的使用ServiceLoader加载服务实现类的代码片段: ServiceLoader<MyService> serviceLoader = ServiceLoader.load(MyService.class ); for (MyService service : serviceLoader) { service.execute(); } 在上述代码中,ServiceLoader会从META-INF/ <MyService> serviceLoader = ServiceLoader.load(MyService.class); for (MyService service : serviceLoader <MyService> serviceLoader = ServiceLoader.load(MyService.class); for (MyService service : serviceLoader
SPI中查找接口的实现类是通过java.util.ServiceLoader,而在java.util.ServiceLoader类中有一行代码如下:
// 加载具体实现类信息的前缀,也就是以接口命名的文件需要放到 进入java.util.ServiceLoader的源码,可以看到ServiceLoader类实现了java.lang.Iterable接口,如下所示。 public final class ServiceLoader implements Iterable
说明ServiceLoader类是可以遍历迭代的。 从MyServiceLoader类调用ServiceLoader.load(clazz)方法进入源码,如下所示:
//根据类的Class对象加载指定的类,返回ServiceLoader对象
public ().getContextClassLoader();
//动态加载指定的类,将类加载到ServiceLoader中
return ServiceLoader.load(service, cl);
ServiceLoader还实现了Iterator接口。 com.fsx.serviceloader.MyIService // 注意这个是接口 com.fsx.serviceloader.HDFSService com.fsx.serviceloader.LocalService <IService> serviceLoader = ServiceLoader.load(IService.class); for (IService service : serviceLoader ,返回一个ServiceLoader的实现,而ServiceLoader实现了Iterable接口,所以可以通过ServiceLoader来遍历所有在配置文件中定义的类的实例。 Bean一样,类比Spring吧 只是没那么强大而已 ServiceLoader<FileSystem> serviceLoader = ServiceLoader.load(FileSystem.class
<Animal> serviceLoader = ServiceLoader.load(Animal.class);
Iterator<Animal> animalIterator = 结构
很显然,分析SPI机制的原理,从ServiceLoader源码中load方法切入即可,但是需要先从核心类的结构开始分析;
public final class ServiceLoader implements (service, cl);
}
/**
* ServiceLoader构造方法
*/
private ServiceLoader(Class svc, load(Class service,ClassLoader loader) {
return new ServiceLoader<>(service, loader); <Driver> loadedDrivers = ServiceLoader.load(java.sql.Driver.class);
Iterator<Driver>
<DatabaseInterface> serviceLoader = ServiceLoader.load(DatabaseInterface.class); for (DatabaseInterface 用于实例化ServiceLoader,但并不会加载SPI接口的具体实现类,而是采用懒加载的方式,迭代时才进行加载从ServiceLoader.load方法进入,发现类加载使用的是当前线程的类加载器public (); return ServiceLoader.load(service, cl);}最终load方法会调用的ServiceLoader构造方法进行初始化,然后调用reload方法ServiceLoader ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); Iterator 实现,ServiceLoader类加载实现类时可能打破双亲委派模型,父类加载器的职责交给子类加载器执行ServiceLoader迭代器优先采用缓存,没有缓存才进行懒加载SPI接口的实现类迭代器hasNext
④ ServiceLoader ServiceLoader是JDK内置的SPI利器,主要负责读取SPI配置文件并将第三方SPI实现类加载到JVM中。 1. driversInitialized) { // ServiceLoader.load()并不会立即去加载第三方厂商驱动,其只是返回一个ServiceLoader 实例而已 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); driversInitialized) { // ServiceLoader.load()并不会立即去加载第三方厂商驱动,其只是返回一个ServiceLoader 实例而已 ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
SPI加载的核心就是ClassLoader的getResource系列方法,jdk提供了一个工具类,就是上面说的ServiceLoader。 ServiceLoader
public final class ServiceLoader implements Iterable{
...
}
它实现了Iterable接口。 它的入口方法是load
public static ServiceLoader load(Class service, ClassLoader loader){
return new ServiceLoader<>(service, loader);
}
public static ServiceLoader load(Class service) { 而且,ServiceLoader实例在多线程环境中不安全。
当ServiceLoader尝试加载服务实现类但无法找到或解析服务的配置文件时,会抛出此异常。典型的场景包括: 使用ServiceLoader来动态加载某些接口的实现类,例如在插件化开发中。 以下是一个典型的使用ServiceLoader加载服务实现类的代码片段: ServiceLoader<MyService> serviceLoader = ServiceLoader.load(MyService.class ); for (MyService service : serviceLoader) { service.execute(); } 在上述代码中,ServiceLoader会从META-INF/ <MyService> serviceLoader = ServiceLoader.load(MyService.class); for (MyService service : serviceLoader <MyService> serviceLoader = ServiceLoader.load(MyService.class); for (MyService service : serviceLoader
java.util.ServiceLoader包中:
public static ServiceLoader load(Class service) {
//获取线程上下文类加载器 :
ClassLoader cl = Thread.currentThread().getContextClassLoader();
//生成ServiceLoader对象:
return ServiceLoader.load(service, cl);
}
public static ServiceLoader load(Class service, ClassLoader loader){
return new ServiceLoader<>(service, loader);
}
private ServiceLoader(Class ,将此类加载赋值给ServiceLoader类中的loader成员变量。
**ServiceLoader:**Java SPI 的核心类,用于加载 SPI 实现类。ServiceLoader 中有各种实用方法来获取特定实现、迭代它们或重新加载服务。 文件中的内容如下:io.github.dunwu.javacore.spi.MysqlStorageio.github.dunwu.javacore.spi.RedisStorage复制代码 2.4 ServiceLoader 完成了上面的步骤,就可以通过 ServiceLoader 来加载服务。 示例如下:import java.util.ServiceLoader;public class SpiDemo { public static void main(String[] args) { ServiceLoader<DataStorage> serviceLoader = ServiceLoader.load(DataStorage.class); System.out.println
3.多个并发多线程使用 ServiceLoader 类的实例是不安全的。 源码实现中, java.util.ServiceLoader#PREFIX 可以看到这个目录的硬编码。 加载实现类并调用服务 这时,通过ServiceLoader 加载实现类并调用服务: Main.java package com.light.sword; import java.util.ServiceLoader 可以看到其中最为核心的就是通过一系列的约定(其实,就是按照人家 java.util.ServiceLoader 的规范标准来), 然后,通过ServiceLoader 这个类来加载具体的实现类,进而调用实现类的服务 SPI 实现原理解析 首先,ServiceLoader实现了Iterable接口,所以它有迭代器的属性,这里主要都是实现了迭代器的hasNext和next方法。