设为首页 加入收藏

TOP

JDK中「SPI」原理分析(一)
2023-08-06 07:49:33 】 浏览:66
Tags:JDK SPI

基于【JDK1.8】

一、SPI简介

1、概念

SPI即service-provider-interface的简写;

JDK内置的服务提供加载机制,可以为服务接口加载实现类,解耦是其核心思想,也是很多框架和组件的常用手段;

2、入门案例

2.1 定义接口

就是普通的接口,在SPI的机制中称为【service】,即服务;

public interface Animal {
    String animalName () ;
}

2.2 两个实现类

提供两个模拟用来测试,就是普通的接口实现类,在SPI的机制中称为【service-provider】即服务提供方;

CatAnimal实现类;

public class CatAnimal implements Animal {
    @Override
    public String animalName() {
        System.out.println("Cat-Animal:布偶猫");
        return "Ragdoll";
    }
}

DogAnimal实现类;

public class DogAnimal implements Animal {
    @Override
    public String animalName() {
        System.out.println("Dog-Animal:哈士奇");
        return "husky";
    }
}

2.3 配置文件

文件目录:在代码工程中创建META-INF.services文件夹;

文件命名:butte.program.basics.spi.inf.Animal,即全限定接口名称;

文件内容:添加相应实现类的全限定命名;

butte.program.basics.spi.impl.CatAnimal
butte.program.basics.spi.impl.DogAnimal

2.4 测试代码

通过ServiceLoader加载配置文件中指定的服务实现类,然后遍历并调用Animal接口方法,从而执行不同服务提供方的具体逻辑;

public class SpiAnaly {
    public static void main(String[] args) {
        ServiceLoader<Animal> serviceLoader = ServiceLoader.load(Animal.class);
        Iterator<Animal> animalIterator = serviceLoader.iterator();
        while(animalIterator.hasNext()) {
            Animal animal = animalIterator.next();
            System.out.println("animal-name:" + animal.animalName());
        }
    }
}

结果输出

Cat-Animal:布偶猫 \n animal-name:ragdoll
Dog-Animal:哈士奇 \n animal-name:husky

二、原理分析

1、ServiceLoader结构

很显然,分析SPI机制的原理,从ServiceLoader源码中load方法切入即可,但是需要先从核心类的结构开始分析;

public final class ServiceLoader<S> implements Iterable<S> {
    // 配置文件目录
    private static final String PREFIX = "META-INF/services/";
    // 表示正在加载的服务的类或接口
    private final Class<S> service;
    // 类加载器用来定位,加载,实例化服务提供方
    private final ClassLoader loader;
    // 创建ServiceLoader时采用的访问控制上下文
    private final AccessControlContext acc;
    // 按实例化的顺序缓存服务提供方
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
    // 惰性查找迭代器
    private LazyIterator lookupIterator;
    /**
     * service:表示服务的接口或抽象类
     * loader: 类加载器
     */
    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }
    /**
     * ServiceLoader构造方法
     */
    private ServiceLoader(Class<S> svc, ClassLoader cl) {
        loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
        acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
        reload();
    }
    public void reload() {
        providers.clear();
        // 实例化迭代器
        lookupIterator = new LazyIterator(service, loader);
    }
    public static <S> ServiceLoader<S> load(Class<S> service,ClassLoader loader) {
        return new ServiceLoader<>(service, loader);
    }

    private class LazyIterator implements Iterator<S> {
        // 服务接口
        Class<S> service;
        // 类加载器
        ClassLoader loader;
        // 实现类URL
        Enumeration<URL> configs = null;
        // 实现类全名
        Iterator<String> pending = null;
        // 下个实现类全名
        String nextName = null;
    }
}

断点截图:

2、iterator迭代方法

ServiceLoader类的迭代器方法中,实际使用的是LazyIterator内部类的方法;

p
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇5、Spring之bean的作用域和生命周.. 下一篇工欲善其事,必先利其器之 Maven ..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目