SPI机制
SPI,即(service provider interface)机制,有很多组件的实现,如日志、数据库访问等都是采用这样的方式,一般通用组件为了提升可扩展性,基于接口编程,将操作接口形成标准规范,但是可以开放多种扩展实现,这种做法也符合开闭设计原则,使组件具有可插拨特性。不同的厂商或组织可以基于规范推出自己的实现,只需要在自己的jar包中通过配置文件和相应的实现类即可以实现扩展。甚至开发者自己也可以很方便对框架进行定制化实现。
JDK SPI介绍
JDK实现spi服务查找: ServiceLoader。
举个例子:
首先定义下示例接口
package com.example;
public interface Spi {
booleanisSupport(String name);
String sayHello();
}
ServiceLoader会遍历所有jar查找META-INF/services/com.example.Spi文件
A厂商提供实现
package com.a.example;
public class SpiAImpl implements Spi {
publicboolean isSupport(String name) {
return"SPIA".equalsIgnoreCase(name.trim());
}
public String syaHello() {
return “hello 我是厂商A”;
}
}
在A厂商提供的jar包中的META-INF/services/com.example.Spi文件内容为:
com.a.example.SpiAImpl #厂商A的spi实现全路径类名
B厂商提供实现
package com.b.example;
public class SpiBImpl implements Spi {
publicboolean isSupport(String name) {
return"SPIB".equalsIgnoreCase(name.trim());
}
public String syaHello() {
return “hello 我是厂商B”;
}
}
在B厂商提供的jar包中的META-INF/services/com.example.Spi文件内容为:
com.b.example.SpiBImpl #厂商B的spi实现全路径类名
ServiceLoader.load(Spi.class)读取厂商A、B提供jar包中的文件,ServiceLoader实现了Iterable接口可通过while for循环语句遍历出所有实现。
一个接口多种实现,就如策略模式一样提供了策略的实现,但是没有提供策略的选择, 使用方可以根据isSupport方法根据业务传入厂商名来选择具体的厂商。
public class SpiFactory {
//读取配置获取所有实现
privatestatic ServiceLoader spiLoader = ServiceLoader.load(Spi.class);
//根据名字选取对应实现
publicstatic Spi getSpi(String name) {
for(Spi spi : spiLoader) {
if(spi.isSupport(name) ) {
returnspi;
}
}
returnnull;
}
}
Duddo SPI
Dubbo 改进了 JDK 标准的 SPI 的以下问题:
- JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
- 如果扩展点加载失败,连扩展点的名称都拿不到了。
- 增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。
示例
在扩展类的jar包内,放置扩展点配置文件 META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。
以扩展 Dubbo 的协议为例,在协议的实现 jar 包内放置文本文件:META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol,内容为:
xxx=com.alibaba.xxx.XxxProtocol
实现类内容:
package com.alibaba.xxx;
import com.alibaba.dubbo.rpc.Protocol;
public class XxxProtocol implemenets Protocol {
// ...
}
ExtensionLoad
dubbo扩展机制的实现核心类是ExtensionLoad,几乎所有扩展实现都在这个类里面。每个可扩展接口的扩展实现类和实现实例的都管理通过是ExtensionLoad进行,每个接口维护一个单例的ExtensionLoad,所有可扩展接口的实现都维护在ExtensionLoad中,如下所示:
/**
* SPI 类和ExtensionLoader映射
*/
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
在单例模式中,最典型的实现就是通过私有构造方法实现的:
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
在dubbo扩展点实现过程中,有几个重要的特性需要提前了解一下:
扩展点自动包装
自动包装扩展点的 Wrapper 类。ExtensionLoader 在加载扩展点时,如果加载到的扩展点有拷贝构造函数,则判定为扩展点 Wrapper 类。
Wrapper类内容:
package com.alibaba.xxx;
import com.alibaba.dubbo.rpc.Protocol;
public class XxxProtocolWrapper implemenets Protocol {
Protocol impl;
public XxxProtocol(Protocol protocol) { impl = protocol; }
// 接口方法做一个操作后,再调用extension的方法
public void refer() {
//... 一些操作
impl.refer();
// ... 一些操作
}
// ...
}
Wrapper 类同样实现了扩展点接口,但是 Wrapper 不是扩展点的真正实现。它的用途主要是用于从 ExtensionLoader 返回扩展点时,包装在真正的扩展点实现外。即从 ExtensionLoader 中返回的实际上是 Wrapper 类的实例,Wrapper 持有了实际的扩展点实现类。这个是典型的装饰者模式,即真正的实现类是被包装在Wrapper之中,Wrapper类还做一些其它事情。
扩展点自动装配
加载扩展点时,自动注入依赖的扩展点。加载扩展点时,扩展点实现类的成员如果为其它扩展点类型,ExtensionLoader 在会自动注入依赖的扩展点。ExtensionLoader 通过扫描扩展点实现类的所有 setter 方法来判定其成员。即 ExtensionLoader 会执行扩展点的拼装操作。这个类似于Spring的IOC,后面会专门介绍。
扩展点自适应
在调用过程,自动选择一个扩展实现执行,一个扩展点只允许有一个自适应实现。dubbo通过@Adaptive注解标定自适应实现,这个注解可以在实现类上,也可以在方法上。比如ExtensionFactory的自适应实现就是通过在实现类AdaptiveExtensionFactory上加@Adaptive注解实现的:
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
...
}
如 Cluster就是通过在方法加@Adaptive实现的:
@SPI(FailoverCluster.NAME)
public interface Cluster {
/**
* Merge the directory invokers to a virtual invoker.
*
* @param <T>
* @param directory
* @return cluster invoker
* @throws RpcException
*/
@Adaptive
<T> Invoker<T> join(Directory<T> directory) throws RpcException;
}
这两种方式的自适应扩展类的实现方式也不同,在类上加注解是通过在实现上标识该类为自适应实现类,而在方法上加注解的,是通过动态代码生成自适应实现类。
扩展点自动激活
对于集合类扩展点,比如:Filter, InvokerListener, ExportListener, TelnetHandler, StatusChecker 等,可以同时加载多个实现,此时,可以用自动激活来简化配置
在ExtensionLoader中比较重要的公用方法就是这些:
- getExtensionLoader
- getAdaptiveExtension
- getActivateExtension
下面就详细剖析一下ExtensionLoader的实现流程。
获取ExtensionLoader流程
每个可扩展接口对应的ExtensionLoader都是单例,唯一获取ExtensionLoader对象的入口就是ExtensionLoader::getExtensionLoader方法,如主要流程图:
- 首先通过ExtensionLoader::getExtensionLoader
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null)
throw new IllegalArgumentException("Extension type == null");
if(!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
if(!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type +
") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
}
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
会校验尝试获取Loader的接口是否有@SPI注解,先在缓存中找,如果没有缓存,则调用私有构造方法:
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
这里有个重要的对象objectFactory,这个对象的作用就是自动装配依赖,也就是IOC,可以看出,除了ObjectFactory本身,所有扩展点都有ObjectFactory实例,这个也是通过SPI管理的,它是通过getAdaptiveExtension()方法获取,这就是后面要介绍自适应扩展实现,有关ObjectFactory的内容会在后面IOC中详细分析。
自适应扩展
我们从getAdaptiveExtension()方法切入,这个方法要完成的任务就是获取该扩展点的自适应实现实例,其流程如下图所示:
主要完成以下工作:
- 1.检查自适应缓存是否存在。
- 2.如果缓存未命中,则开始自适应例构建过程。
- 3.要构建自适应实例,先要有自适应的实现类,实现类有两种方式:一种通过配置文件,一种是通过是字节码的方式动态生成。
配置文件配置的自适应类通过在实现类上面加@Adaptive注解,如
.....
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
}
字节码生成的自适应实现类是在方法层面@Adaptive注解,如
@SPI("dubbo")
public interface Protocol {
....省略代码
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
}
- 4.优先加载配置文件,将自适应实现类缓存在cachedAdaptiveClass中,同时通过加载配置文件,也将激活实现缓存在cachedActivates之中,这个在后面的激活实现中有用到。将包装类实例缓存在cachedWrapperClasses。可以简单一窥加载配置文件的代码
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
String fileName = dir + type.getName();
try {
Enumeration<java.net.URL> urls;
ClassLoader classLoader = findClassLoader();
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL url = urls.nextElement();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
try {
String line = null;
while ((line = reader.readLine()) != null) {
final int ci = line.indexOf('#');
if (ci >= 0) line = line.substring(0, ci);
line = line.trim();
if (line.length() > 0) {
try {
String name = null;
int i = line.indexOf('=');
if (i > 0) {
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
}
if (line.length() > 0) {
Class<?> clazz = Class.forName(line, true, classLoader);
//配置的实现必须实现该接口
if (! type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error when load extension class(interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + "is not subtype of interface.");
}
if (clazz.isAnnotationPresent(Adaptive.class)) {
//如果是自适应实现
if(cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (! cachedAdaptiveClass.equals(clazz)) {
//只允许有一个自适应实现类
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
} else {
//如果不是自适应类
try {
//判断是不是包装类,即是否有接口的构造方法
clazz.getConstructor(type);
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} catch (NoSuchMethodException e) {
//不是包装类
clazz.getConstructor();
if (name == null || name.length() == 0) {
//找到Extension注解
name = findAnnotationName(clazz);
if (name == null || name.length() == 0) {
//如果Extension注解没有默认名称,则根据类的名称关系判断
if (clazz.getSimpleName().length() > type.getSimpleName().length()
&& clazz.getSimpleName().endsWith(type.getSimpleName())) {
//如果实现类和接口有名称上关系,比如XXImpl则将后面的作为实现类标识
name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();
} else {
throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url);
}
}
}
String[] names = NAME_SEPARATOR.split(name);
if (names != null && names.length > 0) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
if (! cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz) {
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
}
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t);
exceptions.put(line, e);
}
}
} // end of while read lines
} finally {
reader.close();
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", class file: " + url + ") in " + url, t);
}
} // end of while urls
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " +
type + ", description file: " + fileName + ").", t);
}
}
- 5.加载完配置文件后,检查缓存的自适应实现类,若没有,则通过字节码技术生成自适应类。调用createAdaptiveExtensionClass()方法,实际上是通过拼接class文本,然后通过compiler编译文本生成class,这里又是一个自适应的类AdaptiveCompiler。
private Class<?> getAdaptiveExtensionClass() {
//先通过配置文化加载实现类,并且识别自适应实现类
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
//如果没有通过Adaptive注解标识的自适应实现类,则通过字节码创建
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
....省略代码
private Class<?> createAdaptiveExtensionClass() {
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
@Adaptive
public class AdaptiveCompiler implements Compiler {
private static volatile String DEFAULT_COMPILER;
public static void setDefaultCompiler(String compiler) {
DEFAULT_COMPILER = compiler;
}
public Class<?> compile(String code, ClassLoader classLoader) {
Compiler compiler;
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
String name = DEFAULT_COMPILER; // copy reference
if (name != null && name.length() > 0) {
compiler = loader.getExtension(name);
} else {
compiler = loader.getDefaultExtension();
}
return compiler.compile(code, classLoader);
}
}
下面是通过字节码动态生成的Protocol接口的自适应扩展Protocol$Adpative:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
public void destroy() {throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
if (arg0 == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
public void destroyServer() {
throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroyServer() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
}
自动激活扩展
在dubbo中,某些组件可以同时有多个实现同时加载时,就可以通过@Activate注解自动激活,常见的自动激活扩展,如过滤器Filter,有顺序要求,提供了三个排序属性,before、after和order。还有一些过滤条件,主要是通过分组和key,如Provider和consumer的过滤逻辑可能就不一样,Activate的源码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
/**
* Group过滤条件。
*/
String[] group() default {};
String[] value() default {};
/**
* 排序信息,可以不提供。
*/
String[] before() default {};
/**
* 排序信息,可以不提供。
*/
String[] after() default {};
/**
* 排序信息,可以不提供。
*/
int order() default 0;
}
在ExtensionLoader中,有三个重载获取激活扩展实现的方法:
- public List<T> getActivateExtension(URL url, String[] values, String group)
- public List<T> getActivateExtension(URL url, String[] values)
- public List<T> getActivateExtension(URL url, String key, String group)
后面两个也是通过调用第一个重载方法实现,下面来分析一下它的源码:
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> exts = new ArrayList<T>();
List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
getExtensionClasses();
for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Activate activate = entry.getValue();
if (isMatchGroup(group, activate.group())) {
T ext = getExtension(name);
if (! names.contains(name)
&& ! names.contains(Constants.REMOVE_VALUE_PREFIX + name)
&& isActive(activate, url)) {
exts.add(ext);
}
}
}
Collections.sort(exts, ActivateComparator.COMPARATOR);
}
List<T> usrs = new ArrayList<T>();
for (int i = 0; i < names.size(); i ++) {
String name = names.get(i);
if (! name.startsWith(Constants.REMOVE_VALUE_PREFIX)
&& ! names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
if (Constants.DEFAULT_KEY.equals(name)) {
if (usrs.size() > 0) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
T ext = getExtension(name);
usrs.add(ext);
}
}
}
if (usrs.size() > 0) {
exts.addAll(usrs);
}
return exts;
}
这个方法所做的工作无非就是在之前加载配置时缓存的cachedActivates中过滤查询符合条件的自动激动实例,并根据@Activate注解中配置的排序规则排序。
IOC注入
在创建自适应实例时,都会调用ExtensionLoader的injectExtension方法:
private T createAdaptiveExtension() {
try {
//传入自适应实例注入到ExtensionLoader
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extenstion " + type + ", cause: " + e.getMessage(), e);
}
}
private T injectExtension(T instance) {
try {
//必须要有对象工厂
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
Class<?> pt = method.getParameterTypes()[0];
try {
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} 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;
}
然后我们看到了ExtensionFactory对象,dubbo中的IOC实例是通过ExtensionFactory实现的,其实就是检测扩展实现类有没有通过set方法设置的属性,如果有,就通过ExtensionFactory加载而设置。
ExtensionFactory的类实现体系:
在构造ExtensionLoader对象时,有个对象extensionFactory是必须要创建的,可以看到它就是用自适应实例,而ExtensionFatocry的自适应实例便是AdaptiveExtensionFactory,通过下面它的源码,我们可以发现,它维护了其他非自适应扩展实例,其实也就两个SpiExtensionFactory和SpringExtensionFactory。尝试用这两个实例去加载,加载到便返回。
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
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);
}
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;
}
}
ExtensionFatocry 可以理解为对象工厂,只不过这里的对应就是Dubbo中的扩展Extension,AdaptiveExtensionFactory可以理解为通用扩展实现获取的入口,至于具体的获取方式分为两种,如果一种是通过Dubbo 自己的SPI方式加载到的扩展,同时还支持复用Srping 的方式,可以看看这两种实现的代码便可知:
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;
}
}
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")
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;
}
}
总结
作为贯穿整个Dubbo设计始终的思想,SPI在整个框架中随处可见,本文围绕ExtensionLoader扩展点机制,通过一些dubbo组件扩展示例,分析了其核心源码和流程。希望可以对于理解Dubbo的扩展点乃至dubbo源码解析过程中有所帮助,最后总结几点:
- 对于每个扩展点,只维护一个ExtensionLoad,具体扩展实现类和实例,都是通过相应的ExtensionLoader获取的。
- 针对每个扩展实现的实例都是单例的,所以在扩展实现时应保证线程安全。
- 自适应实现只能有一个,自适应实现类获取有两种方式,一种是通过配置文件,这种就是针对@Adaptive注解在类级别的时,而@Adaptive注解在方法级别时,自适应实现类就需要通过字符码动态生成。
- 自动激活扩展实现可以有多个,一般情况下,采用自动激动方式扩展的一般都会有多个,正因为有多个,自动激动扩展实现可能有顺序性,且可以分组。
参考
http://dubbo.apache.org/books...
https://blog.csdn.net/jdluoji...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。