一、SPI机制理解
SPI 全称为 Service Provider Interface 是一种服务发现机制。
它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。
实现步骤:
1、在ClassPath路径下的META-INF/services文件夹下创建文件
2、文件名为接口服务的全限定名,文件内容是多个实现类
3、API加载实现类。
二、Java SPI 案例
1、编写接口服务
public interface SPIService {
void execute();
}
2、创建两个接口实现类 SPIServiceImpl1 和 SPIServiceImpl2
public class SPIServiceImpl1 implements SPIService {
@Override
public void execute() {
System.out.println( this.getClass().getSimpleName() + "....execute..........");
}
}
public class SPIServiceImpl2 implements SPIService{
@Override
public void execute() {
System.out.println( this.getClass().getSimpleName() + "....execute..........");
}
}
3、在META-INF/services 下创建文件,文件名为com.xyy.spi.java.SPIService,文件内容为两个实现类的全限定名
com.xyy.spi.java.SPIServiceImpl1
com.xyy.spi.java.SPIServiceImpl2
4、编写测试类
public class SPIMainClass {
@Test
public void test() {
//方式一:sun.misc.Service 加载
Iterator<SPIService> providers = Service.providers(SPIService.class);
while (providers.hasNext()) {
SPIService servcie = providers.next();
servcie.execute();
}
System.out.println("**************************************************");
//方式二:java.util.ServiceLoader 加载
ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class);
Iterator<SPIService> iterator = load.iterator();
while (iterator.hasNext()) {
SPIService servcie = iterator.next();
servcie.execute();
}
}
}
运行结果:
三、源码分析
1、sun.misc.Service 加载源码分析流程图
总结:
1、创建Service.LazyIterator,设置service等属性
2、hasNext加载META-INF/services下接口文件内容,设置LazyIterator的nextName属性等
3、next方法获得nextName,Class.forName加载类,反射实例化
2、java.util.ServiceLoader 加载源码分析流程图
总结:
1、初始化 ServiceLoader,设置lookupIterator属性为LazyIterator
2、hasNext先从缓存的providers中获取,为空,则去加载META-INF/services下接口文件内容,设置LazyIterator的nextName属性等
3、next方法获得nextName,Class.forName加载类,反射实例化,放入providers
四、应用
1、JDBC驱动
1、创建自定义的MyDriver类,extends com.mysql.cj.jdbc.NonRegisteringDriver implements java.sql.Driver
public class MyDriver extends com.mysql.cj.jdbc.NonRegisteringDriver implements java.sql.Driver{
public MyDriver() throws SQLException {
}
static {
try {
System.out.println("register MyDriver...");
//注册
DriverManager.registerDriver(new MyDriver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register MyDriver!");
}
}
@Override
public Connection connect(String url, Properties info) throws SQLException {
System.out.println("创建连接..."+ url);
System.out.println("jdbc配置信息..." + info);
Connection connect = super.connect(url, info);
System.out.println("数据库连接创建完成..."+ connect.toString());
return connect;
}
}
2、在META-INF/services下创建java.sql.Driver文件,内容为:com.xyy.spi.jdbc.MyDriver
com.xyy.spi.jdbc.MyDriver
3、测试类
@Test
public void test() throws Exception {
String url = "jdbc:mysql://127.0.0.1:3306/db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull&autoReconnect=true&allowMultiQueries=true&useSSL=false";
String user = "root";
String password = "root";
/**
* 不需要这个啦
*/
//Class.forName("com.xyy.spi.jdbc.MyDriver");
Connection connection = DriverManager.getConnection(url, user, password);
PreparedStatement preparedStatement = connection.prepareStatement("SELECT id FROM table");
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
Long id = resultSet.getLong(1);
System.out.println("id=="+ id);
}
preparedStatement.close();
connection.close();
}
运行结果:
如何加载到MyDriver?
源码分析流程图:
总结:
1、DriverManager类加载时,静态代码块调用loadInitialDrivers(),SPI机制加载META-INF/services/java.sql.Driver内的实现类
2、加载MyDriver的静态方法,注册MyDriver
3、DriverManager的getConnection方法,会遍历注册驱动,调用驱动类的connnect方法
2、Dubbo应用
dubbo的SPI机制:
1、接口有@SPI注解,指定默认实现
2、加载三个路径: META-INF/services/、META-INF/dubbo/、META-INF/services/internal
示例1 、扩展dubbo的Protocol接口服务
1、创建类MyProtocol,实现Protocol
public class MyProtocol implements Protocol {
/**
* 我们重写了默认端口
* @return
*/
@Override
public int getDefaultPort() {
return 8888;
}
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
return null;
}
@Override
public <T> Invoker<T> refer(Class<T> aClass, URL url) throws RpcException {
return null;
}
@Override
public void destroy() {
}
}
2、在META-INF/dubbo/下创建文件com.alibaba.dubbo.rpc.Protocol,内容
MyProtocol=com.xyy.spi.dubbo.MyProtocol
3、测试代码
@Test
public void testMyProtocol(){
ExtensionLoader<Protocol> extensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
Protocol myProtocol = extensionLoader.getExtension("MyProtocol");
System.out.println(myProtocol.getDefaultPort());
}
运行结果:
示例2、我们自己定义接口服务
1、创建接口DubboService,写上 @SPI(“dubboService1”)注解,默认是dubboService1
@SPI("dubboService1")
public interface DubboService {
public void sayHello();
}
2、创建DubboService接口的两个实现类
public class DubboServiceImp1 implements DubboService {
@Override
public void sayHello() {
System.out.println("DubboServiceImp1...hello");
}
}
public class DubboServiceImp2 implements DubboService {
@Override
public void sayHello() {
System.out.println("DubboServiceImp2...hello");
}
}
3、在META-INF/dubbo/下创建文件com.xyy.spi.dubbo.DubboService,内容
dubboService1=com.xyy.spi.dubbo.DubboServiceImp1
dubboService2=com.xyy.spi.dubbo.DubboServiceImp2
4、测试代码
@Test
public void testDubboService(){
ExtensionLoader<DubboService> extensionLoader1 = ExtensionLoader.getExtensionLoader(DubboService.class);
DubboService dubboService = extensionLoader1.getExtension("dubboService1");
dubboService.sayHello();
}
运行结果:
源码分析
入口:
ExtensionLoader.getExtensionLoader(DubboService.class);
看下 ExtensionLoader.getExtensionLoader方法:
@SuppressWarnings("unchecked")
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!");
}
//必须有@SPI注解
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
//从缓存中获取接口对应的ExtensionLoader,EXTENSION_LOADERS 是ConcurrentMap<Class<?>, ExtensionLoader<?>>
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
//创建ExtensionLoader,放入缓存
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
这个ExtensionLoader.withExtensionAnnotation方法,查看是否有SPI注解:
private static <T> boolean withExtensionAnnotation(Class<T> type) {
return type.isAnnotationPresent(SPI.class);
}
创建 new ExtensionLoader(type)方法:
private ExtensionLoader(Class<?> type) {
//接口服务的class
this.type = type;
//如果类型是ExtensionFactory.class 则设为null,否则就是再次使用SPI机制获得ExtensionFactory
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
下面我们看下获得具体扩展的方法 这个getExtension(String name):
public T getExtension(String name) {
//为空,则报异常
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
//如是字符串"true",则获取默认的扩展实例。
if ("true".equals(name)) {
return getDefaultExtension();
}
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
//创建指定的扩展实例
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
创建指定的扩展,createExtension方法:
private T createExtension(String name) {
//从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射关系表
//获得指定扩展的class对象
Class<?> 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);
}
//依赖注入和cachedWrapperClasses,后面分析
injectExtension(instance);
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}
获得所有的扩展getExtensionClasses()方法:
private Map<String, Class<?>> getExtensionClasses() {
//缓存中获取映射关系表
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//加载映射关系表
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
loadExtensionClasses()方法:
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));
}
// 获取@SPI注解中的值,并缓存起来
if (names.length == 1) cachedDefaultName = names[0];
}
}
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
//加载指定文件夹下的配置文件,META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
加载目录loadDirectory方法:
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) {
// fileName 是 META-INF/dubbo/com.xyy.spi.dubbo.DubboService
String fileName = dir + type.getName();
try {
Enumeration<java.net.URL> urls;
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
//加载文件,一个resourceURL就是一个文件
loadResource(extensionClasses, classLoader, resourceURL);
}
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", description file: " + fileName + ").", t);
}
}
loadResource方法加载资源:
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
try {
String line;
while ((line = reader.readLine()) != null) {
//去掉# 后面的注释
final int ci = line.indexOf('#');
if (ci >= 0) line = line.substring(0, ci);
line = line.trim();
if (line.length() > 0) {
try {
String name = null;
//包含等号,按等号切割
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
//加载类
loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
exceptions.put(line, e);
}
}
}
} finally {
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
loadClass方法加载class:
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
// clazz 必须是type类型的
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error when load extension class(interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + "is not subtype of interface.");
}
//判断clazz是否为标注了@Adaptive注解
if (clazz.isAnnotationPresent(Adaptive.class)) {
if (cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (!cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
}
//判断是否是Wrapper类型
else if (isWrapperClass(clazz)) {
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} else {
//clazz是一个普通的拓展类
//获得默认构造
clazz.getConstructor();
if (name == null || name.length() == 0) {
//name 配置为空,去类上找@Extension注解
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
//判断clazz是否为标注了@Activate注解
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
// 存储名称到class的映射关系,这样就解析好了一行
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
查看处理@Extension注解,findAnnotationName方法:
private String findAnnotationName(Class<?> clazz) {
com.alibaba.dubbo.common.Extension extension = clazz.getAnnotation(com.alibaba.dubbo.common.Extension.class);
//没注解,再去判断
if (extension == null) {
String name = clazz.getSimpleName();
//扩展类名是否以接口的名称结尾,包含的话,就截取扩展类前面的部分,不包含的话就直接扩展类名
if (name.endsWith(type.getSimpleName())) {
name = name.substring(0, name.length() - type.getSimpleName().length());
}
//转成小写
return name.toLowerCase();
}
//有注解,返回注解value
return extension.value();
}
源码分析流程图: