返回顶部
首页 > 资讯 > 后端开发 > Python >java开发ServiceLoader实现机制及SPI应用
  • 283
分享到

java开发ServiceLoader实现机制及SPI应用

java开发ServiceLoaderSPIjavaServiceLoaderSPI 2022-11-13 19:11:04 283人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录前言如何绕过双亲委派模式ServiceLoader实现机制SPI在各个框架上的应用小结前言 做过java web开发的小伙伴大多数时候都需要链接数据库,这个时候就需要配置数据库引

前言

做过java web开发的小伙伴大多数时候都需要链接数据库,这个时候就需要配置数据库引擎DriverClassName参数,这样我们的java应用才能通过数据库厂商给的Driver与指定的数据库建立通信。但是这里就有一个疑问:

java.sql.Driverjdk自带的接口,它是由BoostrapClassLoader加载的,DriverClassName是外部厂商提供的具体实现,是由AppClassLoader加载的,要建立与数据库的通信,必然是通过java.sql.Driver接口方法发起的,那么在java.sql.Driver是如何拿到具体实现的呢?它是不是违背了ClassLoader的双亲委派模式呢?

如何绕过双亲委派模式

为了拿到AppClassLoader中加载的java.sql.Driver实现类,我们可以查看一下DriverManager是怎么处理的:

public static Driver getDriver(String url)
        throws SQLException {
    println("DriverManager.getDriver("" + url + "")");
    ensureDriversInitialized();
    ......
}
private static void ensureDriversInitialized() {
    ......
    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    // 核心代码ServiceLoader
                    ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                    Iterator<Driver> driversIterator = loadedDrivers.iterator();
                    try {
                        while (driversIterator.hasNext()) {
                            driversIterator.next();
                        }
                    } catch (Throwable t) {
                        // Do nothing
                    }
                    return null;
                }
            });
    ......
}

我们最终可以发现,DriverManager通过ServiceLoader.load(Driver.class)就拿到了我们配置的DriverClassName实现类。这就实现在DriverManager中拿到了外部提供的Driver实现,绕过来双亲委派模式。

ServiceLoader实现机制

我们来看一下ServiceLoader是如何实现SPI机制的,先从ServiceLoader.load()方法入手:

    public static <S> ServiceLoader<S> load(Class<S> service) {
        // 从当前线程中获取ClassLoader
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        // 创建一个ServiceLoader对象
        return new ServiceLoader<>(Reflection.getCallerClass(), service, cl);
    }

1.从当前线程中获取ClassLoader;因为在创建AppClassLoader后,将AppClassLoader设置进当前线程的上下文中;

2.根据ClassLoader以及目标接口类创建一个ServiceLoader对象;

其实ServiceLoader核心代码在hasNext()方法中:

       @Override
        public boolean hasNext() {
            if (acc == null) {
                return hasNextService();
            } else {
                PrivilegedAction<Boolean> action = new PrivilegedAction<>() {
                    public Boolean run() { return hasNextService(); }
                };
                return AccessController.doPrivileged(action, acc);
            }
        }

最终都会调用到hasNextService()方法中:

private boolean hasNextService() {
    // nextProvider默认为null,如果通过next()取出来了,nextProvider就会变成null
    while (nextProvider == null && nextError == null) {
        try {
            // 找到目标实现类
            Class<?> clazz = nextProviderClass();
            if (clazz == null)
                return false;
            if (clazz.getModule().isNamed()) {
                // ignore class if in named module
                continue;
            }
            // 判断service接口是否和clazz有父子关系
            if (service.isAssignableFrom(clazz)) {
                Class<? extends S> type = (Class<? extends S>) clazz;
                // 获取无参构造函数
                Constructor<? extends S> ctor
                            = (Constructor<? extends S>)getConstructor(clazz);
                // 包装成一个ProviderImpl对象
                ProviderImpl<S> p = new ProviderImpl<S>(service, type, ctor, acc);
                // 并赋值给nextProvider
                nextProvider = (ProviderImpl<T>) p;
            } else {
                fail(service, clazz.getName() + " not a subtype");
            }
        } catch (ServiceConfigurationError e) {
            nextError = e;
        }
    }
    return true;
}

外部提供的实现类一定要有一个无参构造函数,否则会导致ServiceLoader加载失败;

我们下面再继续深入看看ServiceLoader是怎么找到实现类的:

private Class<?> nextProviderClass() {
    if (configs == null) {
        try {
            // 拼接文件名:"META-INF/services/接口名称"
            // 比如接口名为:java.sql.Driver,
            // 那么文件路径就是:"META-INF/services/java.sql.Driver"
            String fullName = PREFIX + service.getName();
            // 没有指定ClassLoader,就通过getSystemClassLoader()加载目标文件
            if (loader == null) {
                configs = ClassLoader.getSystemResources(fullName);
            } else if (loader == ClassLoaders.platfORMClassLoader()) {
                // 如果是platformClassLoader,它没有class path,那么看看BootLoader有没有class path
                if (BootLoader.hasClassPath()) {
                    configs = BootLoader.findResources(fullName);
                } else {
                    configs = Collections.emptyEnumeration();
                }
            } else {
                // 通过指定classLoader加载目标文件
                configs = loader.getResources(fullName);
            }
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    // 上面代码只会执行一次,这样configs就不会为null,下次进来直接取下一个实现类
    // 把configs内容解析成一个迭代器
    while ((pending == null) || !pending.hasNext()) {
        if (!configs.hasMoreElements()) {
            return null;
        }
        pending = parse(configs.nextElement());
    }
    // 通过迭代器获取下一个实现类名称
    String cn = pending.next();
    try {
        // 通过类名反射成Class对象
        return Class.forName(cn, false, loader);
    } catch (ClassNotFoundException x) {
        fail(service, "Provider " + cn + " not found");
        return null;
    }
}

1.实现类的载入是因为在META-INF/services/文件夹中创建了以目标接口名称命名的文件,并在里面写上了实现类的全路径类名。

2.ServiceLoader通过ClassLoader从class path中载入目标文件里面的内容,并解析出实现类的全路径类名;

3.最终通过反射的方式创建出实现类的Class对象,这样就完成了SPI的实现;

SPI在各个框架上的应用

除了在数据库Driver上使用了SPI,我们还可以发现SPI在各个框架上都有大量的应用。比如我最近在看的Seata分布式事务框架,里面就有用到SPIio.seata.common.loader.EnhancedServiceLoader

另一个就是我们经常使用的Mysql-connector-java以及阿里的Druid:

小结

通过以上源码分析以及示例演示,我们简单做一个小结:

1.ServiceLoader打破双亲委派模式的方式通过获取当前线程上下文中的ClassLoader完成的;

2.SPI的实现类名称必须放在META-INF/services/文件夹下面,以目标接口名称作为文件名称,文件内容为目标实现类全路径类名;

3.目标实现类必须要有一个无参构造函数,否则SPI会失败;

以上就是java开发ServiceLoader实现机制及SPI应用的详细内容,更多关于java开发ServiceLoader SPI的资料请关注编程网其它相关文章!

--结束END--

本文标题: java开发ServiceLoader实现机制及SPI应用

本文链接: https://lsjlt.com/news/170365.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • java开发ServiceLoader实现机制及SPI应用
    目录前言如何绕过双亲委派模式ServiceLoader实现机制SPI在各个框架上的应用小结前言 做过java web开发的小伙伴大多数时候都需要链接数据库,这个时候就需要配置数据库引...
    99+
    2022-11-13
    java开发ServiceLoader SPI java ServiceLoader SPI
  • Java中怎么实现SPI机制
    Java中怎么实现SPI机制,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。2 什么是SPI机制SPI是Service Provider Interface 的简...
    99+
    2023-06-16
  • 深入探讨Java SPI机制及其应用场景
    目录一、什么是SPI二、使用场景三、使用步骤示例四、原理解析1、SPI的核心就是ServiceLoader.load()方法2、ServiceLoader核心代码介绍一、什么是SPI...
    99+
    2023-05-17
    Java SPI机制 Java SPI接口
  • Spi机制在Android开发的应用示例详解
    目录Spi机制介绍举个例子ServiceLoader.load在Android中的应用总结Spi机制介绍 SPI 全称是 Service Provider Interface,是一种...
    99+
    2022-11-13
    Android开发Spi机制 Android Spi
  • java框架基础之SPI机制实现及源码解析
    目录1 定义2 案例实现标准接口厂商的具体接口实现3 SPI机制源码分析3.1 load加载过程3.2 实例化过程1 定义 SPI 的全名为 Service Pr...
    99+
    2024-04-02
  • Java SPI概念、实现原理、优缺点、应用场景、使用步骤、实战SPI案例
    一、前言 在当今互联网时代,应用程序越来越复杂,对于我们开发人员来说,如何实现高效的组件化和模块化已经成为了一个重要的问题。而 Java SPI(Service Provider Interface)机制,作为一种基于接口的服务发现机制,可...
    99+
    2023-08-17
    java SPI
  • Java开发者必读:NPM缓存的实现机制及其优化方法
    在前端开发中,NPM是一个非常重要的工具,它为我们提供了众多的包管理和依赖管理功能。但是,在使用NPM的过程中,我们可能会遇到一些缓存相关的问题,如何优化这些问题成为了我们关注的焦点。 本文将介绍NPM缓存的实现机制及其优化方法,让Jav...
    99+
    2023-08-30
    leetcode npm 缓存
  • PHP开发中的缓存机制与应用实战
    在 php 开发中,缓存机制通过将经常访问的数据临时存储在内存或磁盘中来提升性能,从而减少数据库访问次数。缓存类型主要包括内存、文件和数据库缓存。php 中可以使用内置函数或第三方库实现...
    99+
    2024-05-09
    缓存 php redis 持久化存储
  • Java开发实现飞机大战
    目录一、飞机大战1 封装所有飞行物公共属性和功能的父类2 封装英雄机属性和功能类3 封装敌机属性和功能的类4 封装大飞机属性和功能的类5 子弹类6 飞机大战射击的主方法二、测试结果本...
    99+
    2024-04-02
  • MySQL锁机制及其实际应用
    MySQL 锁机制及其应用摘要:MySQL作为一种关系型数据库管理系统,其锁机制在并发访问中起到了至关重要的作用。本文将介绍MySQL的锁机制,包括锁的类型、获取和释放锁的方式,以及在实际应用中的使用方法,并提供具体的代码示例。一、介绍在多...
    99+
    2023-12-21
    应用 MySQL 锁机制
  • .NET6开发TodoList应用之实现API版本控制
    目录需求目标原理与思路实现添加Nuget Package并配置服务实现API版本控制一点扩展总结需求 API接口版本管理,对于一些规模稍大的企业应用来说,是经常需要关注的一大需求。尽...
    99+
    2024-04-02
  • MVP实现Android应用层开发原理及过程
      背景   之所以要谈这个话题是因为你在开发App时可能会发现,Activity担负的责任非常之重,如果站在MVC框架角度看自己开发的App,一般xml布局文件科Act...
    99+
    2022-06-06
    Android
  • 低代码Java开发实现企业级应用
    # 随着互联网技术的发展,企业对信息化的需求越来越强烈,低代码开发技术应运而生。本文将介绍低代码Java开发的概念,探讨其在企业级应用中的实际应用和优势。低代码开发的概念低代码开发是一种以用户为中心、以快速迭代为核心的软件开发模式。它利用可...
    99+
    2024-01-26
    企业级 代码 Java
  • JAVA怎么实现乐观锁及CAS机制
    本篇内容介绍了“JAVA怎么实现乐观锁及CAS机制”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!前言生活中我们看待一个事物总有不同的态度,比...
    99+
    2023-07-04
  • java开发web前端cookie session及token会话机制详解
    目录引入:概念:一、cookie机制1、基本介绍2、分类3、cookie的作用域4、基本原理5、常用API6、基本应用7、Cookie中存储中文问题二、session机制1、基本介绍...
    99+
    2024-04-02
  • Java开发反射机制的实战经验总结
    目录前言一、创建Class的三种方式二、反射获取类的所有属性和属性类型三、反射动态修改类属性的注解值四、反射获取类的方法及调用方式总结前言 我在实际项目当中有经常用到反射机制,故而将...
    99+
    2024-04-02
  • java反射机制及常用应用场景是什么
    这篇文章主要介绍“java反射机制及常用应用场景是什么”,在日常操作中,相信很多人在java反射机制及常用应用场景是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”java反射机制及常用应用场景是什么”的疑...
    99+
    2023-06-29
  • Java开发者如何实现Git的实时响应?
    在现代软件开发中,Git已经成为了一种非常流行的版本控制工具。Git可以帮助我们管理代码的历史版本,协作开发,以及实现代码的自动化构建和部署。然而,如果我们想要实现Git的实时响应,就需要用到一些特殊的技术和工具。在本文中,我们将介绍一些...
    99+
    2023-10-13
    git 实时 响应
  • .NET6开发TodoList应用之实现ActionFilter
    目录需求目标原理与思路实现验证总结需求 Filter在.NET Web API项目开发中也是很重要的一个概念,它运行在执行MVC响应的Pipeline中执行,允许我们将一些可以在多个...
    99+
    2024-04-02
  • 图文详解java反射机制及常用应用场景
    目录一、什么是java反射?二、Hello World三、类加载与反射关系四、操作反射的java类4.1.获取Class对象的三种方法4.2.获取Class类对象的基本信息4.3. ...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作