前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >聊聊 Java SPI

聊聊 Java SPI

作者头像
程序猿杜小头
发布于 2022-12-01 13:38:11
发布于 2022-12-01 13:38:11
88000
代码可运行
举报
文章被收录于专栏:程序猿杜小头程序猿杜小头
运行总次数:0
代码可运行

The built-in service discovery mechanism in Java

Running with Java 11.0.10

SPI (Service Provider Interface) 是自 Java 1.6 引入的一种基于接口或抽象类的服务发现机制。得益于 Java SPI 机制,开发人员只需为第三方预留出 SPI 拓展接口,这样可以在不修改代码的前提下,通过增删第三方依赖来实现系统的灵活拓展。

要想成功地玩转 Java SPI,下面四个组件是缺一不可的:

① Service Provider Interface

服务供应商接口,即SPI拓展接口;只能是接口抽象类

② Service Provider

服务供应商,即针对SPI拓展接口提供SPI实现类的第三方;SPI实现类必须定义一个无参构造方法,否则报错:Unable to get public no-arg constructor

③ SPI Configuration File

首先,SPI配置文件必须贮存于classpath:/META-INF/services/目录下;其次,其必须采用UTF-8编码;此外,其必须以SPI拓展接口的完全限定名来命名;最后,SPI配置文件的内容应当为第三方SPI实现类的完全限定名。

④ ServiceLoader

ServiceLoader是JDK内置的SPI利器,主要负责读取SPI配置文件并将第三方SPI实现类加载到JVM中。

1. SPI应用案例解读

既然是亲儿子,SPI机制在JDK内部还是有若干应用场景的,其中大家最为熟悉的应该就是JDBC API了。众所周知,官方只是制定了一套数据库交互规范,秉持'让专业的人干专业的事'这一原则,官方并没有提供具体的实现,转而将实现逻辑交由各数据库厂商负责。在JDBC 4.0前后,分别颖现出两种编程范式,如下所示:

Before JDBC 4.0
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Connection connection = null;
Statement statement = null;
try {
    // 加载驱动
    Class.forName("org.postgresql.Driver");
    connection = DriverManager.getConnection("jdbc:postgresql://HOST:PORT/DB", "USERNAME", "PASSWORD");
    statement = connection.createStatement();
    statement.execute("delete from tbl_user where user_id = 1");
} catch (SQLException e) {
    log.error("user deletion failure", e);
} finally {
    JdbcUtils.closeStatement(statement);
    JdbcUtils.closeConnection(connection);
}
After JDBC 4.0
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Connection connection = null;
Statement statement = null;
try {
    connection = DriverManager.getConnection("jdbc:postgresql://HOST:PORT/DB", "USERNAME", "PASSWORD");
    statement = connection.createStatement();
    statement.execute("delete from tbl_user where user_id = 1");
} catch (SQLException e) {
    log.error("user deletion failure", e);
} finally {
    JdbcUtils.closeStatement(statement);
    JdbcUtils.closeConnection(connection);
}

显然,在JDBC 4.0之前,我们需要通过Class.forName()来手动加载指定厂商的数据库驱动;若后期更换数据库驱动,必须修改forName()方法中的驱动参数。而在JDBC 4.0之后,因为不再需要手动加载数据库驱动,顾而也就不涉及代码的修改了,这就是Java SPI带给我们的能力!

java.sql.Driver是JDK为第三方数据库厂商预留的SPI拓展接口,主要用于构建Connection。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface Driver {
    Connection connect(String url, Properties info);
    boolean acceptsURL(String url);
}

这里以PostgreSQL为例!org.postgresql:postgresql驱动包结构如下:

从上图来看,META-INF/services目录下的java.sql.Driver文件应该就是SPI配置文件了,其内容如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
org.postgresql.Driver

