Dubbo在加载扩展点时为了提高性能,会将需要Class与实例进行缓存。
Class缓存:Dubbo SPI获取扩展类时,会先从缓存中获取,如果缓存中不存在,则加载配置文件配置文件,根据配置把Class缓存到内存中,并不会直接全部初始化。
实例缓存:与获取Class相同,每次获取实例的时候,先从缓存中获取,如果获取不到,则重新加载并缓存起来。
这也是Dubbo SPI相对Java SPI性能上有优势的原因,Dubbo SPI缓存的Class并不会全部实例化,而是按需进行实例化并缓存。
被缓存的Class和对象实例可以根据不同的特性分为不同的类别:
1、普通扩展类
最基础的,配置在SPI配置文件中的扩展类实现。
2、包装扩展类
Wrapper类是一种装饰者模式,在构造方法中传入一个具体扩展接口的实现,属于Dubbo的自动包装特性。在ExtensionLoader在加载扩展时,如果发现这个扩展类包含其它扩展点作为构造函数参数,则这个扩展类就会被认定为是Wrapper类。
例如:
public class ProtocolFilterWrapper implements Protocol {
private final Protocol protocol;
public ProtocolFilterWrapper(Protocol protocol) {
if (protocol == null) {
throw new IllegalArgumentException("protocol == null");
}
this.protocol = protocol;
}
......
}
ProtocolFilterWrapper类实现了Protocol扩展接口,构造函数中传入的Protocol扩展对象便是Wrapper的增强对象,这样便可以将通用逻辑进行封装,使扩展点可以专注于自己的业务实现。
3、自适应扩展类
一个扩展接口会有多种实现类,具体使用哪个实现类可以不写死在配置或代码中,在运行时,通过传入URL中的某些参数动态确定,这属于扩展点的自适应特性。
4、其它
如扩展类加载器、扩展名缓存等。
ExtensionLoader中的各种缓存:
// 扩展类与对应的扩展类加载器缓存
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>();
// 扩展类与类初始化后的实例
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>();
// ==============================
private final Class<?> type;
private final ExtensionFactory objectFactory;
// 扩展类与扩展名缓存
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
// 普通扩展类缓存,不包括自适应扩展类和Wrapper类
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
// 自动激活扩展类缓存
private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
// 扩展名与扩展对象缓存
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
// 实例化后的自适应(Adaptive)扩展对象,只能存在一个
private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
// 自适应扩展类缓存
private volatile Class<?> cachedAdaptiveClass = null;
// 默认扩展名缓存
private String cachedDefaultName;
private volatile Throwable createAdaptiveInstanceError;
// wrapper类缓存
private Set<Class<?>> cachedWrapperClasses;