编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

motan源码分析二:使用spi机制进行类加载

wxchong 2024-08-18 00:39:05 开源技术 9 ℃ 0 评论

在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 void checkInit() {
 if (!init) {
 loadExtensionClasses();
 }
}
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>>();
 }
 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);
}

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的类加载方式。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表