一、common下的loader
1、SPI (EnhancedServiceLoader)
1)、测试
@Test
public void testLoadByClassAndClassLoader() {
// 实际就是通过反射,创建对象
Hello load = EnhancedServiceLoader.load(Hello.class, Hello.class.getClassLoader());
Assertions.assertEquals(load.say(), "Olá.");
}
2)、主流程
private static class InnerEnhancedServiceLoader<S> {
private static final Logger LOGGER = LoggerFactory.getLogger(InnerEnhancedServiceLoader.class);
private static final String SERVICES_DIRECTORY = "META-INF/services/";
private static final String SEATA_DIRECTORY = "META-INF/seata/";
// 下面的a流程会挨个给这些对象添加值
private static final ConcurrentMap<Class<?>, InnerEnhancedServiceLoader<?>> SERVICE_LOADERS =
new ConcurrentHashMap<>();
private final Class<S> type;
private final Holder<List<ExtensionDefinition>> definitionsHolder = new Holder<>();
private final ConcurrentMap<ExtensionDefinition, Holder<Object>> definitionToInstanceMap =
new ConcurrentHashMap<>();
private final ConcurrentMap<String, List<ExtensionDefinition>> nameToDefinitionsMap = new ConcurrentHashMap<>();
private final ConcurrentMap<Class<?>, ExtensionDefinition> classToDefinitionMap = new ConcurrentHashMap<>();
private InnerEnhancedServiceLoader(Class<S> type) {
this.type = type;
}
/**
* Get the ServiceLoader for the specified Class
*
* @param type the type of the extension point
* @param <S> the type
* @return the service loader
*/
private static <S> InnerEnhancedServiceLoader<S> getServiceLoader(Class<S> type) {
if (type == null) {
throw new IllegalArgumentException("Enhanced Service type == null");
}
// 添加<Hello.class, new InnerEnhancedServiceLoader<>(Hello.class)> 到 SERVICE_LOADERS
// computeIfAbsent 方法似乎只是将 map 的 computeIfAbsent 方法变成了静态的,并调换了下判断顺序
return (InnerEnhancedServiceLoader<S>)CollectionUtils.computeIfAbsent(SERVICE_LOADERS, type,
key -> new InnerEnhancedServiceLoader<>(type));
}
/**
* Specify classLoader to load the service provider
*
* @param loader the loader
* @return s s
* @throws EnhancedServiceNotFoundException the enhanced service not found exception
*/
// 很多种load,这里只看这一种,其他可以类比这个
private S load(ClassLoader loader) throws EnhancedServiceNotFoundException {
return loadExtension(loader, null, null);
}
// 也有几种,这里只看这一种,其他可以类比这个
@SuppressWarnings("rawtypes")
private S loadExtension(ClassLoader loader, Class[] argTypes,
Object[] args) {
try {
// a、初始化上面的那些对象,第一次 debug,结果如下
loadAllExtensionClass(loader);
// b、获取 definitionsHolder 中的 ExtensionDefinition 集合中的最后一个
ExtensionDefinition defaultExtensionDefinition = getDefaultExtensionDefinition();
// c、通过反射实例化上步得到的 defaultExtensionDefinition 里的 serviceClass
// 第二次 debug,结果如下
return getExtensionInstance(defaultExtensionDefinition, loader, argTypes, args);
} catch (Throwable e) {
if (e instanceof EnhancedServiceNotFoundException) {
throw (EnhancedServiceNotFoundException)e;
} else {
throw new EnhancedServiceNotFoundException(
"not found service provider for : " + type.getName() + " caused by " + ExceptionUtils
.getFullStackTrace(e));
}
}
}
}
3)、分解主流程
a、初始化数据
private final Holder<List<ExtensionDefinition>> definitionsHolder = new Holder<>();
private List<Class> loadAllExtensionClass(ClassLoader loader) {
List<ExtensionDefinition> definitions = definitionsHolder.get();
if (definitions == null) {
synchronized (definitionsHolder) {
definitions = definitionsHolder.get();
if (definitions == null) {
// 初始化
definitions = findAllExtensionDefinition(loader);
definitionsHolder.set(definitions);
}
}
}
return definitions.stream().map(def -> def.getServiceClass()).collect(Collectors.toList());
}
/**
* Helper Class for hold a value.
* @param <T>
*/
private static class Holder<T> {
private volatile T value;
private void set(T value) {
this.value = value;
}
private T get() {
return value;
}
}
private static final String SERVICES_DIRECTORY = "META-INF/services/";
private static final String SEATA_DIRECTORY = "META-INF/seata/";
@SuppressWarnings("rawtypes")
private List<ExtensionDefinition> findAllExtensionDefinition(ClassLoader loader) {
List<ExtensionDefinition> extensionDefinitions = new ArrayList<>();
try {
// 看第一个loadFile,下面的可以类比
loadFile(SERVICES_DIRECTORY, loader, extensionDefinitions);
loadFile(SEATA_DIRECTORY, loader, extensionDefinitions);
} catch (IOException e) {
throw new EnhancedServiceNotFoundException(e);
}
//After loaded all the extensions,sort the caches by order
if (!nameToDefinitionsMap.isEmpty()) {
for (List<ExtensionDefinition> definitions : nameToDefinitionsMap.values()) {
Collections.sort(definitions, (def1, def2) -> {
int o1 = def1.getOrder();
int o2 = def2.getOrder();
return Integer.compare(o1, o2);
});
}
}
if (!extensionDefinitions.isEmpty()) {
Collections.sort(extensionDefinitions, (definition1, definition2) -> {
int o1 = definition1.getOrder();
int o2 = definition2.getOrder();
return Integer.compare(o1, o2);
});
}
return extensionDefinitions;
}
@SuppressWarnings("rawtypes")
private void loadFile(String dir, ClassLoader loader, List<ExtensionDefinition> extensions)
throws IOException {
// fileName = META-INF/services/io.seata.common.loader.Hello
String fileName = dir + type.getName();
Enumeration<java.net.URL> urls;
if (loader != null) {
urls = loader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL url = urls.nextElement();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), Constants.DEFAULT_CHARSET))) {
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 {
// 创建ExtensionDefinition
ExtensionDefinition extensionDefinition = getUnloadedExtensionDefinition(line, loader);
if (extensionDefinition == null) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("The same extension {} has already been loaded, skipped", line);
}
continue;
}
extensions.add(extensionDefinition);
} catch (LinkageError | ClassNotFoundException e) {
LOGGER.warn("Load [{}] class fail. {}", line, e.getMessage());
}
}
}
} catch (Throwable e) {
LOGGER.warn("load clazz instance error: {}", e.getMessage());
}
}
}
}
private final ConcurrentMap<String, List<ExtensionDefinition>> nameToDefinitionsMap = new ConcurrentHashMap<>();
private final ConcurrentMap<Class<?>, ExtensionDefinition> classToDefinitionMap = new ConcurrentHashMap<>();
private ExtensionDefinition getUnloadedExtensionDefinition(String className, ClassLoader loader)
throws ClassNotFoundException {
//Check whether the definition has been loaded
if (!isDefinitionContainsClazz(className, loader)) {
Class<?> clazz = Class.forName(className, true, loader);
String serviceName = null;
Integer priority = 0;
Scope scope = Scope.SINGLETON;
// 读 Hello 上的 LoadLevel 注解
LoadLevel loadLevel = clazz.getAnnotation(LoadLevel.class);
if (loadLevel != null) {
serviceName = loadLevel.name();
priority = loadLevel.order();
scope = loadLevel.scope();
}
ExtensionDefinition result = new ExtensionDefinition(serviceName, priority, scope, clazz);
classToDefinitionMap.put(clazz, result);
if (serviceName != null) {
CollectionUtils.computeIfAbsent(nameToDefinitionsMap, serviceName, e -> new ArrayList<>())
.add(result);
}
return result;
}
return null;
}
b、
private ExtensionDefinition getDefaultExtensionDefinition() {
List<ExtensionDefinition> currentDefinitions = definitionsHolder.get();
return CollectionUtils.getLast(currentDefinitions);
}
c、通过反射实例化
private S getExtensionInstance(ExtensionDefinition definition, ClassLoader loader, Class[] argTypes,
Object[] args) {
if (definition == null) {
throw new EnhancedServiceNotFoundException("not found service provider for : " + type.getName());
}
// LoadLevel 注解的 scope 字段,是否为单例
if (Scope.SINGLETON.equals(definition.getScope())) {
Holder<Object> holder = CollectionUtils.computeIfAbsent(definitionToInstanceMap, definition,
key -> new Holder<>());
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createNewExtension(definition, loader, argTypes, args);
holder.set(instance);
}
}
}
return (S)instance;
} else {
return createNewExtension(definition, loader, argTypes, args);
}
}
4)、debug
二、影响
比如注册中心,有 nacos、zk 等代码,只需配置下类似下面的文件,就可以加载对应的代码。