motan源码分析二:使用spi机制进行类加载
2024-08-27 05:52:58
在motan的源码中使用了很多的spi机制进行对象的创建,下面我们来具体分析一下它的实现方法。
1.在实际的jar包的\META-INF\services目录中引入相关的文件,例如下图中,我解压了core的jar文件后,获得到的相应文件列表:
2.以第一节中的ConfigHandler为例来分析,打开上图中的com.weibo.api.motan.config.handler.ConfigHandler文件,文件内容标识着ConfigHandler接口的实现类为:com.weibo.api.motan.config.handler.SimpleConfigHandler
#
# Copyright 2009-2016 Weibo, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# com.weibo.api.motan.config.handler.SimpleConfigHandler
3.在第一节中,创建ConfigHandler对象的代码是这样的:
ConfigHandler configHandler = ExtensionLoader.getExtensionLoader(ConfigHandler.class).getExtension(MotanConstants.DEFAULT_VALUE);
4.开始进入到实际的加载代码核心部分,首先来看一下类加载器的具体实现:
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
checkInterfaceType(type);//基础性检查 ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoaders.get(type);//之前是否已经加载过此加载器 if (loader == null) {
loader = initExtensionLoader(type);//第一次加载
}
return loader;
} private static <T> void checkInterfaceType(Class<T> clz) {
if (clz == null) {
failThrows(clz, "Error extension type is null");
} if (!clz.isInterface()) {
failThrows(clz, "Error extension type is not interface");
} if (!isSpiType(clz)) {
failThrows(clz, "Error extension type without @Spi annotation");
}
}
public static synchronized <T> ExtensionLoader<T> initExtensionLoader(Class<T> type) {
ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoaders.get(type); if (loader == null) {
loader = new ExtensionLoader<T>(type);//新创建一个加载器 extensionLoaders.putIfAbsent(type, loader); loader = (ExtensionLoader<T>) extensionLoaders.get(type);
} return loader;
}
5.下面我们将进入到加载器的内部,分析具体的实现:
private ExtensionLoader(Class<T> type) {
this(type, Thread.currentThread().getContextClassLoader());//使用当前线程的类加载器做为加载器,type为ConfigHandler接口
} public T getExtension(String name) {
checkInit();//检查是否初始化 if (name == null) {
return null;
} try {
Spi spi = type.getAnnotation(Spi.class); if (spi.scope() == Scope.SINGLETON) {
return getSingletonInstance(name);//返回唯一的对象
} else {
Class<T> clz = extensionClasses.get(name); if (clz == null) {
return null;
} return clz.newInstance();//重新创建对象
}
} catch (Exception e) {
failThrows(type, "Error when getExtension " + name, e);
} return null;
} private synchronized void loadExtensionClasses() {
if (init) {
return;
} extensionClasses = loadExtensionClasses(PREFIX);//加载相关的类
singletonInstances = new ConcurrentHashMap<String, T>(); init = true;
}
private ConcurrentMap<String, Class<T>> loadExtensionClasses(String prefix) {
String fullName = prefix + type.getName();//全名为:jar包名+\META-INF\services\com.weibo.api.motan.config.handler.ConfigHandler文件里的类
List<String> classNames = new ArrayList<String>(); try {
Enumeration<URL> urls;
if (classLoader == null) {
urls = ClassLoader.getSystemResources(fullName);
} else {
urls = classLoader.getResources(fullName);
} if (urls == null || !urls.hasMoreElements()) {
return new ConcurrentHashMap<String, Class<T>>();
}
System.out.println("fullname:"+fullName);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
System.out.println("url:"+url.getFile());
parseUrl(type, url, classNames);
}
} catch (Exception e) {
throw new MotanFrameworkException(
"ExtensionLoader loadExtensionClasses error, prefix: " + prefix + " type: " + type.getClass(), e);
}
for(String classN : classNames){
System.out.println("class:"+classN);
}
return loadClass(classNames);
}
6.在parseUrl方法中进行文件的内容读取,并在loadClass中完成类的加载
private void parseUrl(Class<T> type, URL url, List<String> classNames) throws ServiceConfigurationError {
InputStream inputStream = null;
BufferedReader reader = null;
try {
inputStream = url.openStream();
reader = new BufferedReader(new InputStreamReader(inputStream, MotanConstants.DEFAULT_CHARACTER)); String line = null;
int indexNumber = 0; while ((line = reader.readLine()) != null) {
indexNumber++;
parseLine(type, url, line, indexNumber, classNames);//读取到类的名称:com.weibo.api.motan.config.handler.SimpleConfigHandler
}
} catch (Exception x) {
failLog(type, "Error reading spi configuration file", x);
} finally {
try {
if (reader != null) {
reader.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (IOException y) {
failLog(type, "Error closing spi configuration file", y);
}
}
}
private ConcurrentMap<String, Class<T>> loadClass(List<String> classNames) {
ConcurrentMap<String, Class<T>> map = new ConcurrentHashMap<String, Class<T>>(); for (String className : classNames) {
try {
Class<T> clz;
if (classLoader == null) {
clz = (Class<T>) Class.forName(className);//装载类:com.weibo.api.motan.config.handler.SimpleConfigHandler
} else {
clz = (Class<T>) Class.forName(className, true, classLoader);
} checkExtensionType(clz); String spiName = getSpiName(clz); if (map.containsKey(spiName)) {
failThrows(clz, ":Error spiName already exist " + spiName);
} else {
map.put(spiName, clz);
}
} catch (Exception e) {
failLog(type, "Error load spi class", e);
}
} return map; }
motan类加载的知识点总结:
1.使用jdk的spi规范,在\META-INF\services中添加实际的使用类描述,从而实现类与类之间的完全解耦;
2.类加载器使用的是当前线程的类加载器;
3.motan的类加载器可以支持单例和多例两种模式;
4.motan中大量使用了spi的类加载方式。
最新文章
- sqlserver 中数据导入到mysql中的方法以及注意事项
- kali 渗透的一些笔记
- JS学习之事件流
- 在项目中那个少用if else 语句,精简代码,便于维护的方法(1)
- RxJava基本流程和lift源码分析
- 剑指offer—第三章高质量的代码(按顺序打印从1到n位十进制数)
- BZOJ2005: [Noi2010]能量采集 莫比乌斯反演的另一种方法——nlogn筛
- JavaScript高级程序设计20.pdf
- (转)Maven实战(二)构建简单Maven项目
- hdu 3333 Turing Tree
- C#读取Excel的其中一种方式OleDb读取(100万条)--快速大量插入SQL中
- spring依赖注入中接口的问题
- 在CSDN开通博客专栏后如何发布文章(图文)
- Aandroid 图片加载库Glide 实战(一),初始,加载进阶到实践
- Java使用Aspose组件进行多文档间的转换操作
- 【RSYSLOG】The Property Replacer【转】
- js 引擎 和 html 渲染引擎
- Myeclipse错误:Errors occurred during the build. Errors running builder &#39;DeploymentBuilder&#39; on project &#39;MyCastBoxAPP&#39; java.lang.NullPointerException
- Abp添加菜单
- Shell 和Python的区别。