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

  

motan源码分析二:使用spi机制进行类加载的更多相关文章

  1. dubbo源码分析01:SPI机制

    一.什么是SPI SPI全称为Service Provider Interface,是一种服务发现机制,其本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件.这样可以在运行时,动态为 ...

  2. Solr4.8.0源码分析(19)之缓存机制(二)

    Solr4.8.0源码分析(19)之缓存机制(二) 前文<Solr4.8.0源码分析(18)之缓存机制(一)>介绍了Solr缓存的生命周期,重点介绍了Solr缓存的warn过程.本节将更深 ...

  3. 十、Spring之BeanFactory源码分析(二)

    Spring之BeanFactory源码分析(二) 前言 在前面我们简单的分析了BeanFactory的结构,ListableBeanFactory,HierarchicalBeanFactory,A ...

  4. 多线程之美8一 AbstractQueuedSynchronizer源码分析<二>

    目录 AQS的源码分析 该篇主要分析AQS的ConditionObject,是AQS的内部类,实现等待通知机制. 1.条件队列 条件队列与AQS中的同步队列有所不同,结构图如下: 两者区别: 1.链表 ...

  5. Java ArrayList源码分析(含扩容机制等重点问题分析)

    写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...

  6. Fresco 源码分析(二) Fresco客户端与服务端交互(1) 解决遗留的Q1问题

    4.2 Fresco客户端与服务端的交互(一) 解决Q1问题 从这篇博客开始,我们开始讨论客户端与服务端是如何交互的,这个交互的入口,我们从Q1问题入手(博客按照这样的问题入手,是因为当时我也是从这里 ...

  7. Solr4.8.0源码分析(18)之缓存机制(一)

    Solr4.8.0源码分析(18)之缓存机制(一) 前文在介绍commit的时候具体介绍了getSearcher()的实现,并提到了Solr的预热warn.那么本文开始将详细来学习下Solr的缓存机制 ...

  8. 框架-springmvc源码分析(二)

    框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...

  9. java-通过 HashMap、HashSet 的源码分析其 Hash 存储机制

    通过 HashMap.HashSet 的源码分析其 Hash 存储机制 集合和引用 就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并非真正的把 Java 对象放入数组中.仅仅是把对象的 ...

随机推荐

  1. Linq编程101例

    原文地址:101 LINQ Samples in C# Part1 - Restriction Operators Part2 - Projection Operators Part3 - Parti ...

  2. Content-Disposition的使用和注意事项

    转载:http://www.cnblogs.com/jzaileen/articles/1281025.html 最近不少Web技术圈内的朋友在讨论协议方面的事情,有的说web开发者应该熟悉web相关 ...

  3. TSQL Challenge 2

    和之前发布的TSQL Challenge 1是同一系列的文章,看到那篇学习哪篇,没有固定的顺序,只为锻炼下思维. Compare rows in the same table and group th ...

  4. 解决 asp.net 伪静态 IIS设置后 直正HTML无法显示的问题

    asp.net+UrlRewriter来实现网站伪静态,实现伪静态有一些好处,比如对于搜索引擎更好收录页面,还有一个好处就是隐藏了真实地址的参数,所以有独立服务器的朋友,配置IIS实现伪静态功能,挺不 ...

  5. 模块化的JavaScript开发的优势在哪里

    如今模块化的 JavaScript 的开发越来越火热,无论是模块加载器还是优秀的 JavaScript 模块,都是层出不穷.既然这么火,肯定是有存在的理由,肯定是解决了某些实际问题.很多没接触过模块化 ...

  6. PhotoShop 移动工具详解

    自动选择工具 勾选后  可以随意移动任意图层     不勾选  只适用于移动当前所选图层 Ctrl+Z 还原移动Ctrl+Alt+Z 后退一步 复制图像 Alt键+拖动 Shift+Alt+拖动   ...

  7. Bug: freetype/fterrors.h: No such file or directory

    Bug描述: 安装PIL过程中出现题目中的错误信息,具体如下:

  8. weekly review

    鉴于某位昔日工作在我身边的大师一直在写review,所以为了能靠近大师,我也要开始写review了. 无名师曾经说过,想要成为大师的话,要先找到一个大师,然后追随大师,再然后与大师通行,之后成为大师, ...

  9. 基类中定义的虚函数在派生类中重新定义时,其函数原型,包括返回类型、函数名、参数个数、参数类型及参数的先后顺序,都必须与基类中的原型完全相同 but------> 可以返回派生类对象的引用或指针

      您查询的关键词是:c++primer习题15.25 以下是该网页在北京时间 2016年07月15日 02:57:08 的快照: 如果打开速度慢,可以尝试快速版:如果想更新或删除快照,可以投诉快照. ...

  10. [转]解读ASP.NET 5 & MVC6系列(7):依赖注入

    本文转自:http://www.cnblogs.com/TomXu/p/4496440.html 在前面的章节(Middleware章节)中,我们提到了依赖注入功能(Dependency Inject ...