{
return new ServiceLoader<>(service, loader);
}
public static ServiceLoader load(Class service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
private ServiceLoader(Class svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, “Service interface cannot be null”);
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}
…
}
我们一起来分析下这个服务加载器的工作流程,首先通过ServiceLoader.load()
进行加载。先获取当前线程绑定的 ClassLoader
,如果当前线程绑定的 ClassLoader
为null
,则使用 SystemClassLoader
进行代替,而后清除一下provider
缓存,最后创建一个 LazyIterator
。 LazyIterator
的部分源码如下:
private class LazyIterator implements Iterator
{
Class service;
ClassLoader loader;
Enumeration configs = null;
Iterator pending = null;
String nextName = null;
…
public boolean hasNext() {
if (acc == null) {
return hasNextService();
} else {
PrivilegedAction action = new PrivilegedAction() {
public Boolean run() { return hasNextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
…
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
//key:获取完全限定名
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;
}
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
}
…
}
key
:通过预定好的目录地址以及类名来指定类的具体地址,类加载器根据这个地址来加载具体的实现类。
大致的SPI加载过程如下所示:
===============================================================================
Seata
是一个分布式事务的框架,具体的使用这里不再赘述,有时间可以出专门写它的文章。本节主要关注Seata是如何利用SPI的方式进行框架能力扩展的。
在Seata
框架中使用 EnhancedServiceLoader
实现服务载入,通过名称我们可以知道他是一种增强型的ServiceLoader
。那么相对于JDK自身的ServiceLoader
,他到底强在哪里呢?
由下图可知, EnhancedServiceLoader
不仅支持Java
原生的服务发现目录,同样支持自己自定义的META-INF/seata/
目录。
另外在具体接口实现类上都有@LoadLevel的注解,如果其中有多个配置中心实现类都被加载,那么可以根据对应注解上的属性order
进行排序。将实际优先级最大的类进行加载。
我们都知道注册中心是微服务体系中的必不可少的基础组件,它记录了服务提供者的地址信息。那么在Seata
中,Seata
的客户端如事务管理器TM
、资源管理器RM需要与事务协调者TC
进行通信,那么就需要通过注册中心来获取服务端的地址信息。Seata
注册中心支持多个第三方注册中心,如Consul
、Apollo
、Etcd3
等。我们来看下Seata
是怎么使用SPI机制来实现对于多个注册中心扩展支持的。
首先定义一个ConfigurationProvider
的接口,你看是不是嗅到了熟悉的味道,只要使用SPI
那么就需要首先把规矩给小弟们定好。
最后
由于篇幅有限,这里就不一一罗列了,20道常见面试题(含答案)+21条MySQL性能调优经验小编已整理成Word文档或PDF文档
还有更多面试复习笔记分享如下
需要首先把规矩给小弟们定好。
最后
由于篇幅有限,这里就不一一罗列了,20道常见面试题(含答案)+21条MySQL性能调优经验小编已整理成Word文档或PDF文档
[外链图片转存中…(img-N65CTZ5r-1714551496940)]
还有更多面试复习笔记分享如下
[外链图片转存中…(img-EfTh6drj-1714551496940)]