浅谈SPI

153 阅读17分钟

一:Java SPI的概念和术语

SPI 全称:Service Provider Interface(服务提供者的接口);它是从Java6开始引入的,是一种基于ClassLoader来发现并加载服务的机制。

1.1、Java SPI 有四个要素

  • **SPI 接口:为服务提供者实现类约定的的接口或抽象类。
  • **SPI 实现类:实际提供服务的实现类,且必须具备午餐默认构造方法(因为通过反射技术实例化它时,是不带参数的)。
  • **SPI 配置:Java SPI 机制约定的配置文件,提供查找服务实现类的逻辑。配置文件必须置于 META-INF/services/ 目录中,且文件名应与服务提供者接口的完全限定名保持一致。文件中的每一行都有一个实现服务类的详细信息,同样是服务提供者类的完全限定名称,且独占一行。
  • **ServiceLoader:Java SPI 的核心组件,用于在运行时发现并加载 SPI 实现类。ServiceLoader 中有各种实用方法来获取特定实现、迭代它们或重新加载服务。

1.2、设计思想:

JAVA SPI = 基于接口的编程+策略模式+配置文件 的动态加载机制。

1.3、Java SPI ServiceLoader 解析图

Java SPI ServiceLoader解析简图.png

1.4、Java SPI 的运行流程:

应用程序加载JAVA SPI过程.png

1.5、核心代码

简单介绍一下该类的基本操作流程。

通过ServiceLoader的load(Class service)方法进入程序内部:

public static <S> ServiceLoader<S> load(Class<S> service) {
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    return ServiceLoader.load(service, cl);
}
// service 传入的是期望加载的 SPI 接口类型
// loader 是用于加载 SPI 服务的类加载器
public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader){
    return new ServiceLoader<>(service, loader);
}

上面load方法内获得到ClassLoader,并再此调用内部的load(Class service,lassLoader loader)方法,该方法内会创建ServiceLoader对象,并初始化一些常量。

为什么需要指定类加载器?SPI 的接口是 Java 核心库的一部分,是由BootstrapClassLoader来加载的;SPI 实现的 Java 类一般是由AppClassLoader来加载的。BootstrapClassLoader是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。

它也不能代理给 AppClassLoader,因为它是最顶层的类加载器(在双亲委派模型中,子类加载器可以使用父类加载器已经加载的类,而父类加载器无法使用子类加载器已经加载的)。

也就是说,类加载器的代理模式无法解决这个问题。如果不做任何的设置,Java 应用的线程的上下文类加载器默认就是 AppClassLoader。

在核心类库使用 SPI 接口时,传递的类加载器使用线程上下文类加载器,就可以成功的加载到 SPI 实现的类。参考 :Java类加载器 — classloader 的原理及应用

// 重新加载 SPI 服务
private ServiceLoader(Class<S> svc, ClassLoader cl) {
    service = Objects.requireNonNull(svc, "Service interface cannot be null");
    // 指定类加载 ClassLoader 和访问控制上下文
    loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
    acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
    // 然后,重新加载 SPI 服务
    reload();
}

ServiceLoader的构造方法内会调用reload方法,来清理缓存,初始化LazyIterator,注意此处是Lazy,也就懒加载。此时并不会去加载文件下的内容。

public void reload() {
    // 清空缓存中所有已实例化的 SPI 服务
    providers.clear();
    // 根据 ClassLoader 和 SPI 类型,创建懒加载迭代器
    lookupIterator = new LazyIterator(service, loader);
}

上述代码处理,简单的说就是先找当前线程绑定的 ClassLoader,如果没有就用 SystemClassLoader,然后清除一下缓存,再创建一个 LazyIterator。
接下来看LazyIterator,从上面代码可以看到我们调用了 hasNext() 来做实例循环,通过 next() 得到一个实例。而 LazyIterator 其实就是 Iterator 的实现类。

