Java SPI 机制

什么是 SPI

SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制。可以将服务接口与服务实现分离以达到解耦可拔插、大大提升了程序可扩展性。

1、制定统一的规范

2、服务提供商提供这个规范具体的实现,在自己 jar 包的 META-INF/services/ 目录里创建一个以服务接口命名的文件,内容是实现类的全命名

3、平台引入外部模块的时候,就能通过该jar包 META-INF/services/ 目录下的配置文件找到该规范具体的实现类名,然后装载实例化,完成该模块的注入。

Java SPI 应用实例

定义接口

package com.demo.lin.spi;

/**
 * @author Lin = ̄ω ̄=
 */
public interface Download {
	void download(String url);
}

实现类

package com.demo.lin.spi.impl;

import com.demo.lin.spi.Download;

/**
 * @author Lin = ̄ω ̄=
 */
public class FileDownload implements Download {

	@Override
	public void download(String url) {
		System.out.println("download " + url);
	}
}

配置

com.demo.lin.spi.Download 内容

com.demo.lin.spi.impl.FileDownload

调用

package com.demo.lin.spi;

import java.util.ServiceLoader;

/**
 * @author Lin = ̄ω ̄=
 */
public class TestDemo {

	public static void main(String[] args) {
		ServiceLoader<Download> searches = ServiceLoader.load(Download.class);
		for (Download search : searches) {
			search.download("https://www.bilibili.com/video/BV1Vt4y1D7v8");
		}
	}
}

输出

Java SPI 的用途

其中一个典型案例就是 java.sql.Driver

java.sql.Driver 定义了一个接口,并没有具体实现,具体的实现都是由不同厂商来提供的。

当我们想连接 Mysql 时,引入 jar 包如 mysql-connector-java-5.1.45.jar,在 META-INF/services 目录下会有一个名字为 java.sql.Driver 的文件:

com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver

我们在代码中写到

String url = "jdbc:mysql://localhost:3306/test";
Connection conn = DriverManager.getConnection(url,username,password);

DriverManager 内部使用 ServiceLoader.load(Driver.class),搜索 classpath 下以及 jar 包中所有的 META-INF/services 目录下的java.sql.Driver 文件,并找到文件中的实现类的名字,实例化具体的实现类。

Spring SPI机制

Spring 的 SPI 机制主要体现在 SpringBoot 中,当我们启动 SpringBoot 的 run() 方法,会调用类 SpringFactoriesLoader 下的 loadSpringFactories 方法,去加载 META-INF/spring.factories 下的配置。

public final class SpringFactoriesLoader {

	/**
	 * The location to look for factories.
	 * <p>Can be present in multiple JAR files.
	 */
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
	
    // ...
    
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            // ...
        }
        // ...
    }
}
posted @ 2021-05-19 15:05  心灵蚂蚁  阅读(103)  评论(0编辑  收藏  举报