手写RPC框架<一> SPI机制

发布于:2025-06-09 ⋅ 阅读:(22) ⋅ 点赞:(0)

SPI机制简介

SPI(Service Provider Interface)是一种服务发现机制,允许框架在运行时动态加载和使用实现。它将服务接口与具体实现解耦,使框架可以灵活扩展功能

Dubbo 的 SPI机制是其核心特性之一,用于实现框架的可扩展设计。它借鉴了 Java 标准 SPI,但功能更强大,支持 自动注入依赖、自适应扩展、URL 参数动态选择 等高级特性。其中
● 自动注入依赖,是使用了反射,然后通过Setter方法,进行依赖注入
● 自适应扩展和URL参数动态选择,是基于@Adaptive注解,可以在运行时候,通过URL,对需要注入类进行选择。

本文,只是实现了Dubbo的SPI基本类加载、对象缓存的功能,并没有实现自动依赖注入,自适应扩展,URL,后续可以参照Dubbo源码进行改进。

1. 类加载

可以采用Dubbo源码的SPI机制,去掉Adaptive,直接加载全部的自类的实现。
ExtentionClassLoader(Class<?>type):SPI机制的核心就是扩展类加载器,核心方法就是getExtension(String name);也就是说,创建type接口的,名称为name的一个实例。

package github.javaguide.extension;

import github.javaguide.factory.SingletonFactory;
import github.javaguide.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * refer to dubbo spi: https://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html
 */
@Slf4j
public final class ExtensionLoader<T> {

    private static final String SERVICE_DIRECTORY = "META-INF/extensions/";

    /**
     * 扩展类加载器的缓存,每一个类都有一个扩展类加载器。
     * 需要考虑多线程的问题
     * */
    private static final Map<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();

    /**
     * 扩展类实力的缓存,根据全类名进行缓存
     * */
//    private static final Map<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>();

    private final Class<?> type;

    /**
     * 实力缓存,根据名字进行缓存
     * 保证可见性的 Holder。
     * */
    private final Map<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();

    /**
     * 类缓存,根据名称进行缓存,从文件中进行读取的key,value
     * */
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();

    private ExtensionLoader(Class<?> type) {
        this.type = type;
    }

    /**
     * 获取扩展类加载器
     * */
    public static <S> ExtensionLoader<S> getExtensionLoader(Class<S> type) {
        if (type == null) {
            throw new IllegalArgumentException("Extension type should not be null.");
        }
        if (!type.isInterface()) {
            // 需要是接口
            throw new IllegalArgumentException("Extension type must be an interface.");
        }
        if (type.getAnnotation(SPI.class) == null) {
            // 类上需要包含SPI注解
            throw new IllegalArgumentException("Extension type must be annotated by @SPI");
        }
        // 创建类加载器,直接就是使用ConcurrentHashMap进行创建的,每一个类有一个自己的类加载器
        ExtensionLoader<S> extensionLoader = (ExtensionLoader<S>) EXTENSION_LOADERS.get(type);
        if (extensionLoader == null) {
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<S>(type));
            extensionLoader = (ExtensionLoader<S>) EXTENSION_LOADERS.get(type);
        }