// lookupIterator 是 LazyIterator 实例,用于懒加载 SPI 实例
public boolean hasNext() {
    if (acc == null) {
        return hasNextService();
    } else {
        PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
            public Boolean run() { return hasNextService(); }
        };
        return AccessController.doPrivileged(action, acc);
    }
}
private boolean hasNextService() {
    if (nextName != null) {
        return true;
    }
    if (configs == null) {
        try {
            // 拼接 META-INF/services/ + SPI 接口全限定名
            String fullName = PREFIX + service.getName();
            // 通过类加载器,尝试加载资源文件
            if (loader == null)
                configs = ClassLoader.getSystemResources(fullName);
            else
                configs = loader.getResources(fullName);
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    while ((pending == null) || !pending.hasNext()) {
        if (!configs.hasMoreElements()) {
            return false;
        }
        // 解析资源文件中的内容,获取 SPI 接口的实现类的全限定名 nextName
        pending = parse(service, configs.nextElement());
    }
    nextName = pending.next();
    return true;
}

public S next() {
    if (acc == null) {
        return nextService();
    } else {
        PrivilegedAction<S> action = new PrivilegedAction<S>() {
            public S run() { return nextService(); }
        };
        return AccessController.doPrivileged(action, acc);
    }
}
private S nextService() {
    if (!hasNextService())
        throw new NoSuchElementException();
    String cn = nextName;
    nextName = null;
    Class<?> c = null;
    try {
        c = Class.forName(cn, false, loader);
    } catch (ClassNotFoundException x) {
        fail(service,
             "Provider " + cn + " not found");
    }
    if (!service.isAssignableFrom(c)) {
        fail(service, "Provider " + cn  + " not a subtype");
    }
    try {
        S p = service.cast(c.newInstance());
        // ServiceLoader 类维护了一个缓存 providers( LinkedHashMap 对象),缓存 providers 中保存了已经被成功加载的 SPI 实例,这个 Map 的 key 是 SPI 接口实现类的全限定名,value 是该实现类的一个实例对象。
        providers.put(cn, p);
        return p;
    } catch (Throwable x) {
        fail(service, "Provider " + cn + " could not be instantiated", x);
    }
    throw new Error();          // This cannot happen
}

hasNextService()方法:就是在约定好的地方找到接口对应的文件,然后加载文件并且解析文件里面的内容。 nextService()方法:就是通过文件里填写的全限定名加载类,并且创建其实例放入缓存之后返回实例。

以上核心逻辑:ServiceLoader扫描了所有jar包下的配置文件*(META-INF/servies/)。然后通过解析全限定名获得,并在遍历时进行实例化。

二、JAVA SPI个人实践

public interface Exercise {
    void doExercise();
}
public class RunningExercise implements Exercise {
    @Override
    public void doExercise() {
        System.out.println("Running");
    }
}
public class SwimmingExercise implements Exercise {
    @Override
    public void doExercise() {
        System.out.println("Swimming");
    }
}
public class ExerciseTest {
    public static void main(String[] args) {
        ServiceLoader<Exercise> services = ServiceLoader.load(Exercise.class);
        Iterator<Exercise> iterator = services.iterator();
        while (iterator.hasNext()) {
            Exercise exercise = iterator.next();
            exercise.doExercise();
        }
    }
}

在 META-INF/services/ 目录下建了个以接口全限定名命名的文件(com.learn.demo.provider.service.Exercise),内容如下:

io.seata.samples.integration.call.service.RunningExercise
io.seata.samples.integration.call.service.SwimmingExercise

运行测试程序结果如下

image.png

整体的 Java SPI 的源码解析已经完毕,就是约定一个目录,根据接口名去那个目录找到文件,文件解析得到实现类的全限定名,然后循环加载实现类和创建其实例。

以上面实例解释JAVA SPI过程:

JAVA SPI 过程解析图 (1).png

实际上,Java SPI 机制依赖于 ServiceLoader 类去解析、加载服务。

三:SPI机制的广泛应用

3.1应用案例之JDBC

JDBC访问数据库的调用流程:

JDBC访问数据库的调用流程 (1).png

原始手动加载驱动,进行数据库操作:

// 加载驱动(使用Class.forName方法)
Class.forName("com.mysql.jdbc.Driver");
// 打开数据库连接
Connection conn  = DriverManager.getConnection(url, user, password);
// 执行SQL语句(使用Statement类)
Statement st =  conn.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM T_UER");
while(rs.next()){
    System.out.println(rs.getInt("id"));
}
// 关闭链接
rs.close();
st.close();
conn.close();
public static Class<?> forName(String className) throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

针对一个数据库,可能会存在着不同的数据库驱动实现。我们在使用特定的驱动实现时,不希望修改现有的代码,而希望通过一个简单的配置就可以达到效果。

DriverManager是jdbc里管理和注册不同数据库driver的工具类,位于Java.sql包中,由boot类加载器来进行加载。加载该类时,会先执行静态代码块:

static {
    try {
        // Mysql 在初始化时注册自己的驱动到驱动管理器中(注册驱动)
        DriverManager.registerDriver(new Driver());
    } catch (SQLException var1) {
        throw new RuntimeException("Can't register driver!");
    }
}
public static synchronized void registerDriver(java.sql.Driver driver)
    throws SQLException {
    registerDriver(driver, null);
}
public static synchronized void registerDriver(java.sql.Driver driver,
        DriverAction da)
    throws SQLException {

    /* Register the driver if it has not already been added to our list */
    if(driver != null) {
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    } else {
        // This is for compatibility with the original DriverManager
        throw new NullPointerException();
    }

    println("registerDriver: " + driver);

}

DriverManager是管理Jdbc驱动的基础服务类,位于Java.sql包中,由boot类加载器来进行加载。加载该类时,会先执行静态代码块:

/**
 * Load the initial JDBC drivers by checking the System property
 * jdbc.properties and then use the {@code ServiceLoader} mechanism
 */
// 
static {
    // 加载各种数据库驱动
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}

加载数据库驱动:

private static void loadInitialDrivers() {
        String drivers;
        try {
            // 加载配置文件中的jdbc.drivers指定的驱动
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    return System.getProperty("jdbc.drivers");
                }
            });
        } catch (Exception ex) {
            drivers = null;
        }
        // If the driver is packaged as a Service Provider, load it.
        // Get all the drivers through the classloader
        // exposed as a java.sql.Driver.class service.
        // ServiceLoader.load() replaces the sun.misc.Providers()
	// 加载SPI约定的/META-INF/services下面的驱动
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
            	// 实例化ServiceLoader对象,并注入线程上下文类加载器和java.sql.Driver.class
                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;
            }
        });
        println("DriverManager.initialize: jdbc.drivers = " + drivers);
        if (drivers == null || drivers.equals("")) {
            return;
        }
        String[] driversList = drivers.split(":");
        println("number of Drivers:" + driversList.length);
        for (String aDriver : driversList) {
            try {
                println("DriverManager.Initialize: loading " + aDriver);
                Class.forName(aDriver, true,
                        ClassLoader.getSystemClassLoader());
            } catch (Exception ex) {
                println("DriverManager.Initialize: load failed: " + ex);
            }
        }
    }

