dubbo源码分析4——SPI机制_ExtensionFactory类的作用
ExtensionFactory的源码:
@SPI
public interface ExtensionFactory { /**
* Get extension.
*
* @param type object type.
* @param name object name.
* @return object instance.
*/
<T> T getExtension(Class<T> type, String name); }
ExtensionFactory的作用就类似spring框架中的IOC的作用,正是因为JDK的SPI机制比较简单,所以duboo框架才重写了SPI机制,并实现了IOC和AOP的功能。本篇先介绍它的IOC的功能,根据上篇的分析可知,IOC功能的代码出现在ExtensionLoader的 injectExtension方法里面,我们就先来分析这个方法,先列出两段源码:
触发调用injectExtension方法的源码:
private T createExtension(String name) {
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, (T) clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
injectExtension(instance); //完成IOC的依赖注入
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}
injectExtension方法的源码:
private T injectExtension(T instance) { //instanceSPI实现类的实例对象 try {
if (objectFactory != null) {
//获取instance的所有方法,
for (Method method : instance.getClass().getMethods()) {
//找出满足三要素条件的方法(1.set为前缀的 2.方法参数为1个的 3.是public的方法)
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
Class<?> pt = method.getParameterTypes()[0]; //获取要 set的类型
try {
//取出要 set的属性名
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
Object object = objectFactory.getExtension(pt, property); //调用ExtensionFactory的getExtension方法获取要set的对象
//此时我们就可以将ExtensionFactory看作容器,判断这个要set的属性在容器中是否存在
if (object != null) {
method.invoke(instance, object); //执行set方法,完成一个属性的注入
}
} catch (Exception e) {
logger.error("fail to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
上面的源码通过注释已经把injectExtension 方法分析的很清楚了,下面只需要再分析下AdaptiveExtensionFactory类的源码就可以了,原因上篇文章也写明了,因为所有的SPI类(除ExtensionFactory之外)对应的ExtensionLoader实例的objectFactory属性的类型都是AdaptiveExtensionFactory类
AdaptiveExtensionFactory类的构造方法分析:
根据 loadFile()方法的缓存原则,AdaptiveExtensionFactory实例中的factories的size返回应为2,里面只会保存这两个类实例:
spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
因为adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory是保存在cachedAdaptiveClass上的
private final List<ExtensionFactory> factories; public AdaptiveExtensionFactory() {
ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
for (String name : loader.getSupportedExtensions()) {
list.add(loader.getExtension(name));
}
factories = Collections.unmodifiableList(list);
}
这样我们只要分析清楚AdaptiveExtensionFactory类的getExtension方法,就可以明白这个IOC容器是如何取出需要的SPI实例依赖了
public <T> T getExtension(Class<T> type, String name) {
for (ExtensionFactory factory : factories) {
T extension = factory.getExtension(type, name);
if (extension != null) {
return extension;
}
}
return null;
}
从上述代码上看其实就去调用下面这两个类的getExtension(type, name)方法
com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
SpringExtensionFactory的源码:
public class SpringExtensionFactory implements ExtensionFactory { private static final Set<ApplicationContext> contexts = new ConcurrentHashSet<ApplicationContext>(); public static void addApplicationContext(ApplicationContext context) {
contexts.add(context);
} public static void removeApplicationContext(ApplicationContext context) {
contexts.remove(context);
} @SuppressWarnings("unchecked")
//这个比较容易理解,就是从spring的容器中去获取
public <T> T getExtension(Class<T> type, String name) {
for (ApplicationContext context : contexts) {
if (context.containsBean(name)) {
Object bean = context.getBean(name);
if (type.isInstance(bean)) {
return (T) bean;
}
}
}
return null;
} }
SpiExtensionFactory的源码 :
看了这个方法真是觉的挺绕的,明明先是ExtensionLoader在注入时找自己的 objectFactory ( 即ExtensionFactory)来帮忙,结果objectFactory的这个实现,又把锅甩回给了loader.getAdaptiveExtension()方法,由于这个getAdaptiveExtension就又回到了之前讲过的一个调用过程,这里再列一下:
getAdaptiveExtension()内部的调用过程如下(注意这是一个实例方法):
-> if(cachedAdaptiveInstance.get() == null){ createAdaptiveExtension() }
->getAdaptiveExtensionClass() //下面的调用有两个分支
// 分支1
->getExtensionClasses()
->loadExtensionClasses()
->loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
injectExtension //完成注入,这是 ExtensionFactory 类的作用之所在
// 分支2
->createAdaptiveExtensionClass()
injectExtension
public class SpiExtensionFactory implements ExtensionFactory { public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
if (loader.getSupportedExtensions().size() > 0) {
return loader.getAdaptiveExtension();
}
}
return null;
} }
总结:
通过跟踪源码,以目前的这几个实现类而言,SpiExtensionFactory其实啥都没干,最后还是loader.getAdaptiveExtension()方法在负责IOC,SpiExtensionFactory只可以理解成是一个门面类的作用。
dubbo源码分析4——SPI机制_ExtensionFactory类的作用的更多相关文章
- dubbo源码分析1——SPI机制的概要介绍
插件机制是Dubbo用于可插拔地扩展底层的一些实现而定制的一套机制,比如dubbo底层的RPC协议.注册中心的注册方式等等.具体的实现方式是参照了JDK的SPI思想,由于JDK的SPI的机制比较简单, ...
- dubbo源码分析5——SPI机制_AdaptiveExtension的原理和作用
private T createAdaptiveExtension() { try { return injectExtension((T) getAdaptiveExtensionClass().n ...
- dubbo源码分析2——SPI机制中的SPI实现类的读取和预处理
SPI机制中的SPI实现类的读取和预处理是由ExtensionLoader类的loadFile方法来完成的 loadFile方法的作用是读取dubbo的某个SPI接口的spi描述文件,然后进行缓存,缓 ...
- Dubbo源码分析之 SPI(一)
一.概述 dubbo SPI 在dubbo的作用是基础性的,要想分析研究dubbo的实现原理.dubbo源码,都绕不过 dubbo SPI,掌握dubbo SPI 是征服dubbo的必经之路. 本篇文 ...
- Dubbo源码分析之SPI(二)
一.概述 本篇文章是dubbo SPI源码分析的第二篇,接着第一篇继续分析dubbo SPI的内容,我们主要介绍 getDefaultExtension() 获取默认扩展点方法. 由于此方法比较简单, ...
- dubbo源码分析6——SPI机制中的AOP
在 ExtensionLoader 类的loadFile方法中有下图的这段代码: 类如现在这个ExtensionLoader中的type 是Protocol.class,也就是SPI接口的实现类中Xx ...
- dubbo源码分析3——SPI机制中的ExtensionLoader类的objectFactory属性分析
ExtensionLoader类是整个SPI的核心类,每个SPI都会对应一个ExtensionLoader类实例,这个类的构造方法如下: private ExtensionLoader(Class&l ...
- Dubbo源码分析之SPI(三)
一.概述 本篇介绍自适应扩展,方法getAdaptiveExtension()的实现.ExtensionLoader类本身很多功能也使用到了自适应扩展.包括ExtensionFactory扩展. 通俗 ...
- dubbo源码分析01:SPI机制
一.什么是SPI SPI全称为Service Provider Interface,是一种服务发现机制,其本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件.这样可以在运行时,动态为 ...
随机推荐
- OS + Linux RedHat 6.3 QA
s 问题1:could not open session 解决办法 https://blog.csdn.net/qq_40809549/article/details/82658720 解决1: 配置 ...
- scrapy_splash模块解析动态js
一般遇到动态加载的网页就比较棘手,一般采用scrapy_splash和selenium这两种方式来解决.貌似scrapy_splash更强大,因为就从爬取美团这个网站而言,scrapy_splash可 ...
- python 函数动态参数,名称空间,global,nonlocal
##################################总结######################################动态参数 *args:位置参数动态传参,接收到的是元 ...
- a标签与js的冲突
如上图,需要做一个页面,点击左边的标题,右边就显示左边标题下的子标题的集合, html代码如下: <div id="newleft"> <ul> <l ...
- 【1】Java中double转BigDecimal的注意事项
项目遇到该问题 先上结论:不要直接用double变量作为构造BigDecimal的参数. 线上有这么一段Java代码逻辑: 1,接口传来一个JSON串,里面有个数字:57.3. 2,解析JSON并把这 ...
- [Android] Android : lambda expressions are not supported at this language level(需设置project language level)
最近在Github上下载一个别人的开源项目 ,里面用到了Javajdk1.8的新特性:Lambda 表达式.而我用的Android studio发现不能用这个Lambda 表达式. 本地环境: And ...
- Webpack2学习记录-2
这篇在 webpack-demo 目前下新建一个 w2 目录,学习 webpack.config.js 及 与 npm scripts 的使用. 1.w2 下新建一个 webpack.config.j ...
- Javaweb学习笔记——(十八)——————事务、DBCP、C3P0、装饰者模式
事务 什么是事务? 转账: 1.给张三账户减1000元 2.给李四账户加1000元 当给张三账户减1000元之后,抛出了异常,这 ...
- asp.net上传图片,上传图片
想必很多人工作中经常需要实现上传图片的功能. 先引用此插件 http://files.cnblogs.com/files/hmYao/jquery-form.js. 前台代码 <form dat ...
- php 随机生成数字字母组合
直接上代码: function getRandomString($len, $chars=null) { if (is_null($chars)) { $chars = "abcdefghi ...