        return extensionLoader;
    }

    public T getExtension(String name) {
        if (StringUtil.isBlank(name)) {
            throw new IllegalArgumentException("Extension name should not be null or empty.");
        }
        // 创建一个对象,如果没有的情况下,创建一个新的
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<>());
            holder = cachedInstances.get(name);
        }
        // 单例模式创建对象,双检测锁。没有只是使用ConcurrentHashMap
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtensionNew(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

    /**
     * 使用SigletonFactory创建单例bean
     * */
    private T createExtensionNew(String name) {
        // 1. 首先获取扩展类加载器
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw new RuntimeException("扩展类不存在:  " + name);
        }
        // 2. 获取实例
        return (T) SingletonFactory.getInstance(clazz);
    }

    private Map<String, Class<?>> getExtensionClasses() {
        // 1. 从缓存中获取所有的类
        Map<String, Class<?>> classes = cachedClasses.get();
        // 2. 缓存中没有,进行双检测锁
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    classes = new HashMap<>();
                    // 3. 从文件夹中加载所有的扩展类
                    loadDirectory(classes);
                    cachedClasses.set(classes);
                }
            }
        }
        return classes;
    }

    /**
     * java的SPI机制
     * */
    private void loadDirectory(Map<String, Class<?>> extensionClasses) {
        // 1. 构建配置文件的路径
        String fileName = ExtensionLoader.SERVICE_DIRECTORY + type.getName();
        try {
            Enumeration<URL> urls;
            // 2. Java的SPI,扩展类加载器,然后设置文件的URl
            ClassLoader classLoader = ExtensionLoader.class.getClassLoader();
            urls = classLoader.getResources(fileName);
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    URL resourceUrl = urls.nextElement();
                    // 3. 加载并解析
                    loadResource(extensionClasses, classLoader, resourceUrl);
                }
            }
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }

    private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, URL resourceUrl) {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceUrl.openStream(), UTF_8))) {
            String line;
            // 读取配置文件的没一行
            while ((line = reader.readLine()) != null) {
                // 1. 过滤掉注释
                final int ci = line.indexOf('#');
                if (ci >= 0) {
                    line = line.substring(0, ci);
                }
                // 2. 去掉空格
                line = line.trim();
                if (line.length() > 0) {
                    try {
                        // 3. = 实现的key value对解析,存入到map中
                        final int ei = line.indexOf('=');
                        String name = line.substring(0, ei).trim();
                        String clazzName = line.substring(ei + 1).trim();
                        if (name.length() > 0 && clazzName.length() > 0) {
                            // 4. Java的SPI的具体实现
                            Class<?> clazz = classLoader.loadClass(clazzName);
                            extensionClasses.put(name, clazz);
                        }
                    } catch (ClassNotFoundException e) {
                        log.error(e.getMessage());
                    }
                }

            }
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }
}

上述的流程:

  1. 首先,解析自定义的类文件,得到字类的名字(key),和全类名(value)
  2. 其次,通过Java的应用类加载器,加载字类
  3. 最后,将子类放入缓存中

注意:上述代码虽然可以创建一个实例,但是没有进行依赖注入,也就是说,对于有参类,没有只能进行实例化,没有办法进行初始化,如果SPI的子类有成员变量,访问成员变量的时候,就会报空指针异常。

2. 单例工厂

上面代码的代码中,有个自定义的单例工厂,类实现如下:
核心的逻辑,就是双检测锁实现的线程安全的单例模式。

package github.javaguide.factory;

import github.javaguide.extension.Holder;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * 获取单例对象的工厂类
 *
 * @author shuang.kou
 * @createTime 2020年06月03日 15:04:00
 */
@Slf4j
public final class SingletonFactory {
    private static final Map<String, Object> OBJECT_MAP = new ConcurrentHashMap<>();
    private static final Object lock = new Object();

    private static final Map<String, Holder<Object>> OBJECT_MAP_NEW = new HashMap<>();

    private SingletonFactory() {
    }

    public static <T> T getInstance(Supplier<T> constructor, Class<T> c) {
        if (c == null) {
            throw new IllegalArgumentException("Class cannot be null");
        }
        String key = c.getName();

        // 1. 第一次检查:快速读取缓存(无锁)
        Holder<Object> holder = OBJECT_MAP_NEW.get(key);
        if (holder != null && holder.get() != null) {
            // 1.1 holder保证了可见性,从而不会使用没有初始化的对象
            return c.cast(holder.get());
        }

        // 2. 同步块:确保只有一个线程创建实例
        synchronized (lock) {
            // 3. 第二次检查:防止其他线程已创建holder
            holder = OBJECT_MAP_NEW.computeIfAbsent(key, k -> new Holder<>());

            // 4. 创建实例(此处不需要再次检查holder.get(),因为锁保证了互斥性)
            if (holder.get() == null) {
                try {
                    // 4.1 创建对象
                    T instance = constructor.get();
                    // 4.2 放入到map里面
                    holder.set(instance);
                } catch (Exception e) {
                    throw new RuntimeException("创建示例失败", e);
                }
            }
        }

        return c.cast(holder.get());
    }