经历了上述ServiceLoader类中的一系列操作之后(包括服务发现和类加载),位于mysql驱动包中的Driver类会被初始化。 Driver类加载时,会执行静态代码块,即执行DriverManager.registerDriver(new Driver())方法,向DriverManager中注册一个Driver实例。

这里可以看到,不同的驱动实现了相同的接口java.sql.Driver,然后通过registerDriver把当前driver加载到DriverManager中这就体现了使用方提供规则,提供方根据规则把自己的实现加载到使用方中的spi思想。

public static synchronized void registerDriver(java.sql.Driver driver)
    throws SQLException {

    registerDriver(driver, null);
}
public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da)
    throws SQLException {
    /* Register the driver if it has not already been added to our list */
    // 如果驱动程序还没有添加到我们的列表中,则加入列表中
    if(driver != null) {
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    } else {
        // This is for compatibility with the original DriverManager
        throw new NullPointerException();
    }
    println("registerDriver: " + driver);
}

该MySQL驱动添加到成员变量registeredDrivers中,该成员变量存放已注册的jdbc驱动列表,在应用程序执行数据库连接操作时,会调用getConnection方法,遍历registeredDrivers,获取驱动,建立数据库连接。

那如果发现了多个 driver 的话,要选择哪一个具体的实现呢?那就是 JDBC URL 了,driver 的实现有一个约定,如果 driver 根据 JDBC URL 判断不是自己可以处理的连接就直接返回空,DriverManager 就是基于这个约定直到找到第一个不返回 null 的连接为止。

