概述
概述
扩展点加载机制在dubbo框架中无处不在, 它也是dubbo 高扩展性的关键实现. 所以要彻底理解dubbo的实现原理, 弄明白扩展点加载机制至关重要.
dubbo 的扩展点机制本质上是在Java SPI 基础 上改造的. 那么什么是Java 的SPI 呢
所有技术的衍生都是基于业务需要,因为有需要,才会想解决办法, 进而产生各种技术. 那么Java的SPI 的产生又是基于什么样的需求呢?
我们知道JDBC是为了统一数据库的操作而由Sun 公司定制的一套接口标准, 而具体的实现则由各个数据库厂商提供, 比如我们使用mysql数据库,那么我们就要引用mysql的依赖, 这个依赖就是对JDBC的实现. 那么Java 虚拟机怎么知道接口的实现是哪个呢, 就是通过Java的SPI 机制
SPI机制的核心类是ServiceLoader, 通过调用ServiceLoader.load(接口.class) 方法加载该接口的所有实现.
load 方法的实现原理是 去 METSA_INF/services 目录寻找文件名为该接口名的文件, 然后逐行加载该文件中写的接口的实现类
使用JDBC的例子来理解就是 Java 定制了一套数据库接口, 然后数据库厂商提供实现类,然后在他们项目的 META-INF/services包 创建文件名为接口全类名的文件, 文件内容写他对这个接口的实现类的全类名.
比如我们引入了mysql的依赖, Java虚拟机通过这个规则就会加载到对应的实现了.
我们可以看到使用这种加载机制, 可以很好的将接口和实现分离, 极大的提高了灵活性和扩展性.
dubbo 扩展点加载机制在此基础上做了扩展和改进
- dubbo 加载的路径包括 META-INF/services, META-INF/dubbo 和 META-INF/dubbo/internal
- dubbo 可以按扩展名来使用不用的扩展类, Java SPI 是将实现类的全类名直接写在文件中, 而 dubbo 的形式是 扩展名=扩展实现全类名,因此可以按扩展名去使用对应的扩展类
- dubbo 是按需实例化的, 使用Java的ServiceLoader.load 方法加载完实现类,它会对所有的实现类都实例化. 而dubbo 是根据你要使用的那个扩展名来实例化对应的实现的, 其他的扩展类放在缓存中
- dubbo 增加实现了 类似AOP和IOC的功能
三个关键注解
dubbo 扩展点加载机制有三个重要的注解,分别是
- @SPI: 主要是标记功能, 标记一个接口是Dubbo SPI 接口, 也就是表明它是一个扩展点, 可以有多个不同的实现
- @Adaptive: 标注在类上, 表示这个类是该扩展点的默认实现, 最多只能有一个扩展点实现使用这个注解, 也就是一个扩展点的自适应实现智能有一个; 标注在方法上, dubbo 框架会为这个扩展点生成一个默认的自适应实现.
- @Activate: 主要的使用场景是 在不同的场景中需要激活不同的扩展点实现
ExtensionLoader的实现原理
ExtensionLoader 的构造方法是私有的, 使用方法是通过调用静态方法来获取一个ExtensionLoader实例,然后调用getExtension(String name) 方法来加载以name为扩展名的实现
即常见用法是
ExtensionLoader.getExtensionLoader(扩展点.class).getExtension(扩展名)
getExtensionLoader 方法首先会检查传入的类型是否为一个合法的扩展点,也就是是否是一个有@SPI 注解的接口
然后从 EXTENSION_LOADERS 缓存中获取该type对应的扩展点加载器,如果缓存中没有再新建一个, 为什么要从缓存中获取扩展点加载器呢?
因为dubbo 对扩展点的实现设置了很多缓存, 比如扩展点的实现类,自适应类,包装类等等, 这些缓存就保存在这个扩展点对应的扩展点加载器中.
可以看到在构造方法中 objectFactory 也是通过加载扩展点实现来赋值的, objectFactory 具体在给扩展点注入其他扩展点属性的时候会使用到, ExtensionFactory 在后面再详细解析
// 静态的,全局唯一
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
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 an interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type +
") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
}
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;
}
// 私有的静态方法
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory =
(type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
ExtensionLoader 有三个获取扩展点实现的方法, 分别是 getExtension, getAdaptiveExtension 和getActivateExtension 分别获取普通扩展点实现, 自适应扩展点实现, 自动激活的扩展点实现
普通扩展类: 除了自适应扩展类, 包装扩展类,其他的就是普通扩展类
自适应扩展类: 有@Adaptive注解的扩展类, 或者扩展点接口中的方法有@Adaptive注解的, 此时 dubbo 框架会动态生成一个
自动激活扩展点: 主要使用在有多个扩展实现, 需要根据不同条件被激活的场景, 比如Filter需要多个同时被激活.
getExtension
参数说明:
name: 扩展名
wrap: 是否对扩展实例进行包装, 默认为真, 封装后面会详细解析
总体流程:
1. 如果name 为"true" 则使用默认的实现,后面会解析
2. 先从cachedInstances 普通扩展实例缓存中获取, 如果没有先new一个Holder占个坑
3. 然后使用double-check 机制对holder加锁, 然后调用 createExtension 创建实例,在放到holder中
这里有个小问题,为什么要使用holder,并且加锁呢? cachedInstances 已经使用了 ConcurrentHashMap,可以保证线程安全啊?
其中一个原因, 是因为要获取一个扩展点的实例, 需要加载这个扩展点的所有实现类, 实例化之后还要对实例进行依赖注入和包装, 最后才是一个完整的实例,如果直接在实例化完成后放入ConcurrentHashMap, 由 ConcurrentHashMap 保证只有一个 实例, 那么可能会导致多个线程都执行了实例化的过程, 而最后只有一个线程的结果被采用, 造成浪费
public T getExtension(String name) {
return getExtension(name, true);
}
public T getExtension(String name, boolean wrap) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
if ("true".equals(name)) {
return getDefaultExtension();
}
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
instance = createExtension(name, wrap);
holder.set(instance);
}
}
}
return (T) instance;
}
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
private Holder<Object> getOrCreateHolder(String name) {
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<>());
holder = cachedInstances.get(name);
}
return holder;
}
// Holder 实现
public class Holder<T> {
// volatile 保证了线程间可见
private volatile T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}
createExtension
总体流程:
1. 通过 getExtensionClasses 方法获取该type类型的扩展点的所有实现类, 然后拿到扩展名为name的实现类
2. 从EXTENSION_INSTANCES 缓冲中获取该扩展类的实例,如果没有则新构造一个
3. 调用 injectExtension(instance) 方法对该实例进行属性注入
4. 如果要进行包装, 则遍历排序后的 cachedWrapperClasses (在getExtensionClasses过程中会进行缓存), 用 instance 去实例化包装类,然后对包装类的实例调用 injectExtension 方法进行属性注入之后, 作为新的 instance. 我们看到如果有多个包装类会进行层层包装
5. 调用initExtension(instance) 方法
// 静态的,全局唯一, 一个扩展点实现类只会保存一个实例
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
private T createExtension(String name, boolean wrap) {
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null || unacceptableExceptions.contains(name)) {
throw findException(name);
}
try {
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
injectExtension(instance);
if (wrap) {
List<Class<?>> wrapperClassesList = new ArrayList<>();
if (cachedWrapperClasses != null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
for (Class<?> wrapperClass : wrapperClassesList) {
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
if (wrapper == null
|| (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
}
}
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
private void initExtension(T instance) {
if (instance instanceof Lifecycle) {
Lifecycle lifecycle = (Lifecycle) instance;
lifecycle.initialize();
}
}
getExtensionClasses
一样的套路,先尝试从缓存中获取, 如果没有再通过 loadExtensionClasses 加载
这里同样是锁着占位坑 holder
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
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;
}
loadExtensionClasses
总体逻辑:
1. 使用cacheDefaultExtensionName 方法将扩展点 @SPI注解中的value 作为 默认扩展名缓存在 cachedDefaultName 中, 在上述的name 为 “true” 时返回默认扩展实现中会使用到
2. 遍历所有的加载策略, 通过 loadDirectory 方法加载各个加载策略的路径下的该扩展点的所有实现, 这里调用两次 loadDirectory 方法是为了兼容dubbo 贡献给apache之前的实现
加载策略 strategies 也是通过java SPI 来实现的, dubbo 默认提供的LoadingStrategy 一共有三种, 分别对应三个加载路径
org.apache.dubbo.common.extension.DubboInternalLoadingStrategy ----> META-INF/dubbo/internal
org.apache.dubbo.common.extension.DubboLoadingStrategy ------> META-INF/dubbo
org.apache.dubbo.common.extension.ServicesLoadingStrategy -------> META-INF/services
private Map<String, Class<?>> loadExtensionClasses() {
cacheDefaultExtensionName();
Map<String, Class<?>> extensionClasses = new HashMap<>();
for (LoadingStrategy strategy : strategies) {
loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(),
strategy.overridden(), strategy.excludedPackages());
loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"),
strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
}
return extensionClasses;
}
private static final Pattern NAME_SEPARATOR = Pattern.compile("\s*[,]+\s*");
private void cacheDefaultExtensionName() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation == null) {
return;
}
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];
}
}
}
private static volatile LoadingStrategy[] strategies = loadLoadingStrategies();
private static LoadingStrategy[] loadLoadingStrategies() {
return stream(ServiceLoader.load(LoadingStrategy.class).spliterator(), false)
.sorted()
.toArray(LoadingStrategy[]::new);
}
loadDirectory
总体逻辑:
1. 调用 类加载器的 getResources 方法获取所有的对应路径下名为扩展点全类名的文件
2. 调用 loadResource 方法解析每个文件
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type,
boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) {
String fileName = dir + type;
try {
Enumeration<java.net.URL> urls = null;
ClassLoader classLoader = findClassLoader();
// try to load from ExtensionLoader's ClassLoader first
if (extensionLoaderClassLoaderFirst) {
ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader();
if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) {
urls = extensionLoaderClassLoader.getResources(fileName);
}
}
if (urls == null || !urls.hasMoreElements()) {
if (classLoader != null) {
urls = classLoader.getResources(fileName);
} else {
urls = ClassLoader.getSystemResources(fileName);
}
}
if (urls != null) {
while (urls.hasMoreElements()) {
java.net.URL resourceURL = urls.nextElement();
loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages);
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", description file: " + fileName + ").", t);
}
}
loadResource
总体逻辑:
- 逐行解析文件, 截取 注释 # 前面的部分,使用 = 分隔,分别获取扩展名和实现类名
- 调用 Class.forName(clazz, true, classLoader) 加载实现类
- 调用 loadClass 进行分类缓存
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader,
java.net.URL resourceURL, boolean overridden, String... excludedPackages) {
try {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
String line;
String clazz = 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();
// 扩展类全路径
clazz = line.substring(i + 1).trim();
} else {
// 兼容 Java SPI
clazz = line;
}
if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages)) {
loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden);
}
} catch (Throwable t) {
IllegalStateException e = new IllegalStateException(
"Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL +
", cause: " + t.getMessage(), t);
exceptions.put(line, e);
}
}
}
}
} catch (Throwable t) {
logger.error("Exception occurred when loading extension class (interface: " +
type + ", class file: " + resourceURL + ") in " + resourceURL, t);
}
}
总体逻辑:
- 如果该扩展类有@Adaptive注解, 将它缓存在 cachedAdaptiveClass 中
- 如果该扩展类是包装类(即实现了type接口,又将type接口作为构造器参数), 将它缓存在cachedWrapperClasses 中, 在上述提到的包装过程中会使用
- 其他的就是普通的扩展类,缓存在cachedClasses 中, 对于普通扩展类,还会调用一个方法 cacheActivateClass 会将扩展名和Activate 注解缓存在 cachedActivates ,在 getActivateExtension 中会使用
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
boolean overridden) throws NoSuchMethodException {
if (!type.isAssignableFrom(clazz)) {
throw new IllegalStateException("Error occurred when loading extension class (interface: " +
type + ", class line: " + clazz.getName() + "), class "
+ clazz.getName() + " is not subtype of interface.");
}
if (clazz.isAnnotationPresent(Adaptive.class)) {
cacheAdaptiveClass(clazz, overridden);
} else if (isWrapperClass(clazz)) {
cacheWrapperClass(clazz);
} else {
clazz.getConstructor();
if (StringUtils.isEmpty(name)) {
name = findAnnotationName(clazz);
if (name.length() == 0) {
throw new IllegalStateException(
"No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);
}
}
String[] names = NAME_SEPARATOR.split(name);
if (ArrayUtils.isNotEmpty(names)) {
cacheActivateClass(clazz, names[0]);
for (String n : names) {
cacheName(clazz, n);
saveInExtensionClass(extensionClasses, clazz, n, overridden);
}
}
}
}
private void cacheActivateClass(Class<?> clazz, String name) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null) {
cachedActivates.put(name, activate);
} else {
// support com.alibaba.dubbo.common.extension.Activate
com.alibaba.dubbo.common.extension.Activate oldActivate =
clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class);
if (oldActivate != null) {
cachedActivates.put(name, oldActivate);
}
}
}
injectExtension
总体逻辑:
- 如果该扩展点加载器没有objectFactory,则不进行属性注入, 通过构造函数,我们知道只有 ExtensionFactory 会不进行属性注入
- 遍历该实例的所有setter方法, 而且是没有禁止注入注解DisableInject 的方法, 对于基本类型也不进行注入,
- 通过截取setter方法获得属性名, 然后去ExtensionFactory 扩展点工厂找实例, 然后注入
private T injectExtension(T instance) {
if (objectFactory == null) {
return instance;
}
try {
for (Method method : instance.getClass().getMethods()) {
if (!isSetter(method)) {
continue;
}
/**
* Check {@link DisableInject} to see if we need auto injection for this property
*/
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
Class<?> pt = method.getParameterTypes()[0];
if (ReflectUtils.isPrimitives(pt)) {
continue;
}
try {
String property = getSetterProperty(method);
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
method.invoke(instance, object);
}
} catch (Exception e) {
logger.error("Failed to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
private String getSetterProperty(Method method) {
return method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
}
getAdaptiveExtension
一样的套路,先尝试从缓冲中获取自适应实例,如果没有,则占坑,加锁, 调用 createAdaptiveExtension 创建自适应实例
private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
public T getAdaptiveExtension() {
Object instance = cachedAdaptiveInstance.get();
if (instance == null) {
if (createAdaptiveInstanceError != null) {
throw new IllegalStateException("Failed to create adaptive instance: " +
createAdaptiveInstanceError.toString(),
createAdaptiveInstanceError);
}
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get();
if (instance == null) {
try {
instance = createAdaptiveExtension();
cachedAdaptiveInstance.set(instance);
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t);
}
}
}
}
return (T) instance;
}
createAdaptiveExtension, getAdaptiveExtensionClass
- createAdaptiveExtension 方法
调用getAdaptiveExtensionClass 获取自适应扩展类, 进行实例化后,在进行属性注入
这里我们看到对于自适应扩展实例不会进行包装 - getAdaptiveExtensionClass 方法
- 先调用getExtensionClasses方法,通过上面我们知道该方法会加载type扩展点的所有类型,并进行分类缓存, 如果有@Adaptive 注解的实现类,则会缓存在 cachedAdaptiveClass 中
- 调用createAdaptiveExtensionClass 直接生成自适应类的字符串形式的代码,然后使用动态编译器编译 得到自适应扩展类
- AdaptiveClassCodeGenerator 生成代码的规则如下
- 像我们编写一般的Java类一样,首先写包名,import, 类名,该类名为扩展点类名加$Adaptive后缀, 实现的类,继承的类等
- 类的内容是逐一生成扩展点的方法实现, 对于没有@Adaptive注解的方法, 生成空实现, 对于有@Adaptive注解的方法,在生成的方法中会调用
ExtensionLoader.getExtensionLoader(扩展点).getExtension(extName)
那么extName怎么获取呢, 在URL中找 @Adaptive 中声明的key , 如果有拿value作为扩展名,如果 @Adaptive 没有声明key, 那么用. 分隔驼峰类名得到extName, 如果没有对应的扩展类实现, 则使用默认扩展名
获取扩展点实现后,会调用该扩展点的该接口方法.
比如对于Transporter,会生成Transporter$Adaptive 类, 生成的 bind 方法会 获取扩展点实现后,调用bind 方法
private T createAdaptiveExtension() {
try {
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
private Class<?> createAdaptiveExtensionClass() {
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler =
ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
@SPI("netty")
public interface Transporter {
/**
* Bind a server.
*
* @param url server url
* @param handler
* @return server
* @throws RemotingException
* @see org.apache.dubbo.remoting.Transporters#bind(URL, ChannelHandler...)
*/
@Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException;
/**
* Connect to a server.
*
* @param url server url
* @param handler
* @return client
* @throws RemotingException
* @see org.apache.dubbo.remoting.Transporters#connect(URL, ChannelHandler...)
*/
@Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
Client connect(URL url, ChannelHandler handler) throws RemotingException;
}
getActivateExtension
- 先从URL中获取key 对应的值,如果有多个则用, 分隔,作为names
- 如果names中不包含-default
- 调用 getExtensionClasses, 通过上面我们知道这个函数去加载type 扩展点的所有实现, 在加载完之后进行分类缓存时,对于普通扩展类,如果有@Activate 注解, 会将扩展名和Activate 注解缓存在 cachedActivates , 在此时则需要使用该缓存
- 遍历 cachedActivates , 如果names中不包含name, 也不包含-name, 并且@Activate中group和values能和group 和URL中的参数匹配,则激活该扩展名
这个方法的逻辑比较绕, 关键是我们弄清楚要激活哪些扩展类即可
- 先从URL中获取key 对应的值,如果有多个则用, 分隔,作为names
- 激活分为两部分,一部分是从缓存 cachedActivates 中获取. 通过上面我们知道getExtensionClasses这个函数去加载type 扩展点的所有实现, 在加载完之后进行分类缓存时,对于普通扩展类,如果有@Activate 注解, 会将扩展名和Activate 注解缓存在cachedActivates; 另一部分是激活 names中满足条件的
- 对于第一部分, 如果names 中没有-default, 那么会去缓存 cachedActivates 中查找能适配group和URL的扩展名,如果匹配就激活.
- 对于第二部分, 遍历每个names, 如果names 中不包含-name, 那么激活
[说明] default代表所有@Activate集合, -xxxx 则表示不激活xxxx
public List<T> getActivateExtension(URL url, String key, String group) {
String value = url.getParameter(key);
return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group);
}
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> activateExtensions = new ArrayList<>();
// solve the bug of using @SPI's wrapper method to report a null pointer exception.
TreeMap<Class, T> activateExtensionsMap = new TreeMap<>(ActivateComparator.COMPARATOR);
Set<String> loadedNames = new HashSet<>();
Set<String> names = CollectionUtils.ofSet(values);
if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) {
getExtensionClasses();
for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Object activate = entry.getValue();
String[] activateGroup, activateValue;
if (activate instanceof Activate) {
activateGroup = ((Activate) activate).group();
activateValue = ((Activate) activate).value();
} else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) {
activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group();
activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value();
} else {
continue;
}
if (isMatchGroup(group, activateGroup)
&& !names.contains(name)
&& !names.contains(REMOVE_VALUE_PREFIX + name)
&& isActive(activateValue, url)
&& !loadedNames.contains(name)) {
activateExtensionsMap.put(getExtensionClass(name), getExtension(name));
loadedNames.add(name);
}
}
if (!activateExtensionsMap.isEmpty()) {
activateExtensions.addAll(activateExtensionsMap.values());
}
}
List<T> loadedExtensions = new ArrayList<>();
// 遍历URL中指定key的值names, 对于每个值name, 如果name 不是以- 开头 而且 names中也不包含-name, 那么激活该name
for (String name : names) {
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !names.contains(REMOVE_VALUE_PREFIX + name)) {
if (!loadedNames.contains(name)) {
if (DEFAULT_KEY.equals(name)) {
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(0, loadedExtensions);
loadedExtensions.clear();
}
} else {
loadedExtensions.add(getExtension(name));
}
loadedNames.add(name);
} else {
// If getExtension(name) exists, getExtensionClass(name) must exist, so there is no null pointer processing here.
String simpleName = getExtensionClass(name).getSimpleName();
logger.warn("Catch duplicated filter, ExtensionLoader will ignore one of them. Please check. Filter Name: " + name +
". Ignored Class Name: " + simpleName);
}
}
}
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(loadedExtensions);
}
return activateExtensions;
}
// 遍历@Activate 注解的values数组, 对于每个值用: 分隔,得到key, keyValue, 然后遍历URL中的参数(k,v形式), 如果参数k与key 相同或者后缀是.key 而且 keyValue 与v 相同或者 v 不为空, 那么返回true, 激活该扩展名
// 大概含义 就是 URL中包含@Activate 注解中values 声明的值,那么应该激活
private boolean isActive(String[] keys, URL url) {
if (keys.length == 0) {
return true;
}
for (String key : keys) {
// @Active(value="key1:value1, key2:value2")
String keyValue = null;
if (key.contains(":")) {
String[] arr = key.split(":");
key = arr[0];
keyValue = arr[1];
}
for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
String k = entry.getKey();
String v = entry.getValue();
if ((k.equals(key) || k.endsWith("." + key))
&& ((keyValue != null && keyValue.equals(v)) || (keyValue == null && ConfigUtils.isNotEmpty(v)))) {
return true;
}
}
}
return false;
}
ExtensionFactory的实现原理
在对扩展实例进行属性注入,使用到了objectFactory, 通过构造函数,我们知道它是ExtensionFactory的自适应实现
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory =
(type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
ExtensionFactory 有三个实现类
- AdaptiveExtensionFactory
- SpiExtensionFactory
- SpringExtensionFactory
其中, AdaptiveExtensionFactory 有@Adaptive注解, 通过上述 对getAdaptiveExtension 方法的流程分析, 如果有扩展类上有@Adaptive注解,则直接作为自适应实现, 所以 objectFactory 为 AdaptiveExtensionFactory
AdaptiveExtensionFactory
AdaptiveExtensionFactory 会在构造函数中初始化factories, 然后在执行 getExtension 方法时,调用每个factory的getExtension 方法. 那么factories是什么呢
通过查看ExtensionLoader 的 getSupportedExtensions 方法, factories 就是getExtensionClasses 方法的返回结果,而getExtensionClasses 是普通扩展类, 所以factories也就是 SpiExtensionFactory 和 SpringExtensionFactory
综上, 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);
}
@Override
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;
}
}
// ExtensionLoader 的 getSupportedExtensions 方法
public Set<String> getSupportedExtensions() {
Map<String, Class<?>> clazzes = getExtensionClasses();
return Collections.unmodifiableSet(new TreeSet<>(clazzes.keySet()));
}
SpiExtensionFactory
getExtension 方法返回type类型 的自适应扩展实例
public class SpiExtensionFactory implements ExtensionFactory {
@Override
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().isEmpty()) {
return loader.getAdaptiveExtension();
}
}
return null;
}
}
SpringExtensionFactory
从 getExtension 方法可以看到 它是遍历SpringContext, 然后从Spring上下文中获取满足条件的实例的.那么Spring上下文是什么时候注入的, 是在 ReferenceBean, ServiceBean 和 ConfigCenterBean 初始化时注入的.
也就是dubbo 的属性注入不但可以注入其他的扩展点实例,还可以从spring容器中查找bean注入
public class SpringExtensionFactory implements ExtensionFactory {
private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class);
private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>();
public static void addApplicationContext(ApplicationContext context) {
CONTEXTS.add(context);
if (context instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) context).registerShutdownHook();
// see https://github.com/apache/dubbo/issues/7093
DubboShutdownHook.getDubboShutdownHook().unregister();
}
}
public static void removeApplicationContext(ApplicationContext context) {
CONTEXTS.remove(context);
}
public static Set<ApplicationContext> getContexts() {
return CONTEXTS;
}
// currently for test purpose
public static void clearContexts() {
CONTEXTS.clear();
}
@Override
@SuppressWarnings("unchecked")
public <T> T getExtension(Class<T> type, String name) {
//SPI should be get from SpiExtensionFactory
if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
return null;
}
for (ApplicationContext context : CONTEXTS) {
T bean = getOptionalBean(context, name, type);
if (bean != null) {
return bean;
}
}
//logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + type.getName());
return null;
}
}
public static <T> T getOptionalBean(ListableBeanFactory beanFactory, String beanName, Class<T> beanType) throws BeansException {
if (beanName == null) {
return getOptionalBeanByType(beanFactory, beanType);
}
T bean = null;
try {
bean = beanFactory.getBean(beanName, beanType);
} catch (NoSuchBeanDefinitionException e) {
// ignore NoSuchBeanDefinitionException
} catch (BeanNotOfRequiredTypeException e) {
// ignore BeanNotOfRequiredTypeException
logger.warn(String.format("bean type not match, name: %s, expected type: %s, actual type: %s",
beanName, beanType.getName(), e.getActualType().getName()));
}
return bean;
}
private static <T> T getOptionalBeanByType(ListableBeanFactory beanFactory, Class<T> beanType) {
// Issue : https://github.com/alibaba/spring-context-support/issues/20
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, beanType, true, false);
if (beanNames == null || beanNames.length == 0) {
return null;
} else if (beanNames.length > 1) {
throw new NoUniqueBeanDefinitionException(beanType, Arrays.asList(beanNames));
}
return (T) beanFactory.getBean(beanNames[0]);
}
扩展点动态编译的实现
dubbo 动态生成的自适应扩展类只是字符串, 还需要编译才能得到真正的Class.
从如下代码可以看到,dubbo先是生成类的字符串code, 然后获取Compiler的自适应实现 来编译code 得到Class
private Class<?> createAdaptiveExtensionClass() {
String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate();
ClassLoader classLoader = findClassLoader();
org.apache.dubbo.common.compiler.Compiler compiler =
ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
Compiler 有两个子类 AdaptiveCompiler, AbstractCompiler, AbstractCompiler 的也有两个子类 JavassistCompiler 和 JdkCompiler
和 ExtensionFactory 类似, AdaptiveCompiler 也是自适应实现,是管理 JavassistCompiler 和 JdkCompiler 的
AbstractCompiler 只是实现了一些通用逻辑,具体的编译工作由子类 JavassistCompiler 和 JdkCompiler 实现
AdaptiveCompiler
AdaptiveCompiler 的compile 方法, 先获取默认的编译器DEFAULT_COMPILER, 如果没有才调用ExtensionLoader的getDefaultExtension获得
getDefaultExtension 是加载cachedDefaultName 缓存的扩展名, 也就是扩展点@SPI注解中声明的值, Compiler声明的是javassist, 所以也就是如果没有设置 DEFAULT_COMPILER,默认使用的编译器是 JavassistCompiler
而通过代码搜索得知, DEFAULT_COMPILER 是在ApplicationConfig实例化的注入的,通过专栏的下一章得知 该属性是在解析配置文件中的<dubbo:application compiler=‘jdk’/> 赋值 的, 所以如果想使用 JdkCompiler 编译器,可以在配置文件配置
具体的编译工作由 JavassistCompiler 或者 JdkCompiler完成, 暂时就不再详细解析了,分别用到了 Javassist 和jdk原生的编译器.
后续再在Java字节码专栏进行详细讲解
@Adaptive
public class AdaptiveCompiler implements Compiler {
private static volatile String DEFAULT_COMPILER;
public static void setDefaultCompiler(String compiler) {
DEFAULT_COMPILER = compiler;
}
@Override
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);
}
}
//ExtensionLoader 的getDefaultExtension
public T getDefaultExtension() {
getExtensionClasses();
if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) {
return null;
}
return getExtension(cachedDefaultName);
}
最后
以上就是野性跳跳糖为你收集整理的dubbo 扩展点加载机制的全部内容,希望文章能够帮你解决dubbo 扩展点加载机制所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复