显然,PostgreSQL作为数据库厂商,org.postgresql.Driver毫无保留地实现了java.sql.Driver接口,在其源码中有一静态初始化代码块,用于向java.sql.DriverManager注册自身实例。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Driver implements java.sql.Driver {
    private static Driver registeredDriver;
    // 静态初始化代码块
    static {
        try {
            if (Objects.nonNull(registeredDriver)) {
                throw new IllegalStateException("Driver is already registered.");
            }
            registeredDriver = new Driver();
            // 第三方厂商向DriverManager注册驱动
            DriverManager.registerDriver(registeredDriver);
        } catch (SQLException e) {
            throw new ExceptionInInitializerError(e);
        }
    }
    @Override
    public Connection connect(String url, Properties info) {
        // 略
    }
    @Override
    public boolean acceptsURL(String url) {
        // 略
    }
}

接下来,自然要看看java.sql.DriverManager的源码了,其主要用来维护驱动实例;registerDriver()静态方法会将驱动实例添加到CopyOnWriteArrayList中,而deregisterDriver()静态方法又会将驱动实例从CopyOnWriteArrayList中移除;在getDriver()getConnection()这俩静态方法中,存在一段相同的逻辑,即通过ServiceLoader.load(Driver.class)来加载PostgreSQL针对java.sql.Driver接口提供的SPI实现类。关于类的加载,一般通过Class.forName()方法来实现,其会触发静态初始化代码块的执行,那也就是说org.postgresql.Driver中的静态初始化代码块是在这里被触发执行的。但为什么ServiceLoader.load(Driver.class)执行完之后,还要有一个空的迭代逻辑呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class DriverManager {
    // 已注册JDBC驱动列表
    private static final CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
    // 锁
    private static final Object lockForInitDrivers = new Object();
    // 第三方厂商驱动是否已经加载完毕,若已加载,则后期不再重复加载
    private static volatile boolean driversInitialized;
    // 私有构造方法
    private DriverManager(){}
    // 获取Driver实例
    public static Driver getDriver(String url) {
        if (!driversInitialized) {
            synchronized (lockForInitDrivers) {
                if (!driversInitialized) {
                    // ServiceLoader.load()并不会立即去加载第三方厂商驱动,其只是返回一个ServiceLoader实例而已
                    ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                    // 生成Iterator实例
                    Iterator<Driver> driversIterator = loadedDrivers.iterator();
                    // 延迟加载,即在迭代时才真正去加载第三方厂商驱动
                    while (driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                    driversInitialized = true;
                }
            }
        }
        Driver driver = null;
        for (DriverInfo driverInfo : registeredDrivers) {
            if (driverInfo.driver.acceptsURL(url)) {
                driver = driverInfo.driver;
                break;
            }
        }
        return driver;
    }

    // 获取Connection实例
    public static Connection getConnection(String url, String user, String password) {
        if (!driversInitialized) {
            synchronized (lockForInitDrivers) {
                if (!driversInitialized) {
                    // ServiceLoader.load()并不会立即去加载第三方厂商驱动,其只是返回一个ServiceLoader实例而已
                    ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                    // 生成Iterator实例
                    Iterator<Driver> driversIterator = loadedDrivers.iterator();
                    // 延迟加载,即在迭代时才真正去加载第三方厂商驱动
                    while (driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                    driversInitialized = true;
                }
            }
        }
        Properties info = MapUtils.toProperties(ImmutableMap.of("user", user, "password", password));
        Connection connection = null;
        for (DriverInfo driverInfo : registeredDrivers) {
            // 第三方厂商会实现java.sql.Driver接口,实现其connect()方法
            connection = driverInfo.driver.connect(url, info);
            if (connection != null) {
                break;
            }
        }
        return connection;
    }

    // 注册JDBC驱动
    public static void registerDriver(Driver driver) {
        registeredDrivers.addIfAbsent(new DriverInfo(driver));
    }
    // 注销JDBC驱动
    public static void deregisterDriver(Driver driver) {
        DriverInfo driverInfo = new DriverInfo(driver);
        synchronized (lockForInitDrivers) {
            if (registeredDrivers.contains(driverInfo)) {
                registeredDrivers.remove(driverInfo);
            }
        }
    }
}

带着刚才的疑问,进入ServiceLoader的源码一探究竟吧。盯着load()方法看了许久,哥陷入深深的沉思:这玩意儿就是单纯地返回一个ServiceLoader实例而已,并没有Class.forName()的身影啊,莫非上述空的迭代逻辑才是真正用来执行类加载的吗?iterator()方法生成了一个LazyClassPathLookupIterator迭代器,也许玄机就在这个迭代器中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public final class ServiceLoader<S> implements Iterable<S> {
    private final Class<S> service;
    private final String serviceName;
    private final ClassLoader loader;
    private Iterator<ServiceLoader.Provider<S>> lookupIterator1;
    private final List<S> instantiatedProviders = new ArrayList<>();

    public static interface Provider<S> extends Supplier<S> {
        Class<? extends S> type();
        @Override S get();
    }
    private ServiceLoader(Class<?> caller, Class<S> svc, ClassLoader cl) {
        this.service = svc;
        this.serviceName = svc.getName();
        this.loader = cl;
    }

    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return new ServiceLoader<>(Reflection.getCallerClass(), service, cl);
    }
    @Override
    public Iterator<S> iterator() {
        if (lookupIterator1 == null) {
            Iterator<Provider<S>> iterator = new LazyClassPathLookupIterator<>();
            lookupIterator1 = new Iterator<Provider<S>>() {
                @Override
                public boolean hasNext() {
                    return iterator.hasNext();
                }
                @Override
                public Provider<S> next() {
                    if (iterator.hasNext()) {
                        return iterator.next();
                    } else {
                        throw new NoSuchElementException();
                    }
                }
            };
        }
        return new Iterator<S>() {
            int index;
            @Override
            public boolean hasNext() {
                if (index < instantiatedProviders.size()) {
                    return true;
                }
                return lookupIterator1.hasNext();
            }
            @Override
            public S next() {
                S next;
                if (index < instantiatedProviders.size()) {
                    next = instantiatedProviders.get(index);
                } else {
                    next = lookupIterator1.next().get();
                    instantiatedProviders.add(next);
                }
                index++;
                return next;
            }
        };
    }
}

继续跟进,在查阅LazyClassPathLookupIterator的源码后,脑海中的疑问也随之烟消云散了。nextProviderClass()无疑是核心逻辑所在,它首先一次性读取SPI配置文件,然后在每一次迭代时通过Class.forName()方法来加载SPI实现类。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private final class LazyClassPathLookupIterator<T> implements Iterator<Provider<T>> {
    static final String PREFIX = "META-INF/services/";

    Enumeration<URL> configs;
    Iterator<String> pending;
    Provider<T> nextProvider;
    ServiceConfigurationError nextError;

    LazyClassPathLookupIterator() {}

    private Class<?> nextProviderClass() {
        if (configs == null) {
            String fullName = PREFIX + service.getName();
            configs = loader.getResources(fullName);
        }
        while ((pending == null) || !pending.hasNext()) {
            if (!configs.hasMoreElements()) {
                return null;
            }
            pending = parse(configs.nextElement());
        }
        String cn = pending.next();
        try {
            return Class.forName(cn, false, loader);
        } catch (ClassNotFoundException x) {
            return null;
        }
    }

    private boolean hasNextService() {
        while (nextProvider == null && nextError == null) {
            try {
                Class<?> clazz = nextProviderClass();
                if (clazz == null) {
                    return false;
                }

                if (service.isAssignableFrom(clazz)) {
                    Class<? extends S> type = (Class<? extends S>) clazz;
                    Constructor<? extends S> ctor = (Constructor<? extends S>)getConstructor(clazz);
                    ProviderImpl<S> p = new ProviderImpl<S>(service, type, ctor);
                    nextProvider = (ProviderImpl<T>) p;
                } else {
                    fail(service, clazz.getName() + " not a subtype");
                }
            } catch (ServiceConfigurationError e) {
                nextError = e;
            }
        }
        return true;
    }

    private Provider<T> nextService() {
        if (!hasNextService()) {
            throw new NoSuchElementException();
        }

        Provider<T> provider = nextProvider;
        if (provider != null) {
            nextProvider = null;
            return provider;
        } else {
            ServiceConfigurationError e = nextError;
            assert e != null;
            nextError = null;
            throw e;
        }
    }

    @Override
    public boolean hasNext() {
        return hasNextService();
    }
    @Override
    public Provider<T> next() {
        return nextService();
    }
}

实际上,ServiceLoader使用了延迟加载技术,即在需要时才会加载对象或数据;一般,当对象创建的成本非常高且对象的使用非常少时,延迟加载是必不可少的。

2. 入门实战

2.1 json-serializer

2.1.1 定义SPI拓展接口
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package io.github.serializer;

public interface JsonSerializer {
    void serialize(Object obj);
}
2.1.2 定义静态工厂类

JsonSerializerManager是一个静态工厂类,它的构造方法是私有的;getJsonSerializer()静态方法可以根据特定厂商名称来获取相应的JsonSerializer实例。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package io.github.serializer;

public class JsonSerializerManager {
    private static final CopyOnWriteArrayList<JsonSerializerWrapper> REGISTERED_JSON_SERIALIZER = new CopyOnWriteArrayList<>();
    private static final AtomicBoolean IS_INITIALIZED = new AtomicBoolean(false);

    private JsonSerializerManager() {}

    public static void registerJsonSerializer(JsonSerializerWrapper jsonSerializer) {
        REGISTERED_JSON_SERIALIZER.addIfAbsent(jsonSerializer);
    }
    public static void deregisterJsonSerializer(JsonSerializerWrapper jsonSerializer) {
        REGISTERED_JSON_SERIALIZER.remove(jsonSerializer);
    }

    public static JsonSerializer getJsonSerializer(String manufactureName) {
        if (!IS_INITIALIZED.get()) {
            ServiceLoader<JsonSerializer> jsonSerializerServiceLoader = ServiceLoader.load(JsonSerializer.class);
            Iterator<JsonSerializer> jsonSerializerIterator  = jsonSerializerServiceLoader.iterator();
            while (jsonSerializerIterator.hasNext()) {
                jsonSerializerIterator.next();
            }
        }

        return REGISTERED_JSON_SERIALIZER
                .stream()
                .collect(Collectors.toMap(
                        JsonSerializerWrapper::getManufactureName,
                        JsonSerializerWrapper::getJsonSerializer)
                )
                .get(manufactureName);
    }
}

JsonSerializerWrapper主要用于包装JsonSerializer,重点关注equals()hashCode()中的逻辑。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package io.github.serializer;

@Getter
@Setter
public class JsonSerializerWrapper {
    private String manufactureName;
    private JsonSerializer jsonSerializer;

    @Override
    public boolean equals(Object other) {
        return (other instanceof JsonSerializerWrapper)
                && this.jsonSerializer == ((JsonSerializerWrapper) other).jsonSerializer;
    }
    @Override
    public int hashCode() {
        return jsonSerializer.hashCode();
    }
}

2.2 alibaba-json-serializer

2.2.1 引入json-serializer依赖
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>io.github</groupId>
    <artifactId>json-serializer</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
2.2.2 实现SPI拓展接口
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.alibaba.json.serializer.privider;

public class AlibabaFastjsonSerializer implements JsonSerializer {
    static {
        JsonSerializerWrapper wrapper = new JsonSerializerWrapper();
        wrapper.setManufactureName("alibaba");
        wrapper.setJsonSerializer(new AlibabaFastjsonSerializer());
        JsonSerializerManager.registerJsonSerializer(wrapper);
    }

    @Override
    public void serialize(Object obj) {
        System.out.println("Alibaba提供的json序列化方案:fastjson");
    }
}
2.2.3 编写SPI配置文件
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
com.alibaba.json.serializer.privider.AlibabaFastjsonSerializer

2.3 fasterxml-json-serializer

2.3.1 引入json-serializer依赖
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>io.github</groupId>
    <artifactId>json-serializer</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
2.3.2 实现SPI拓展接口
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package org.fasterxml.json.serializer.privider;

public class FasterxmlJacksonSerializer implements JsonSerializer {
    static {
        JsonSerializerWrapper wrapper = new JsonSerializerWrapper();
        wrapper.setManufactureName("fasterxml");
        wrapper.setJsonSerializer(new FasterxmlJacksonSerializer());
        JsonSerializerManager.registerJsonSerializer(wrapper);
    }

    @Override
    public void serialize(Object obj) {
        System.out.println("Fasterxml提供的json序列化方案:jackson");
    }
}
2.3.3 编写SPI配置文件
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
org.fasterxml.json.serializer.privider.FasterxmlJacksonSerializer

2.4 json-serializer-app

2.4.1 引入第三方SPI实现类的依赖
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>json-serializer</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.fasterxml</groupId>
    <artifactId>json-serializer</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
2.4.2 运行
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class JsonSerializerApp {
    public static void main(String[] args) {
        JsonSerializer jsonSerializer = JsonSerializerManager.getJsonSerializer("alibaba");
        jsonSerializer.serialize(new Object());
        jsonSerializer = JsonSerializerManager.getJsonSerializer("fasterxml");
        jsonSerializer.serialize(new Object());
    }
}

运行结果如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Alibaba提供的json序列化方案:fastjson
Fasterxml提供的json序列化方案:jackson

3. 总结

SPI的确很简单,但如何更优雅地设计与应用呢?个人觉得JDK中java.sql.DriverManager是一个很好的参照。此外,Java SPI机制有一个较为明显的缺点:无法按需加载指定第三方SPI实现类!!!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序猿杜小头 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
java中SPI机制
SPI,Service Provider Interface,是有java提供的一套用来被第三方实现或者扩展的API,本质是通过基于接口的编程+策略模式+配置文件实现动态加载。主要是被框架的开发人员使用,比如JDBC中驱驱动java.sql.Driver接口,不同的数据库厂商通过实现次接口完成对数据库的操作,mysql等数据库都有不同的实现类提供给用户,而Java的SPI机制可以为某个接口寻找具体的实现类。
代码改变世界-coding
2022/01/05
4140
java中SPI机制
ServiceLoader和DriverManager的前世今生
相信大家看着我一步步简化到最后,已经蒙了,为什么可以这样写,别急,下面我们就来看看DriverManager到底是怎么实现的
大忽悠爱学习
2022/06/06
7680
ServiceLoader和DriverManager的前世今生
Java是如何实现自己的SPI机制的? JDK源码(一)
这是【源码笔记】的JDK源码解读的第一篇文章,本篇我们来探究Java的SPI机制的相关源码。
源码笔记
2020/03/29
1.5K0
Java是如何实现自己的SPI机制的? JDK源码(一)
一文读懂微内核架构
微内核是一种典型的架构模式 ,区别于普通的设计模式,架构模式是一种高层模式,用于描述系统级的结构组成、相互关系及相关约束。微内核架构在开源框架中的应用非常广泛,比如常见的 ShardingSphere 还有Dubbo都实现了自己的微内核架构。那么,在介绍什么是微内核架构之前,我们有必要先阐述这些开源框架会使用微内核架构的原因。
JAVA日知录
2021/01/04
3.2K0
一文读懂微内核架构
从mysql-connector-java中学习
做过javaweb开发的同学,对mysql的使用一定不陌生。今天,我们来聊一聊jdbc连接mysql。
zhangheng
2020/04/28
1.1K0
Java SPI机制详解
什么是SPI? SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。SPI是一种动态替换发现的机制, 比如有个接口,想运行时动态的给它添加实
aoho求索
2018/06/04
1.2K0
聊聊 SPI 机制
短信平台是通过 SPI 机制加载三方渠道插件,所以这篇文章,我们有必要温习 SPI 机制。
勇哥java实战
2023/11/25
3370
jdbc驱动加载机制
这里主要是因为最近使用jdbc连接数据库时,发现相比之前一般的连接过程,现在竟然不用加载驱动也可以了。这里研究记录下。
Vincent-yuan
2021/08/06
2.2K0
jdbc驱动加载机制
温习 SPI 机制 (Java SPI 、Spring SPI、Dubbo SPI)
SPI 全称为 Service Provider Interface,是一种服务发现机制。
勇哥java实战
2024/10/29
2520
温习 SPI 机制 (Java SPI 、Spring SPI、Dubbo SPI)
java SPI机制的使用及原理
本片文章是针对dubbo SPI机制深入分析的平滑过渡的作用。当然咱们主要是学习优秀的思想,SPI就是一种解耦非常优秀的思想,我们可以思考在我们项目开发中是否可以使用、是否可以帮助我们解决某些问题、或者能够更加提升项目的框架等
全栈程序员站长
2021/08/05
3540
【Java——SPI机制详解】
SPI(Service Provider Interface),是JDK内置的一种服务提供发现机制,可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用,比如java.sql.Driver接口,其他不同厂商可以针对同一接口做出不同的实现,MySQL和PostgreSQL都有不同的实现提供给用户,而Java的SPI机制可以为某个接口寻找服务实现。Java中SPI机制主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,其核心思想就是 解耦。
奥耶可乐冰
2024/05/31
1.7K0
Java SPI原理与源码分析
SPI是Service Provider Interface的缩写,jdk1.6版本开始内置的一种扩展机制,主要用于扩展框架的能力,其实就是框架定义一种能力(规范)和一些常规能力实现,在大部分业务场景下,基本满足需求,但是在一些定制化的场景框架默认的实现可能存在局限,那么我们就可以按照框架定义的规范自定义实现某种能力,然后在应用启动时把我们的自定义实现连同默认实现一同加载并实例化到容器中去,然后按照需要选择使用常规能力或者自定义能力,举个栗子,在Dubbo的负载均衡机制中,我们可以自己实现负载策略,然后消费服务的时候使用我们自己的策略。
叔牙
2020/11/19
5610
Java SPI原理与源码分析
JDBC驱动程序注册 JDBC简介(二)
常用的驱动程序名称与URL格式如上表所示,随着版本变化,会存在些许变动,如有不对,还需重新查验
noteless
2019/02/25
1.5K0
JDBC驱动程序注册  JDBC简介(二)
一篇文章吃透:为什么加载数据库驱动要用Class.forName()
Class.forName():将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static代码块。
明明如月学长
2021/08/27
1.5K0
Java虚拟机--线程上下文类加载器
线程上下文类加载器 通过名字可知,线程上下文类加载,就是当前线程所拥有的类加载器,可通过Thread.currentThread()获取当前线程。 线程上下文类加载器(Thread Context ClassLoader)可以通过java.lang.Thread类的setContextClassLoader()方法设置,创建线程时候未指定的话,则默认从父线程中继承。 那父线程中也没指定呢?那么会默认为应用程序的类加载器。例如:main方法的线程上下文类加载器就是sun.misc.Launcher$AppCl
贾博岩
2018/06/13
1.5K0
深入解析Java SPI🌟从使用到原理的全面之旅🚀
在Java开发中,我们经常需要一种机制来解耦接口和其实现类,使得系统更加灵活、可扩展
菜菜的后端私房菜
2025/01/06
2470
JAVA 拾遗 —— 关于SPI机制
实现一个自定义的SPI1. 项目结构2. interface 模块3. good-printer 模块4. bad-printer模块SPI 在实际项目中的应用SPI 在扩展方面的应用
芋道源码
2019/10/29
4890
JAVA 拾遗 —— 关于SPI机制
JAVA拾遗--关于SPI机制
JDK提供的SPI(Service Provider Interface)机制,可能很多人不太熟悉,因为这个机制是针对厂商或者插件的,也可以在一些框架的扩展中看到。其核心类 java.util.ServiceLoader可以在jdk1.8的文档中看到详细的介绍。虽然不太常见,但并不代表它不常用,恰恰相反,你无时无刻不在用它。玄乎了,莫急,思考一下你的项目中是否有用到第三方日志包,是否有用到数据库驱动?其实这些都和SPI有关。再来思考一下,现代的框架是如何加载日志依赖,加载数据库驱动的,你可能会对class.
kirito-moe
2018/04/27
8930
JAVA拾遗--关于SPI机制
你真的的懂JDBC?
一、前言 Java中操作数据库元老是使用JDBC,而JDBC内部是如何实现的,为何每次使用时候都是写那些不理解的几行固定代码?这些看似不相关的代码内部是否有瓜葛那,下面进来探讨一二。 二、一个例子 p
加多
2018/09/06
5630
JAVA SPI 是怎么实现的?
SPI(Service Provider Interface) ,是 JDK 内置的一种提供发现机制。SPI 是一种动态替换发现的机制。
王小明_HIT
2020/06/04
7490
JAVA SPI  是怎么实现的?
相关推荐
java中SPI机制
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文