motan源碼分析二:使用spi機制進行類加載

motan源碼分析二:使用spi機制進行類加載

在motan的源碼中使用了很多的spi機制進行對象的創建,下面我們來具體分析一下它的實現方法。

1.在實際的jar包的\META-INF\services目錄中引入相關的文件,例如下圖中,我解壓了core的jar文件後,獲得到的相應文件列表:

motan源碼分析二:使用spi機制進行類加載

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  ExtensionLoader getExtensionLoader(Class type) {
checkInterfaceType(type);//基礎性檢查
ExtensionLoader
loader = (ExtensionLoader) extensionLoaders.get(type);//之前是否已經加載過此加載器
if (loader == null) {
loader = initExtensionLoader(type);//第一次加載
}
return loader;
}
private static void checkInterfaceType(Class 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 ExtensionLoader initExtensionLoader(Class type) {
ExtensionLoader loader = (ExtensionLoader) extensionLoaders.get(type);
if (loader == null) {
loader = new ExtensionLoader(type);//新創建一個加載器
extensionLoaders.putIfAbsent(type, loader);
loader = (ExtensionLoader) extensionLoaders.get(type);
}
return loader;
}

5.下面我們將進入到加載器的內部,分析具體的實現:

private ExtensionLoader(Class 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 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 void checkInit() {
if (!init) {
loadExtensionClasses();
}
}
private synchronized void loadExtensionClasses() {
if (init) {
return;
}
extensionClasses = loadExtensionClasses(PREFIX);//加載相關的類

singletonInstances = new ConcurrentHashMap<string>();
init = true;
}
private ConcurrentMap<string>> 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 urls;
if (classLoader == null) {
urls = ClassLoader.getSystemResources(fullName);
} else {
urls = classLoader.getResources(fullName);
}
if (urls == null || !urls.hasMoreElements()) {
return new ConcurrentHashMap<string>>();
}
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
parseUrl(type, url, classNames);
}
} catch (Exception e) {
throw new MotanFrameworkException(
"ExtensionLoader loadExtensionClasses error, prefix: " + prefix + " type: " + type.getClass(), e);
}
return loadClass(classNames);
}
/<string>
/<string>/<string>/<string>/<string>

6.在parseUrl方法中進行文件的內容讀取,並在loadClass中完成類的加載

private void parseUrl(Class 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>> loadClass(List<string> classNames) {
ConcurrentMap<string>> map = new ConcurrentHashMap<string>>();
for (String className : classNames) {
try {
Class clz;
if (classLoader == null) {
clz = (Class) Class.forName(className);//裝載類:com.weibo.api.motan.config.handler.SimpleConfigHandler
} else {
clz = (Class) 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;
}
/<string>/<string>/<string>/<string>/<string>

motan類加載的知識點總結:

1.使用jdk的spi規範,在\META-INF\services中添加實際的使用類描述,從而實現類與類之間的完全解耦;

2.類加載器使用的是當前線程的類加載器;

3.motan的類加載器可以支持單例和多例兩種模式;

4.motan中大量使用了spi的類加載方式。


分享到:


相關文章: