dubbo的SPI(Service Provider Interface)是在java的SPI基础上做了功能特性的扩展;但本质上都是做了和Spring IOC\AOP一样的事,服务(或者bean)的注册统一管理、以及实例化;此外spi提供的是一种jvm级别的服务发现机制,我们只需按照spi的要求,在jar包中进行适当的配置,jvm就会在运行时通过懒加载,帮我们找到所需要的服务并加载。如果我们一直不适用这个服务,那么他就不会被加载,一定程度上避免了资源浪费;
SPI怎么用到我们的业务服务的注册解析管理上?dubbo存在大量的扩展点,业务代码也存在大量扩展点,so.........(此处应该有总结//todo)
1、JDK的SPI扩展机制
使用上就只需要注意下面几点:
-
虽然没有强制要求使用功能interface或者abstract class作为spi的注册服务,但是作为可扩展服务,尽量养成使用interface或者abstract class的习惯;
- 必须放在JAR包或项目的指定路径,即 META-INF/services 下;
- 必须以服务的全限定名命名配置文件,比如本例中,配置文件必须命名为 cn.jinlu.spi.demo.Animal,java会根据此名进行服务查找;
- 内容必须是一个实现类的全限定名,如果要注册多个实现类,按行分割。注释以#开头。;
源码解析部分:
(1)、代码入口:通过ServiceLoader的静态方法load(Class<?> service)进行服务加载
ServiceLoader的SPI服务注册发现机制
public static void main(String[] args) {
String content = "";
// ContentExtractorAware为需要被注册、服务的服务接口,通过静态方法ServiceLoader#load进行加载
ServiceLoader<ContentExtractorAware> loader = ServiceLoader.load(ContentExtractorAware.class);
System.out.println("list all the implements sub class to invoke abstract method:\n");
// 遍历服务的实现
Iterator iterator = loader.iterator();
while (iterator.hasNext()) {
ContentExtractorAware aware = (ContentExtractorAware) iterator.next();
System.out.println(aware.extract(content));
}
}
(2)、ServiceLoader构造
默认会使用当前线程的上下文class loader构造ServiceLoader,ServiceLoader通过实现Iterable<?>接口,使得在构造完ServiceLoader后,ServiceLoader实例并不会立刻扫描当前进程中的服务实例,而是实例化创建了一个LazyIterator懒加载迭代器,只有在实际遍历使用时再扫描所有jar包找到对应的服务。懒加载迭代器被保存在一个内部成员lookupIterator中(核心理念);
ServiceLoader
/**
* 静态方法构建ServiceLoader实例
*
* @param service
* @param <S>
* @return
*/
public static <S> ServiceLoader<S> load(Class<S> service) {
// 当前线程上下文class loader
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
/**
* 构建ServiceLoader实例
*
* @param service
* @param loader
* @param <S>
* @return
*/
public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) {
return new ServiceLoader<>(service, loader);
}
/**
* 私有构造方法,必须通过ServiceLoader.load(Class<?>)静态方法来创建ServiceLoader实例
*
* @param svc
* @param cl
*/
private ServiceLoader(Class<S> svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, "Service interface cannot be null");
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}
/**
* 重新load指定service的实现。通过LazyIterator实现懒加载。
*/
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}
(3)、服务的加载和遍历
a、ServiceLoader迭代器:ServiceLoader通过实现Iterable<?>接口,将服务的实现的实例化延后到遍历是执行
- 未进行迭代时,不对jar包做扫描(即不实例化服务实现类)
- 首次迭代时,Serviceloader.providers中没有任何缓存,总是会通过LazyIterator进行加载,并将service实现的全限定名与加载的service实例作为key-value缓存到ServiceLoader.providers中
- 之后再进行迭代时,直接从ServiceLoader.providers中获取
public final class ServiceLoader<S> implements Iterable<S> { .... // 缓存的service provider,按照初始化顺序排列。 private LinkedHashMap<String, S> providers = new LinkedHashMap<>(); // 懒加载迭代器被保存在一个内部成员lookupIterator中 // 当前的LazyIterator迭代器指针,服务懒加载迭代器 private ServiceLoader.LazyIterator lookupIterator; /** * 创建ServiceLoader迭代器,隐藏了LazyIterator的实现细节 * * @return */ public Iterator<S> iterator() { return new Iterator<S>() { // 创建Iterator迭代器时的ServiceLoader.providers快照, // 因此在首次迭代时,iterator总是会通过LazyIterator进行懒加载 Iterator<Map.Entry<String, S>> knownProviders = providers.entrySet().iterator(); @Override public boolean hasNext() { // 如果已经扫描过,则对providers进行迭代; if (knownProviders.hasNext()) return true; // 如果没有扫描过,则通过lookupIterator进行扫描和懒加载 return lookupIterator.hasNext(); } @Override public S next() { // 如果已经扫描过,则对providers进行迭代; if (knownProviders.hasNext()) return knownProviders.next().getValue(); // 如果没有扫描过,则通过lookupIterator进行扫描和懒加载 return (S) lookupIterator.next(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } ... }
b、内部私有LazyIterator懒加载迭代器,主要实现功能:
- 首次迭代时,通过ClassLoader.getResources(String)获得指定services文件的URL集合
-
如果是首次遍历懒加载器,或者对上一个URL内容解析获得的service实现类集合完成了迭代,则从configs中取下一个services文件URL进行解析,按行获得具体的service实现类集合,并进行迭代。
- 当前URL中解析得到的实现类集合进行迭代,每次返回一个service实现类。
/**
* 懒加载迭代器
*/
private class LazyIterator implements Iterator<S> {
// 服务
Class<S> service;
// jvm资源加载器
ClassLoader loader;
// 存放服务全限定名对应资源集合
Enumeration<URL> configs = null;
// 当前service配置文件的内容迭代器
// 即对services进行遍历,取出一个services配置文件,再对该文件按行解析,
// 每行代表一个具体的service实现类,pending是某个services配置文件中service实现类的迭代器
Iterator<String> pending = null;
// 下一个服务实现类名称
String nextName = null;
private LazyIterator(Class<S> service, ClassLoader loader) {
this.service = service;
this.loader = loader;
}
@Override
public boolean hasNext() {
if (acc == null) {
return hasNextService();
} else {
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return hasNextService();
}
};
return AccessController.doPrivileged(action, acc);
}
}
// 首次迭代时,configs为空,尝试通过classloader获取名为:
// "META-INF/services/[服务全限定名]"的所有配置文件
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
// 注意fullName的定义:"META-INF/services/[服务全限定名]"
String fullName = PREFIX + service.getName();
if (loader == null)
// 通过ClassLoader.getResources()获得资源URL集合
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
// 如果pending为空,或者pending已经迭代到迭代器末尾,则尝试解析下一个services配置文件
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
pending = parse(service, configs.nextElement());
}
// 对当前pending内容进行遍历,每一项代表services的一个实现类
nextName = pending.next();
return true;
}
@Override
public S next() {
if (acc == null) {
return nextService();
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() {
return nextService();
}
};
return AccessController.doPrivileged(action, acc);
}
}
private S nextService() {
if (!hasNextService()){
throw new NoSuchElementException();
}
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service,"Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
fail(service,"Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,"Provider " + cn + " could not be instantiated",x);
}
// This cannot happen
throw new Error();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
java的spi最典型的应用就是jdbc的驱动加载,jdbc4.0之前JDBC的开始,总是需要通过Class.forName显式实例化驱动,否则将找不到对应DB的驱动。但是JDBC4.0开始,这个显式的初始化不再是必选项了,它存在的意义只是为了向上兼容。
为什么JDBC4.0之后不需要了呢?答案就在下面的代码中。在系统启动时,DriverManager静态初始化时会通过ServiceLoader对所有jar包中被注册为 java.sql.Driver 服务的驱动实现类进行初始化,这样就避免了上面通过Class.forName手动初始化的繁琐工作。
public class DriverManager {
// JDBC驱动注册中心,所有加载的JDBC驱动都注册在该CopyOnWriteArrayList中
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
...
/* Prevent the DriverManager class from being instantiated. */
private DriverManager(){}
/**
* Load the initial JDBC drivers by checking the System property
* jdbc.properties and then use the {@code ServiceLoader} mechanism
*/
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
private static void loadInitialDrivers() {
// 如果通过jdbc.drivers配置了驱动,则在本方法最后进行实例化
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
// If the driver is packaged as a Service Provider, load it.
// Get all the drivers through the classloader
// exposed as a java.sql.Driver.class service.
// ServiceLoader.load() replaces the sun.misc.Providers()
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// 通过ServiceLoader加载所有通过SPI方式注册的"java.sql.Driver"服务
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
// 遍历ServiceLoader实例进行强制实例化,因此除了遍历不做任何其他操作
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
}
);
println("DriverManager.initialize: jdbc.drivers = " + drivers);
// 强制加载"jdbc.driver"环境变量中配置的DB驱动
if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}
...
}
以mySql驱动为例看看驱动实例化时做了什么:
package com.mysql.jdbc;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
//
// Register ourselves with the DriverManager
//
static {
try {
// 向DriverManager注册自己
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
/**
* Construct a new driver and register it with DriverManager
*
* @throws SQLException
* if a database error occurs.
*/
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
}
}
因此,只要某个驱动以这种方式被引用并被上下文class loader加载,那么该驱动就会通过SPI的方式被自动发现和加载。实际使用时,Driver.getDriver(url)会通过DB连接url获取到正确的驱动并建立与DB的连接。
2、Dubbo的SPI扩展机制
Dubbo SPI对JDK SPI增强而来,它的增强点,Dubbo 改进了 JDK 标准的 SPI 的以下问题:
-
JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
- 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。
- 增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。
Dubbo SPI 在 dubbo-common
的 extension
包实现,如下图所示:
1、ExtensionLoader属性
com.alibaba.dubbo.common.extension.ExtensionLoader 拓展加载器。这是 Dubbo SPI 的核心。它的属性有:
@SuppressWarnings("deprecation")
public class ExtensionLoader<T> {
private static final Logger logger =
LoggerFactory.getLogger(ExtensionLoader.class);
private static final String SERVICES_DIRECTORY = "META-INF/services/";
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");
// ============================== 静态属性 ==============================
/**
* 拓展加载器集合
*
* key:拓展接口
*/
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
/**
* 拓展实现类集合
*
* key:拓展实现类
* value:拓展对象。
*
* 例如,key 为 Class<AccessLogFilter>
* value 为 AccessLogFilter 对象
*/
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
// ============================== 对象属性 ==============================
/**
* 拓展接口。
* 例如,Protocol
*/
private final Class<?> type;
/**
* 对象工厂
*
* 用于调用 {@link #injectExtension(Object)} 方法,向拓展对象注入依赖的属性。
*
* 例如,StubProxyFactoryWrapper 中有 `Protocol protocol` 属性。
*/
private final ExtensionFactory objectFactory;
/**
* 缓存的拓展名与拓展类的映射。
*
* 和 {@link #cachedClasses} 的 KV 对调。
*
* 通过 {@link #loadExtensionClasses} 加载
*/
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
/**
* 缓存的拓展实现类集合。
*
* 不包含如下两种类型:
* 1. 自适应拓展实现类。例如 AdaptiveExtensionFactory
* 2. 带唯一参数为拓展接口的构造方法的实现类,或者说拓展 Wrapper 实现类。例如,ProtocolFilterWrapper 。
* 拓展 Wrapper 实现类,会添加到 {@link #cachedWrapperClasses} 中
*
* 通过 {@link #loadExtensionClasses} 加载
*/
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
/**
* 拓展名与 @Activate 的映射
*
* 例如,AccessLogFilter。
*
* 用于 {@link #getActivateExtension(URL, String)}
*/
private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
/**
* 缓存的拓展对象集合
*
* key:拓展名
* value:拓展对象
*
* 例如,Protocol 拓展
* key:dubbo value:DubboProtocol
* key:injvm value:InjvmProtocol
*
* 通过 {@link #loadExtensionClasses} 加载
*/
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
/**
* 缓存的自适应( Adaptive )拓展对象
*/
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
/**
* 缓存的自适应拓展对象的类
*
* {@link #getAdaptiveExtensionClass()}
*/
private volatile Class<?> cachedAdaptiveClass = null;
/**
* 缓存的默认拓展名
*
* 通过 {@link SPI} 注解获得
*/
private String cachedDefaultName;
/**
* 创建 {@link #cachedAdaptiveInstance} 时发生的异常。
*
* 发生异常后,不再创建,参见 {@link #createAdaptiveExtension()}
*/
private volatile Throwable createAdaptiveInstanceError;
/**
* 拓展 Wrapper 实现类集合
*
* 带唯一参数为拓展接口的构造方法的实现类
*
* 通过 {@link #loadExtensionClasses} 加载
*/
private Set<Class<?>> cachedWrapperClasses;
/**
* 拓展名 与 加载对应拓展类发生的异常 的 映射
*
* key:拓展名
* value:异常
*
* 在 {@link #loadFile(Map, String)} 时,记录
*/
private Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();
- 在
META-INF/dubbo/internal/
和META-INF/dubbo/
目录下,放置 接口全限定名 配置文件,每行内容为:拓展名=拓展实现类全限定名。 META-INF/dubbo/internal/
目录下,从名字上可以看出,用于 Dubbo 内部提供的拓展实现。下图是一个例子:
- META-INF/dubbo/internal/ 例子
META-INF/dubbo/
目录下,用于用户自定义的拓展实现。META-INF/service/
目录下,Java SPI 的配置目录。我们会看到 Dubbo SPI 对 Java SPI 做了兼容。
NAME_SEPARATOR
,拓展名分隔符,使用逗号。- 我们将属性分成了两类:1)静态属性;2)对象属性。这是为啥呢?
- 【静态属性】一方面,ExtensionLoader 是 ExtensionLoader 的管理容器。一个拓展( 拓展接口 )对应一个 ExtensionLoader 对象。例如,Protocol 和 Filter 分别对应一个 ExtensionLoader 对象。
- 【对象属性】另一方面,一个拓展通过其 ExtensionLoader 对象,加载它的拓展实现们。我们会发现多个属性都是 “cached“ 开头。ExtensionLoader 考虑到性能和资源的优化,读取拓展配置后,会首先进行缓存。等到 Dubbo 代码真正用到对应的拓展实现时,进行拓展实现的对象的初始化。并且,初始化完成后,也会进行缓存。也就是说:
- 缓存加载的拓展配置
- 缓存创建的拓展实现对象
2、ExtensionLoader获得拓展配置
(1)、getExtensionClasses #getExtensionClasses()
方法,获得拓展实现类数组。
/**
* 获得拓展实现类数组
*
* @return 拓展实现类数组
*/
private Map<String, Class<?>> getExtensionClasses() {
// 从缓存中,获得拓展实现类数组
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
// 从配置文件中,加载拓展实现类数组
classes = loadExtensionClasses();
// 设置到缓存中
cachedClasses.set(classes);
}
}
}
return classes;
}
cachedClasses
属性,缓存的拓展实现类集合。它不包含如下两种类型的拓展实现:
- 自适应拓展实现类。例如 AdaptiveExtensionFactory 。拓展 Adaptive 实现类,会添加到
cachedAdaptiveClass
属性中。 - 带唯一参数为拓展接口的构造方法的实现类,或者说拓展 Wrapper 实现类。例如,ProtocolFilterWrapper 。拓展 Wrapper 实现类,会添加到
cachedWrapperClasses
属性中。 - 总结来说,
cachedClasses
+cachedAdaptiveClass
+cachedWrapperClasses
才是完整缓存的拓展实现类的配置。
获取扩展实现类的整体流程如下:
- cachedClasses.get() 从缓存中,获得拓展实现类数组。
- 当缓存不存在时,调用
#loadExtensionClasses()
方法,从配置文件中,加载拓展实现类数组。 - 设置加载的实现类数组,到缓存中。
(2)、loadExtensionClasses #loadExtensionClasses()
方法,从多个配置文件中,加载拓展实现类数组。
/**
* 加载拓展实现类数组
*
* 无需声明 synchronized ,因为唯一调用该方法的 {@link #getExtensionClasses()} 已经声明。
* // synchronized in getExtensionClasses
*
* @return 拓展实现类数组
*/
private Map<String, Class<?>> loadExtensionClasses() {
// 通过 @SPI 注解,获得默认的拓展实现类名
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if (names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if (names.length == 1) cachedDefaultName = names[0];
}
}
// 从配置文件中,加载拓展实现类数组
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
- 首先通过
@SPI
注解,获得拓展接口对应的默认的拓展实现类名。 - 之后通过logFile从配置文件中加载扩展实现类数组,注意此处配置文件的加载顺序;
(3)loadFile(extensionClasses, dir)
方法,从一个配置文件中,加载拓展实现类数组。代码如下:
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;
while ((line = reader.readLine()) != null) {
// 跳过当前被注释掉的情况,例如 #spring=xxxxxxxxx
final int ci = line.indexOf('#');
if (ci >= 0) line = line.substring(0, ci);
line = line.trim();
if (line.length() > 0) {
try {
// 拆分,key=value 的配置格式
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.");
}
// 缓存自适应拓展对象的类到 `cachedAdaptiveClass`
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 {
// 缓存拓展 Wrapper 实现类到 `cachedWrapperClasses`
try {
clazz.getConstructor(type);
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
// 缓存拓展实现类到 `extensionClasses`
} catch (NoSuchMethodException e) {
clazz.getConstructor();
// 未配置拓展名,自动生成。例如,DemoFilter 为 demo 。主要用于兼容 Java SPI 的配置。
if (name == null || name.length() == 0) {
name = findAnnotationName(clazz);
if (name == null || name.length() == 0) {
if (clazz.getSimpleName().length() > type.getSimpleName().length()
&& clazz.getSimpleName().endsWith(type.getSimpleName())) {
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 到 `cachedActivates` 。
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(names[0], activate);
}
for (String n : names) {
// 缓存到 `cachedNames`
if (!cachedNames.containsKey(clazz)) {
cachedNames.put(clazz, n);
}
// 缓存拓展实现类到 `extensionClasses`
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);
}
}
- 获得完整的文件名( 相对路径 )。例如:
"META-INF/dubbo/internal/com.alibaba.dubbo.common.extension.ExtensionFactory"
。 - 获得文件名对应的所有文件 URL 数组。例如:
ExtensionFactory
- 逐个文件 URL 遍历,逐行遍历。跳过当前被
"#"
注释掉的情况,例如#spring=xxxxxxxxx
。 - 按照
key=value
的配置拆分。其中name
为拓展名,line
为拓展实现类名。注意,上文我们提到过 Dubbo SPI 会兼容 Java SPI 的配置格式,那么按照此处的解析方式,name
会为空。这种情况下,拓展名会自动生成。 - 判断拓展实现类,需要实现拓展接口。
- 缓存自适应拓展对象的类到
cachedAdaptiveClass
属性。在@Adaptive 详细解析。 - 缓存拓展 Wrapper 实现类到
cachedWrapperClasses
属性。 a、调用Class#getConstructor(Class<?>... parameterTypes)
方法,通过反射的方式,参数为拓展接口,判断当前配置的拓展实现类为拓展 Wrapper 实现类。若成功(未抛出异常),则代表符合条件。例如,ProtocolFilterWrapper(Protocol protocol) 这个构造方法。 - 若获得构造方法失败,则代表是普通的拓展实现类,缓存到
extensionClasses
变量中。 - 调用
Class#getConstructor(Class<?>... parameterTypes)
方法,获得参数为空的构造方法。 - 未配置拓展名,自动生成。适用于 Java SPI 的配置方式。例如,xxx.yyy.DemoFilter 生成的拓展名为
demo
。- 通过
@Extension
注解的方式设置拓展名的方式已经废弃,胖友可以无视该方法。
- 通过
- 获得拓展名。使用逗号进行分割,即多个拓展名可以对应同一个拓展实现类。
- 缓存
@Activate
到cachedActivates
。 - 缓存到
cachedNames
属性。 - 缓存拓展实现类到
extensionClasses
变量。注意,相同拓展名,不能对应多个不同的拓展实现。 - 若发生异常,记录到异常集合
exceptions
属性。
2、获得拓展加载器
在 Dubbo 的代码里,常常能看到如下的代码:
ExtensionLoader.getExtensionLoader(Class<T> type){
// ...
}
(1)、 getExtensionLoader
#getExtensionLoader(type)
静态方法,根据拓展点的接口,获得拓展加载器(必须是接口)。代码如下:
/**
* 根据拓展点的接口,获得拓展加载器
*
* @param type 接口
* @param <T> 泛型
* @return 加载器
*/
@SuppressWarnings("unchecked")
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!");
}
// 必须包含 @SPI 注解
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;
}
- 调用 withExtensionAnnotation方法,校验必须使用
@SPI
注解标记。 - 从
EXTENSION_LOADERS
静态中获取拓展接口对应的 ExtensionLoader 对象。若不存在,则创建 ExtensionLoader 对象,并添加到EXTENSION_LOADERS
。
(2)、构造方法
构造方法,代码如下:
/**
* 拓展接口。
* 例如,Protocol
*/
private final Class<?> type;
/**
* 对象工厂
*
* 用于调用 {@link #injectExtension(Object)} 方法,向拓展对象注入依赖的属性。
*
* 例如,StubProxyFactoryWrapper 中有 `Protocol protocol` 属性。
*/
private final ExtensionFactory objectFactory;
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
objectFactory
属性,对象工厂,功能上和 Spring IOC 一致。
3、 获得指定拓展对象
在 Dubbo 的代码里,常常能看到如下的代码:
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name)
#getExtension()
方法,返回指定名字的扩展对象。如果指定名字的扩展不存在,则抛异常 IllegalStateException 。代码如下:
/**
* Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException}
* will be thrown.
*/
/**
* 返回指定名字的扩展对象。如果指定名字的扩展不存在,则抛异常 {@link IllegalStateException}.
*
* @param name 拓展名
* @return 拓展对象
*/
@SuppressWarnings("unchecked")
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
// 查找 默认的 拓展对象
if ("true".equals(name)) {
return getDefaultExtension();
}
// 从 缓存中 获得对应的拓展对象
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
// 从 缓存中 未获取到,进行创建缓存对象。
if (instance == null) {
instance = createExtension(name);
// 设置创建对象到缓存中
holder.set(instance);
}
}
}
return (T) instance;
}
- 调用
#getDefaultExtension()
方法,查询默认的拓展对象。在该方法的实现代码中,简化代码为getExtension(cachedDefaultName);
。 - 从缓存中,获得拓展对象。
- 当缓存不存在时,调用
#createExtension(name)
方法,创建拓展对象。 - 添加创建的拓展对象,到缓存中。
(2)、createExtension #createExtension(name)
方法,创建拓展名的拓展对象,并缓存。代码如下:
/**
* 创建拓展名的拓展对象,并缓存。
*
* @param name 拓展名
* @return 拓展对象
*/
@SuppressWarnings("unchecked")
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, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 注入依赖的属性
injectExtension(instance);
// 创建 Wrapper 拓展对象
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
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);
}
}
- 获得拓展名对应的拓展实现类。若不存在,调用
#findException(name)
方法,抛出异常。 - 从缓存
EXTENSION_INSTANCES
静态属性中,获得拓展对象。 - 当缓存不存在时,创建拓展对象,并添加到
EXTENSION_INSTANCES
中。因为#getExtension(name)
方法中已经加synchronized
修饰,所以此处不用同步。 - 调用
#injectExtension(instance)
方法,向创建的拓展注入其依赖的属性。 -
创建 Wrapper 拓展对象,将
instance
包装在其中。在 《Dubbo 开发指南 —— 扩展点加载》 文章中,如此介绍 Wrapper 类:Wrapper 类同样实现了扩展点接口,但是 Wrapper 不是扩展点的真正实现。它的用途主要是用于从 ExtensionLoader 返回扩展点时,包装在真正的扩展点实现外。即从 ExtensionLoader 中返回的实际上是 Wrapper 类的实例,Wrapper 持有了实际的扩展点实现类。
扩展点的 Wrapper 类可以有多个,也可以根据需要新增。
通过 Wrapper 类可以把所有扩展点公共逻辑移至 Wrapper 中。新加的 Wrapper 在所有的扩展点上添加了逻辑,有些类似 AOP,即 Wrapper 代理了扩展点。
- 例如:ListenerExporterWrapper、ProtocolFilterWrapper 。
(3)、injectExtension #injectExtension(instance)
方法,注入依赖的属性。代码如下:
/**
* 注入依赖的属性
*
* @param instance 拓展对象
* @return 拓展对象
*/
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())) { // setting && public 方法
// 获得属性的类型
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;
}
- 必须有
objectFactory
属性,即 ExtensionFactory 的拓展对象,不需要注入依赖的属性。 - 反射获得所有的方法,仅仅处理
public setting
方法。 - 获得属性的类型。获得属性名。获得属性值。注意,此处虽然调用的是
ExtensionFactory#getExtension(type, name)
方法,实际获取的不仅仅是拓展对象,也可以是 Spring Bean 对象。设置属性值。
4、获得自适应的拓展对象
在 Dubbo 的代码里,常常能看到如下的代码:
ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
友情提示,先看下 Adaptive 的内容,在回到此处。getAdaptiveExtension
(1)、getAdaptiveExtension #getAdaptiveExtension()
方法,获得自适应拓展对象。
/**
* 获得自适应拓展对象
*
* @return 拓展对象
*/
@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {
// 从缓存中,获得自适应拓展对象
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
// 若之前未创建报错,
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
// 创建自适应拓展对象
instance = createAdaptiveExtension();
// 设置到缓存
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
// 记录异常
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
// 若之前创建报错,则抛出异常 IllegalStateException
} else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
- 从缓存
cachedAdaptiveInstance
属性中,获得自适应拓展对象。若之前创建报错,则抛出异常 IllegalStateException 。 - 当缓存不存在时,调用
#createAdaptiveExtension()
方法,创建自适应拓展对象,并添加到cachedAdaptiveInstance
中。 - 若创建发生异常,记录异常到
createAdaptiveInstanceError
,并抛出异常IllegalStateException 。
(2)、createAdaptiveExtension #createAdaptiveExtension()
方法,创建自适应拓展对象。代码如下:
/**
* 创建自适应拓展对象
*
* @return 拓展对象
*/
@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
- 调用
#getAdaptiveExtensionClass()
方法,获得自适应拓展类。 - 调用
Class#newInstance()
方法,创建自适应拓展对象。 - 调用
#injectExtension(instance)
方法,向创建的自适应拓展对象,注入依赖的属性。
(3)、 getAdaptiveExtensionClass #getAdaptiveExtensionClass()
方法,获得自适应拓展类。代码如下:
/**
* @return 自适应拓展类
*/
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
- 【
@Adaptive
的第一种】:若cachedAdaptiveClass
已存在,直接返回。的第一种情况。 - 【
@Adaptive
的第二种】:调用#createAdaptiveExtensionClass()
方法,自动生成自适应拓展的代码实现,并编译后返回该类。
(4)createAdaptiveExtensionClassCode #createAdaptiveExtensionClassCode()
方法,自动生成自适应拓展的代码实现,并编译后返回该类。
/**
* 自动生成自适应拓展的代码实现,并编译后返回该类。
*
* @return 类
*/
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);
}
-
调用 #createAdaptiveExtensionClassCode 方法,自动生成自适应拓展的代码实现的字符串; 代码比较简单,已经添加详细注释 如下是 ProxyFactory 的自适应拓展的代码实现的字符串生成例子;
- 使用 Dubbo SPI 加载 Compier 拓展接口对应的拓展实现对象,后调用
Compiler#compile(code, classLoader)
方法,进行编译。🙂 因为不是本文的重点,后续另开文章分享。
5、 获得激活的拓展对象数组
在 Dubbo 的代码里,看到使用代码如下:
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
(1)、getExtensionLoader #getExtensionLoader(url, key, group)
方法,获得符合自动激活条件的拓展对象数组。
/**
* This is equivalent to {@code getActivateExtension(url, url.getParameter(key).split(","), null)}
*
* 获得符合自动激活条件的拓展对象数组
*
* @param url url
* @param key url parameter key which used to get extension point names
* Dubbo URL 参数名
* @param group group
* 过滤分组名
* @return extension list which are activated.
* @see #getActivateExtension(com.alibaba.dubbo.common.URL, String[], String)
*/
public List<T> getActivateExtension(URL url, String key, String group) {
// 从 Dubbo URL 获得参数值
String value = url.getParameter(key);
// 获得符合自动激活条件的拓展对象数组
return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
}
/**
* Get activate extensions.
*
* 获得符合自动激活条件的拓展对象数组
*
* @param url url
* @param values extension point names
* @param group group
* @return extension list which are activated
* @see com.alibaba.dubbo.common.extension.Activate
*/
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);
// 处理自动激活的拓展对象们
// 判断不存在配置 `"-name"` 。例如,<dubbo:service filter="-default" /> ,代表移除所有默认过滤器。
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) // 判断是否配置移除。例如 <dubbo:service filter="-monitor" />,则 MonitorFilter 会被移除
&& isActive(activate, url)) { // 判断是否激活
exts.add(ext);
}
}
}
// 排序
Collections.sort(exts, ActivateComparator.COMPARATOR);
}
// 处理自定义配置的拓展对象们。例如在 <dubbo:service filter="demo" /> ,代表需要加入 DemoFilter (这个是笔者自定义的)。
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)) { // 判断非移除的
// 将配置的自定义在自动激活的拓展对象们前面。例如,<dubbo:service filter="demo,default,demo2" /> ,则 DemoFilter 就会放在默认的过滤器前面。
if (Constants.DEFAULT_KEY.equals(name)) {
if (!usrs.isEmpty()) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
// 获得拓展对象
T ext = getExtension(name);
usrs.add(ext);
}
}
}
// 添加到结果集
if (!usrs.isEmpty()) {
exts.addAll(usrs);
}
return exts;
}
- 从 Dubbo URL 获得参数值。例如说,若 XML 配置 Service
<dubbo:service filter="demo, demo2" />
,并且在获得 Filter 自动激活拓展时,此处就能解析到value=demo,demo2
。另外,value
可以根据逗号拆分。 - 调用
#getActivateExtension(url, values, group)
方法,获得符合自动激活条件的拓展对象数组。 - 处理自动激活的拓展对象们。
- #isMatchGroup(group, groups) 方法,匹配分组。
- #isActive(Activate, url) 方法,是否激活,通过 Dubbo URL 中是否存在参数名为 `@Activate.value` ,并且参数值非空。
- 处理自定义配置的拓展对象们。
- 将
usrs
合并到exts
尾部。
(2)、ActivateComparator
- com.alibaba.dubbo.common.extension.support.ActivateComparator , 自动激活拓展对象排序器。
- 代码比较简单,直接看注释。
3、 @SPI
com.alibaba.dubbo.common.extension.@SPI,扩展点接口的标识。代码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {
/**
* default extension name
*/
String value() default "";
}
-
value
,默认拓展实现类的名字。例如,Protocol 拓展接口,代码如下:@SPI("dubbo") public interface Protocol { // ... 省略代码 }
- 其中
"dubbo"
指的是 DubboProtocol ,Protocol 默认的拓展实现类。
- 其中
4、 @Adaptive
com.alibaba.dubbo.common.extension.@Adaptive ,自适应拓展信息的标记。代码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
/**
* Decide which target extension to be injected. The name of the target extension is decided by the parameter passed
* in the URL, and the parameter names are given by this method.
* <p>
* If the specified parameters are not found from {@link URL}, then the default extension will be used for
* dependency injection (specified in its interface's {@link SPI}).
* <p>
* For examples, given <code>String[] {"key1", "key2"}</code>:
* <ol>
* <li>find parameter 'key1' in URL, use its value as the extension's name</li>
* <li>try 'key2' for extension's name if 'key1' is not found (or its value is empty) in URL</li>
* <li>use default extension if 'key2' doesn't appear either</li>
* <li>otherwise, throw {@link IllegalStateException}</li>
* </ol>
* If default extension's name is not give on interface's {@link SPI}, then a name is generated from interface's
* class name with the rule: divide classname from capital char into several parts, and separate the parts with
* dot '.', for example: for {@code com.alibaba.dubbo.xxx.YyyInvokerWrapper}, its default name is
* <code>String[] {"yyy.invoker.wrapper"}</code>. This name will be used to search for parameter from URL.
*
* @return parameter key names in URL
*/
/**
* 从 {@link URL }的 Key 名,对应的 Value 作为要 Adapt 成的 Extension 名。
* <p>
* 如果 {@link URL} 这些 Key 都没有 Value ,使用 缺省的扩展(在接口的{@link SPI}中设定的值)。<br>
* 比如,<code>String[] {"key1", "key2"}</code>,表示
* <ol>
* <li>先在URL上找key1的Value作为要Adapt成的Extension名;
* <li>key1没有Value,则使用key2的Value作为要Adapt成的Extension名。
* <li>key2没有Value,使用缺省的扩展。
* <li>如果没有设定缺省扩展,则方法调用会抛出{@link IllegalStateException}。
* </ol>
* <p>
* 如果不设置则缺省使用Extension接口类名的点分隔小写字串。<br>
* 即对于Extension接口 {@code com.alibaba.dubbo.xxx.YyyInvokerWrapper} 的缺省值为 <code>String[] {"yyy.invoker.wrapper"}</code>
*
* @see SPI#value()
*/
String[] value() default {};
}
@Adaptive
注解,可添加类或方法上,分别代表了两种不同的使用方式。
友情提示:一个拓展接口,有且仅有一个 Adaptive 拓展实现类。
- 第一种,标记在类上,代表手动实现它是一个拓展接口的 Adaptive 拓展实现类。目前 Dubbo 项目里,只有 ExtensionFactory 拓展的实现类 AdaptiveExtensionFactory 有这么用。
- 第二种,标记在拓展接口的方法上,代表自动生成代码实现该接口的 Adaptive 拓展实现类。
value
,从 Dubbo URL 获取参数中,使用键名( Key ),获取键值。该值为真正的拓展名。- 自适应拓展实现类,会获取拓展名对应的真正的拓展对象。通过该对象,执行真正的逻辑。
- 可以设置多个键名( Key ),顺序获取直到有值。若最终获取不到,使用默认拓展名。
5、 @Activate
com.alibaba.dubbo.common.extension.@Activate ,自动激活条件的标记。代码如下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
/**
* Activate the current extension when one of the groups matches. The group passed into
* {@link ExtensionLoader#getActivateExtension(URL, String, String)} will be used for matching.
*
* @return group names to match
* @see ExtensionLoader#getActivateExtension(URL, String, String)
*/
/**
* Group过滤条件。
* <br />
* 包含{@link ExtensionLoader#getActivateExtension}的group参数给的值,则返回扩展。
* <br />
* 如没有Group设置,则不过滤。
*/
String[] group() default {};
/**
* Activate the current extension when the specified keys appear in the URL's parameters.
* <p>
* For example, given <code>@Activate("cache, validation")</code>, the current extension will be return only when
* there's either <code>cache</code> or <code>validation</code> key appeared in the URL's parameters.
* </p>
*
* @return URL parameter keys
* @see ExtensionLoader#getActivateExtension(URL, String)
* @see ExtensionLoader#getActivateExtension(URL, String, String)
*/
/**
* Key过滤条件。包含{@link ExtensionLoader#getActivateExtension}的URL的参数Key中有,则返回扩展。
* <p/>
* 示例:<br/>
* 注解的值 <code>@Activate("cache,validatioin")</code>,
* 则{@link ExtensionLoader#getActivateExtension}的URL的参数有<code>cache</code>Key,或是<code>validatioin</code>则返回扩展。
* <br/>
* 如没有设置,则不过滤。
*/
String[] value() default {};
/**
* Relative ordering info, optional
*
* @return extension list which should be put before the current one
*/
/**
* 排序信息,可以不提供。
*/
String[] before() default {};
/**
* Relative ordering info, optional
*
* @return extension list which should be put after the current one
*/
/**
* 排序信息,可以不提供。
*/
String[] after() default {};
/**
* Absolute ordering info, optional
*
* @return absolute ordering info
*/
/**
* 排序信息,可以不提供。
*/
int order() default 0;
}
- 对于可以被框架中自动激活加载扩展,
@Activate
用于配置扩展被自动激活加载条件。比如,Filter 扩展,有多个实现,使用@Activate
的扩展可以根据条件被自动加载。- 这块的例子,可以看下 《Dubbo 开发指南 —— 扩展点加载》「扩展点自动激活」 文档提供的。
- 分成过滤条件和排序信息两类属性,胖友看下代码里的注释。
6、 ExtensionFactory
com.alibaba.dubbo.common.extension.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 自身也是拓展接口,基于 Dubbo SPI 加载具体拓展实现类#getExtension(type, name)方法,在 「4.4.3 injectExtension」 中,获得拓展对象,向创建拓展对象注入依赖属性。在实际代码中,我们可以看到不仅仅获得的是拓展对象,也可以是 Spring 中的 Bean 对象。 ExtensionFactory 子类类图如下:ExtensionFactory 类图
1、AdaptiveExtensionFactory com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory ,自适应 ExtensionFactory 拓展实现类。代码如下:
/**
* AdaptiveExtensionFactory
*/
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
/**
* ExtensionFactory 拓展对象集合
*/
private final List<ExtensionFactory> factories;
public AdaptiveExtensionFactory() {
// 使用 ExtensionLoader 加载拓展对象实现类。
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;
}
}
@Adaptive
注解,为 ExtensionFactory 的自适应拓展实现类。- 构造方法,使用 ExtensionLoader 加载 ExtensionFactory 拓展对象的实现类。若胖友没自己实现 ExtensionFactory 的情况下,
factories
为 SpiExtensionFactory 和 SpringExtensionFactory 。 #getExtension(type, name)
方法,遍历factories
,调用其#getExtension(type, name)
方法,直到获得到属性值。
2、 SpiExtensionFactory
com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory ,SPI ExtensionFactory 拓展实现类。代码如下:
public class SpiExtensionFactory implements ExtensionFactory {
/**
* 获得拓展对象
*
* @param type object type. 拓展接口
* @param name object name. 拓展名
* @param <T> 泛型
* @return 拓展对象
*/
public <T> T getExtension(Class<T> type, String name) {
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { // 校验是 @SPI
// 加载拓展接口对应的 ExtensionLoader 对象
ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
// 加载拓展对象
if (!loader.getSupportedExtensions().isEmpty()) {
return loader.getAdaptiveExtension();
}
}
return null;
}
}
3、 SpringExtensionFactory
com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory,Spring ExtensionFactory 拓展实现类。代码如下:
/**
* SpringExtensionFactory
*/
public class SpringExtensionFactory implements ExtensionFactory {
/**
* Spring Context 集合
*/
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);
}
@Override
@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;
}
}
#getExtension(type, name)
方法,遍历contexts
,调用其ApplicationContext#getBean(name)
方法,获得 Bean 对象,直到成功并且值类型正确。
例子:DemoFilter 是自定义实现的 Filter 拓展实现类,代码如下:
public class DemoFilter implements Filter {
private DemoDAO demoDAO;
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
return invoker.invoke(invocation);
}
public DemoFilter setDemoDAO(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
return this;
}
}
-
DemoDAO ,笔者在 Spring 中声明对应的 Bean 对象。
<bean id="demoDAO" class="com.alibaba.dubbo.demo.provider.DemoDAO" />
-
在 「4.4.3 injectExtension」中,会调用
#setDemoDAO(demo)
方法,将 DemoFilter 依赖的属性demoDAO
注入。