3.2应用案例之DUBBO

类似 Java SPI 中 ServiceLoader 的存在,Dubbo有自己SPI实现:

  • Java SPI 有四个要素在Dubbo中基本一致,不同的是:

    • 使用约定的配置路径变为:文件路径是META-INF/services/ (对JAVA SPI的兼容)、META-INF/dubbo/ (一般存放对DUBBO的扩展)、META-INF/dubbo/internal(Dubbo自己的实现)。
    • JAVA SPI中是由ServiceLoader进行加载处理,而在Dubbo中是由ExtensionLoader加载处理
  • 另外需要在Dubbo SPI接口只能是接口,且接口上必须加上注解@SPI。

简单示例:

@SPI
public interface Exercise {
    void exercise();
}
public class RunningExercise implements Exercise {
    @Override
    public void exercise() {
        System.out.println("Running...");
    }
}
public class SwimmingExercise implements Exercise {
    @Override
    public void exercise() {
        System.out.println("Swimming...");
    }
}


// Protocol. (API/SPI, Singleton, ThreadSafe)
@SPI("dubbo")
public interface Protocol {
    int getDefaultPort();
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
    void destroy();
    default List<ProtocolServer> getServers() {
        return Collections.emptyList();
    }
}
public class MyProtocol implements Protocol {
    public static final int DEFAULT_PORT = 1199;
    public static final String DEFAULT_HOST = "com.learn";
    @Override
    public int getDefaultPort() {
        return DEFAULT_PORT;
    }
    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        System.out.println("MyProtocol export...");
        return null;
    }
    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        System.out.println("MyProtocol refer...");
        return null;
    }
    @Override
    public void destroy() {
        System.out.println("MyProtocol destroy...");
    }
}
public class ExerciseTest {
    public static void main(String[] args) {
        ExtensionLoader<Exercise> extensionLoader = ExtensionLoader.getExtensionLoader(Exercise.class);
        Exercise running = extensionLoader.getExtension("running");
        running.exercise();
        Exercise swimming = extensionLoader.getExtension("swimming");
        swimming.exercise();
        System.out.println("********分割线***********");
        ExtensionLoader<Protocol> protocolExtensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
        Protocol myProtocol = protocolExtensionLoader.getExtension("myProtocol");
        myProtocol.destroy();
    }
}

META-INF/dubbo/下:

`# org.apache.dubbo.rpc.Protocol文件中`
`myProtocol=com.learn.demo.provider.service.impl.MyProtocol`

运行测试程序结果如下: image.png

Dubbo加载扩展的整个流程:

extension-load.png

主要步骤为 4 个:

  • 读取并解析配置文件。
  • 缓存所有扩展实现。
  • 基于用户执行的扩展名,实例化对应的扩展实现。
  • 进行扩展实例属性的 IOC 注入以及实例化扩展的包装类,实现 AOP 特性。

核心代码如下:

// 根据类型查找对应的ExtensionLoader
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    if (type == null)
        throw new IllegalArgumentException("Extension type == null");
    if (!type.isInterface()) {
        throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
    }
    if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type(" + type +
                ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
    }
    // 从缓存中获取
    ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    if (loader == null) {
        // 缓存中没有获取到,则创建一个新的当前类型的ExtensionLoader,将创建后的ExtensionLoader存到缓存中
        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
        loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
    }
    return loader;
}
private ExtensionLoader(Class<?> type) {
    this.type = type;
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
// 创建对应类型的实例
public T getAdaptiveExtension() {
    // 从缓存中获取对应的实例
    Object instance = cachedAdaptiveInstance.get();
    if (instance == null) {
        if (createAdaptiveInstanceError == null) {
            synchronized (cachedAdaptiveInstance) {
                instance = cachedAdaptiveInstance.get();
                if (instance == null) {
                    try {
                        // 缓存中不存在,则创建实例
                        instance = createAdaptiveExtension();
                        // 将创建的实例存到缓存中
                        cachedAdaptiveInstance.set(instance);
                    } catch (Throwable t) {
                        createAdaptiveInstanceError = t;
                        throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                    }
                }
            }
        } else {
            throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
        }
    }
    // 返回实例
    return (T) instance;
}

getAdaptiveExtension 方法首先会检查缓存,缓存未命中,则调用 createAdaptiveExtension 方法创建自适应拓展。下面,我们看一下 createAdaptiveExtension 方法的代码。

private T createAdaptiveExtension() {
    try {
        // 获取自适应拓展类,并通过反射实例化
        return injectExtension((T) getAdaptiveExtensionClass().newInstance());
    } catch (Exception e) {
        throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}
private Class<?> getAdaptiveExtensionClass() {
    // 通过 SPI 获取所有的拓展类
    getExtensionClasses();
    // 检查缓存,若缓存不为空,则直接返回缓存
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    // 创建自适应拓展类
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
private Map<String, Class<?>> getExtensionClasses() {
    // doble check 从缓存中获取
    Map<String, Class<?>> classes = cachedClasses.get();
    if (classes == null) {
        synchronized (cachedClasses) {
            classes = cachedClasses.get();
            if (classes == null) {
                // 缓存中没有则去找拓展类
                classes = loadExtensionClasses();
                cachedClasses.set(classes);
            }
        }
    }
    return classes;
}
private Map<String, Class<?>> loadExtensionClasses() {
    final SPI defaultAnnotation = type.getAnnotation(SPI.class);
    if (defaultAnnotation != null) {
        String value = defaultAnnotation.value();
        if ((value = value.trim()).length() > 0) {
            String[] names = NAME_SEPARATOR.split(value);
            if (names.length > 1) {
                throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
                        + ": " + Arrays.toString(names));
            }
            if (names.length == 1) cachedDefaultName = names[0];
        }
    }

    // 加载不同目录下的类,放到Map中
    Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
    // META-INF/dubbo/internal/
    loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
    // META-INF/dubbo/
    loadDirectory(extensionClasses, DUBBO_DIRECTORY);
    // META-INF/services/
    loadDirectory(extensionClasses, SERVICES_DIRECTORY);
    return extensionClasses;
}
private Class<?> createAdaptiveExtensionClass() {
    String code = createAdaptiveExtensionClassCode();
    ClassLoader classLoader = findClassLoader();
    com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    return compiler.compile(code, classLoader);
}

createAdaptiveExtension 方法的代码比较少,但却包含了三个逻辑,分别如下:

  1. 调用 getAdaptiveExtensionClass 方法获取自适应拓展 Class 对象。
  2. 通过反射进行实例化。
  3. 调用 injectExtension 方法向拓展实例中注入依赖(Dubbo 中有两种类型的自适应拓展,一种是手工编码的,一种是自动生成的。手工编码的自适应拓展中可能存在着一些依赖,而自动生成的 Adaptive 拓展则不会依赖其他类。这里调用 injectExtension 方法的目的是为手工编码的自适应拓展注入依赖)。

getAdaptiveExtensionClass 方法同样包含了三个逻辑,如下:

  1. 调用 getExtensionClasses 获取所有的拓展类。
  2. 检查缓存,若缓存不为空,则返回缓存。
  3. 若缓存为空,则调用 createAdaptiveExtensionClass 创建自适应拓展类。
private Class<?> createAdaptiveExtensionClass() {
    // 构建自适应拓展代码
    String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
    ClassLoader classLoader = findClassLoader();
    // 获取编译器实现类
    org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    // 编译代码,生成 Class
    return compiler.compile(code, classLoader);
}

createAdaptiveExtensionClass 方法用于生成自适应拓展类,该方法首先会生成自适应拓展类的源码,然后通过 Compiler 实例(Dubbo 默认使用 javassist 作为编译器)编译源码,得到代理类 Class 实例。

// 根据名称获取拓展实例
public T getExtension(String name) {
    return getExtension(name, true);
}

public T getExtension(String name, boolean wrap) {
    if (StringUtils.isEmpty(name)) {
        throw new IllegalArgumentException("Extension name == null");
    }
    if ("true".equals(name)) {
        // 获取默认的拓展实现类
        return getDefaultExtension();
    }
    // Holder,用于持有目标对象
    final Holder<Object> holder = getOrCreateHolder(name);
    Object instance = holder.get();
    if (instance == null) {
        synchronized (holder) {
            instance = holder.get();
            if (instance == null) {
                // double check, 没有对象实例,则创建对象实例
                instance = createExtension(name, wrap);
                // 设置实例到 holder 中
                holder.set(instance);
            }
        }
    }
    return (T) instance;
}
private Holder<Object> getOrCreateHolder(String name) {
    // 从实例缓存中获取Holder包装的实例
    Holder<Object> holder = cachedInstances.get(name);
    if (holder == null) {
        cachedInstances.putIfAbsent(name, new Holder<>());
        holder = cachedInstances.get(name);
    }
    return holder;
}

private T createExtension(String name, boolean wrap) {
    // 从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射关系表
    Class<T> clazz = getExtensionClasses().get(name);
    if (clazz == null) {
        throw findException(name);
    }
    try {
        T instance = (T) EXTENSION_INSTANCES.get(clazz);
        if (instance == null) {
            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
            instance = (T) EXTENSION_INSTANCES.get(clazz);
        }
        // 向实例中注入依赖
        injectExtension(instance);
        // 如果启用包装的话,则自动为进行包装.`
        // 比如我基于 Protocol 定义了 DubboProtocol 的扩展,但实际上在 Dubbo 中不是直接使用的 DubboProtocol, 而是其包装类
        // ProtocolListenerWrapper`
        if (wrap) {
           
            List<Class<?>> wrapperClassesList = new ArrayList<>();
            if (cachedWrapperClasses != null) {
                wrapperClassesList.addAll(cachedWrapperClasses);
                wrapperClassesList.sort(WrapperComparator.COMPARATOR);
                Collections.reverse(wrapperClassesList);
            }
            // 循环创建 Wrapper 实例
            if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
                for (Class<T> wrapperClass : wrapperClassesList) {
                    Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
                    if (wrapper == null
                            || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
                        // 将当前 instance 作为参数传给 Wrapper 的构造方法,并通过反射创建 Wrapper 实例。
                        // 然后向 Wrapper 实例中注入依赖,最后将 Wrapper 实例再次赋值给 instance 变量
                        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                    }
                }
            }
        }

        // 初始化
        initExtension(instance);
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                type + ") couldn't be instantiated: " + t.getMessage(), t);
    }
}

