01-反射
在之前的文章 反射机制 中,我们学习了 Java 中的反射机制。反射机制主要是为应用程序提供了在运行时访问类及其组成(Class 对象及其中的 Method、Constructor、Field 等对象)的接口。
反射在各类框架中都有广泛的应用,例如 Spring。
除了各类框架,JDK 中的许多机制也都是通过反射实现的,例如 SPI 机制。
02-SPI
SPI(Service Provider Interface)是 Java 6 引入的一种发现和加载特定接口实现的特性。它由四部分组成:
Service,一组接口或类,提供特定的功能或特性;
SPI,一个接口或抽象类,作为 1.中服务的代理或接入点;
1 和 2 组成了 Java 生态系统中常被提到的 API。
Service Provider,2.中 SPI 的特定实现;一般配置在 /META-INF/services/ 文件夹下以 SPI 全量名为文件名,文件内容为服务提供者,即具体的特定实现。
ServiceLoader,java.util.ServiceLoader
,SPI 机制的核心,用来发现并加载 Service Provider;
上述组件之间的关系如下图所示:
图 1. SPI 机制中各组件之间的关系
02.1-SPI in JDBC
JDBC 中加载不同数据库的驱动是最常见的 SPI 应用。Java 中定义了 SPI java.sql.Driver
,在java.sql.DriverManager
中使用java.util.ServiceLoader
对不同实现进行发现并加载。mysql-connector-java-8.0.7-dmr.jar 和 hsqldb-2.3.4.jar 作为 Servcie Provider 提供了对java.sql.Driver
的实现。
注:我们检查上述两个 jar 包可以发现,在它们的 META-INF/serivces 下存在一个名为 java.sql.Driver 的文件,其内容分别是com.mysql.cj.jdbc.Driver
和org.hsqldb.jdbc.JDBCDriver
。
java.sql.DriverManager
中加载 SPI 实现的代码如下:
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try {
while (driversIterator.hasNext()) {
driversIterator.next();
}
} catch (Throwable t) {
// Do nothing
}
复制代码
02.2-SPI in JCL
JCL(Jakarta Commons Logging)是 Jakarta EE 中日志库门面实现。org.apache.commons.logging.LogFactory#getFactory 方法中使用了 SPI 来加载具体的日志库实现。
// Determine which concrete LogFactory subclass to use.
// 1. First, try a global system property
try {
// FACTORY_PROPERTY = "org.apache.commons.logging.LogFactory"
// 全局变量
String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);
}...
// 2. Second, try to find a service by using the JDK1.3 class
// discovery mechanism, which involves putting a file with the name
// of an interface class in the META-INF/services directory, where the
// contents of the file is a single line specifying a concrete class
// that implements the desired interface.
try {
// SERVICE_ID = "META-INF/services/org.apache.commons.logging.LogFactory"
// SPI 机制
final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID);
}...
// 3. Third try looking into the properties file read earlier (if found)
try {
// FACTORY_PROPERTY = "org.apache.commons.logging.LogFactory"
// classpath根目录commons-logging.properties中
// org.apache.commons.logging.LogFactory属性
String factoryClass = props.getProperty(FACTORY_PROPERTY);
}...
// 4. Fourth, try the fallback implementation class
// FACTORY_DEFAULT = "org.apache.commons.logging.impl.LogFactoryImpl"
// 默认实现
factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);
复制代码
jcl-over-slf4j-1.7.32.jar 和 spring-jcl-5.1.5.RELEASE.jar 都属于org.apache.commons.logging.LogFactory
的 Service Provider,它们在 META-INF/services 中分别制定了具体的实现:org.apache.commons.logging.impl.SLF4JLogFactory
和org.apache.commons.logging.LogFactoryService
02.3-SPI in Spring
Spring 中参考 SPI 机制实现了类似的服务发现机制。org.springframework.core.io.support.SpringFactoriesLoader
类实现了对 META-INF/spring.factories 文件发现与文件内类的加载。
org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
// 加载 META-INF/spring-factories 文件
Enumeration<URL> urls = classLoader != null
? classLoader.getResources("META-INF/spring.factories")
: ClassLoader.getSystemResources("META-INF/spring.factories");
复制代码
02.4-SPI in Eclipse Plugin
此处内容参考 pdai.tech : SPI机制 - 插件体系
03-SPI 开发
定义一个 MySpi 抽象类,其包含一个抽象方法 hi
package self.samson.example.basic.spi;
public abstract class MySpi {
public abstract void hi() ;
}
复制代码
实现一个用中文说你好的 Service Provider:
package self.samson.example.basic.spi;
public class ChineseSpi extends MySpi{
@Override
public void hi() {
System.out.println("你好!");
}
}
// 将 META-INFO/services/self.samson.example.basic.spi.MySpi 打包进 zh.jar
// 内容为:self.samson.example.basic.spi.ChineseSpi
复制代码
实现一个用英文说 hi 的 Service Provider:
package self.samson.example.basic.spi;
public class EnglishSpi extends MySpi{
@Override
public void hi() {
System.out.println("hello!");
}
}
// 将 META-INFO/services/self.samson.example.basic.spi.MySpi 打包进 en.jar
// 内容为:self.samson.example.basic.spi.EnglishSpi
复制代码
编写测试类,运行时需要将 zh.jar en.jar 导入到 classpath:
package self.samson.example.basic.spi;
import java.util.Iterator;
import java.util.ServiceLoader;
public class SpiExamples {
public static void main(String[] args) {
ServiceLoader<MySpi> serviceLoader = ServiceLoader.load(MySpi.class);
Iterator<MySpi> iterator = serviceLoader.iterator();
while (iterator.hasNext()) {
MySpi next = iterator.next();
next.hi();
}
}
}
复制代码
输出结果为:
你好!
hello!
历史文章
Java Core「5」自定义注解编程
Java Core「4」java.util.concurrent 包简介
Java Core「3」volatile 关键字
Java Core「2」synchronized 关键字
Java Core「1」JUC- 线程基础
评论