    public static <T> T getInstance(Consumer<T> initConsumer, Class<T> c) {
        if (c == null) {
            throw new IllegalArgumentException("Class cannot be null");
        }
        String key = c.getName();

        // 1. 第一次检查:快速读取缓存(无锁)
        Holder<Object> holder = OBJECT_MAP_NEW.get(key);
        if (holder != null && holder.get() != null) {
            // 1.1 holder保证了可见性,从而不会使用没有初始化的对象
            return c.cast(holder.get());
        }

        // 2. 同步块:确保只有一个线程创建实例
        synchronized (lock) {
            // 3. 第二次检查:防止其他线程已创建holder
            holder = OBJECT_MAP_NEW.computeIfAbsent(key, k -> new Holder<>());

            // 4. 创建实例(此处不需要再次检查holder.get(),因为锁保证了互斥性)
            if (holder.get() == null) {
                try {
                    // 4.1 创建对象
                    T instance = c.getDeclaredConstructor().newInstance();
                    // 4.2 初始化对象
                    initConsumer.accept(instance);
                    // 4.2 放入到map里面
                    holder.set(instance);
                } catch (Exception e) {
                    throw new RuntimeException("创建示例失败", e);
                }
            }
        }

        return c.cast(holder.get());
    }


    public static <T> T getInstance(Class<T> c) {
        if (c == null) {
            throw new IllegalArgumentException("Class cannot be null");
        }
        String key = c.getName();

        // 1. 第一次检查:快速读取缓存(无锁)
        Holder<Object> holder = OBJECT_MAP_NEW.get(key);
        if (holder != null && holder.get() != null) {
            // 1.1 holder保证了可见性,从而不会使用没有初始化的对象
            return c.cast(holder.get());
        }

        // 2. 同步块:确保只有一个线程创建实例
        synchronized (lock) {
            // 3. 第二次检查:防止其他线程已创建holder
            holder = OBJECT_MAP_NEW.computeIfAbsent(key, k -> new Holder<>());

            // 4. 创建实例(此处不需要再次检查holder.get(),因为锁保证了互斥性)
            if (holder.get() == null) {
                try {
                    Constructor<T> constructor =  c.getDeclaredConstructor();
                    constructor.setAccessible(true);
                    T instance = constructor.newInstance();
                    // 4.2 放入到map里面
                    holder.set(instance);
                } catch (Exception e) {
                    throw new RuntimeException("创建示例失败", e);
                }
            }
        }

        return c.cast(holder.get());
    }

    public static <T> T getInstanceOld(Class<T> c) {
        if (c == null) {
            throw new IllegalArgumentException();
        }
        String key = c.toString();
        if (OBJECT_MAP.containsKey(key)) {
            return c.cast(OBJECT_MAP.get(key));
        } else {
            synchronized (lock) {
                if (!OBJECT_MAP.containsKey(key)) {
                    try {
                        T instance = c.getDeclaredConstructor().newInstance();
                        OBJECT_MAP.put(key, instance);
                        return instance;
                    } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                        throw new RuntimeException(e.getMessage(), e);
                    }
                } else {
                    return c.cast(OBJECT_MAP.get(key));
                }
            }
        }
    }
}

上面的类中,getInstance(Consumer<T> initConsumer, Class<T> c)这个方法,提供了带有初始化方法实例获取。可以解决实例化后,无法初始化的问题。

3. SPI的测试

使用maven工程,引入unit4的测试模块,就可以用下面的方法,测试SPI机制了

package github.javaguide;

import github.javaguide.extension.ExtensionLoader;
import github.javaguide.loadbalance.LoadBalance;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

/**
 * @author: Zekun Fu
 * @date: 2025/6/8 19:58
 * @Description:
 */
@Slf4j
public class ExtentionTest {


    @Test
    public void testExtention() {
        LoadBalance loadBalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension("loadBalanceNew");

    }
}



网站公告

今日签到

点亮在社区的每一天
去签到