首先检查缓存,缓存未命中则创建拓展对象。 createExtension 方法包含了如下的步骤:

  1. 通过 getExtensionClasses 获取所有的拓展类。
  2. 通过反射创建拓展对象。
  3. 向拓展对象中注入依赖。
  4. 将拓展对象包裹在相应的 Wrapper 对象中。
  5. 初始化拓展对象。

getAdaptiveExtension 方法首先会检查缓存,缓存未命中,则调用 createAdaptiveExtension 方法创建自适应拓展。下面,我们看一下 createAdaptiveExtension 方法的代码。

其他介绍可以移步于此Dubbo扩展实例

3.3应用案例之SpringBoot自动配置

SpringBoot自动配置即Auto-configuaration:

  • 是基于引入的Jar包,对SpringBoot应用进行自动配置,为”开箱即用“提供基础支持。
  • 提供自动配置功能依赖的Jar包,通常被称为starter,例如mybatis-spring-boot-starter等。

注解@ComponentScan

  • 它的作用是对指定的package进行扫描,找到其中符合条件的类,默认是搜索被注解@ComponentScan修饰的配置类。
  • 通过属性basePackages或者basePackageClass,来指定要扫描的package。
  • 如果未指定package,则默认扫描当前@ComponentScan所修饰的类所在的package。

注解@Import

  • 它的作用是提供了一种显示的从其他地方(通常指第三方Jar包)加载配置类的方式,这样可以避免使用性能较差的组件扫描(ComponentScan)。

  • 支持导入:

    • 普通类(需要知道各个类名)。
    • 接口ImportSelector的实现类。
    • 接ImportBeanDefinitionRegistrar的实现类(一般是为普通BeanDefinition而设计的,是对@Bean注解的一种补充)。

SpringBoot集成其他框架.png

@SpringBootApplication(scanBasePackages = "io.seata.samples.integration.call")
public class BusinessExampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(BusinessExampleApplication.class, args);
    }
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
      @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
 // 代码省略
}

// AutoConfigurationImportSelector implements DeferredImportSelector extends ImportSelector,实现自动配置的功能,通过@Import(AutoConfigurationImportSelector.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    // 代码省略
}

// AutoConfigurationImportSelector#selectImports
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
   if (!isEnabled(annotationMetadata)) {
      return NO_IMPORTS;
   }
   AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
         .loadMetadata(this.beanClassLoader);
   // 获取autoConfigurationMetadata的注解@EnableAutoConfiguration 的属性
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   // 从资源文件spring.factories中获取EnableAutoConfiguration对应所有的类
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   configurations = removeDuplicates(configurations);
   // 通过在注解@EnableAutoConfiguration 设置exclude的相关属性,可以排除指定的自动配置类
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
   checkExcludedClasses(configurations, exclusions);
   configurations.removeAll(exclusions);
   // 根据注解@Conditional来判断是否需要排除某些自动配置类
   configurations = filter(configurations, autoConfigurationMetadata);
   // 触发AutoConfiguration导入的相关事件
   fireAutoConfigurationImportEvents(configurations, exclusions);
   return StringUtils.toStringArray(configurations);
}

// 通过SpringFactories机制,从配置文件spring.factories中找出所有的自动配置类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
      AnnotationAttributes attributes) {
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
         getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
   Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
   return configurations;
}
// 返回SpringFactoriesLoader用来加载候选配置的类
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
   return EnableAutoConfiguration.class;
}

自动配置的核心实现:

  • 使用约定的配置文件:
    • 文件路径是META-INF/spring.factories。
    • 文件内容格式是"key=value1,value2,...valueN",其中key是EnableAutoConfiguration的类名,value是自动配置类的类名。
  • 提供自动配置类的Jar包,则需同时提供配置文件META-INF/spring.factories。
  • 和JAVA SPI一样:使用ClassLoader的getResource和getResources方法,来读取classpath中的配置文件;SpringBoot通过类SpringFactoriesLoader,返回一个类名称的集合,可以根据需求对这些类名进行下一步的处理。
// mybatis-spring-boot-autoconfigure-2.1.1.jar中的META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

SpringBoot自动配置的核心流程:

SpringBoot自动配置核心流程.png

为什么要使用spi:

在使用Java进行面向对象开发时,一般会推荐使用基于接口的编程,程序的模块与模块之间不会直接进行实现类的硬编码。而在实际的开发过程中,往往一个接口会有多个实现类,各实现类要么实现的逻辑不同,要么使用的方式不同,还有的就是实现的技术不同。

为了使调用方在调用接口的时候,明确的知道自己调用的是接口的哪个实现类,或者说为了实现在模块装配的时候不用在程序里动态指明,这就需要一种服务发现机制。

Java SPI 就是提供了这样一个机制:SPI提供了一组组件发现和注册的方式,可以用于实现各种插件,或者灵活替换框架所使用的的组件,为某个接口寻找服务实现的机制。

在面向对象编程中,基于开闭原则和解耦的需要,一般建议用接口进行模块之间通信编程,通常情况下调用方模块是不会感知到被调用方模块的内部具体实现。

基于面向接口编程,优雅的实现模块之间的解耦。 SPI是专门提供给服务提供者或者扩展框架功能的开发者去使用的一个接口。 SPI 将服务接口和具体的服务实现分离开来,将服务调用方和服务实现者解耦,能够提升程序的扩展性、可维护性。修改或者替换服务实现并不需要修改调用方。

参考文件:

1、Java类加载器 — classloader 的原理及应用

2、什么是双亲委派模型

3、Java中SPI机制深入及源码解析

4、Dubbo的扩展性

5、Dubbo扩展实例

6源码级深度理解 Java SPI

7、JDBC连接