前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Dubbo系列五之ExtensionLoader

Dubbo系列五之ExtensionLoader

原创
作者头像
用户9511949
发布2024-09-11 18:32:57
920
发布2024-09-11 18:32:57

1 获取ExtensionLoader

Dubbo提供的SPI机制有ExtensionLoader实现,本文以Protocol为例来说明ExtensionLoader的实现机制,从之前的文章中可知,ServiceBean实现了InitializingBean接口,在其afterPropertiesSet方法中会将ServiceBean添加到ModuleModel的configManager中,在添加之前会为ServiceBean设置ModuleModel,ModuleModel主要用来管理服务的生命周期,如下所示

代码语言:javascript
复制
public final <T extends AbstractConfig> T addConfig(AbstractConfig config) {
    ......
    if (config.getScopeModel() != scopeModel) {
        config.setScopeModel(scopeModel);
    }
    ......
    // lock by config type
    synchronized (configsMap) {
        return (T) addIfAbsent(config, configsMap);
    }
}

设置ModuleModel的代码如下

代码语言:javascript
复制
public final void setScopeModel(ScopeModel scopeModel) {
    if (scopeModel != null && this.scopeModel != scopeModel) {
        checkScopeModel(scopeModel);
        ScopeModel oldScopeModel = this.scopeModel;
        this.scopeModel = scopeModel;
        // reinitialize spi extension and change referenced config's scope model
        this.postProcessAfterScopeModelChanged(oldScopeModel, this.scopeModel);
    }
}

设置完ModuleModel之后会调用postProcessAfterScopeModelChanged(oldScopeModel,this.scopeModel)方法执行设置完之后的逻辑,如下

代码语言:javascript
复制
protected void postProcessAfterScopeModelChanged(ScopeModel oldScopeModel, ScopeModel newScopeModel) {
    super.postProcessAfterScopeModelChanged(oldScopeModel, newScopeModel);
    protocolSPI = this.getExtensionLoader(Protocol.class).getAdaptiveExtension();
    proxyFactory = this.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
}

protocolSPI就是在以上的方法中实例化,在从之前的文章中可知,protocolSPI是Protocol的适配类,在暴露服务时需要使用protocolSPI.export方法进行服务发布,而获取protocolSPI实例是通过对应的ExtensionLoader来获取

看看第一步获取Protocol对应的ExtensionLoader,获取ExtensionLoader是通过ModuleModel这个类来实现,通过调用其父接口ExtensionAccessor的getExtensionLoader(Class<T> type)方法,代码如下

代码语言:javascript
复制
default <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    return this.getExtensionDirector().getExtensionLoader(type);
}

然后通过ExtensionDirector来获取对应Class的ExtensionLoader,如下

代码语言:javascript
复制
public <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
    // 非null判断
    if (type == null) {
        throw new IllegalArgumentException("Extension type == null");
    }
    // class type必须是接口
    if (!type.isInterface()) {
        throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
    }
    // 接口是否存在SPI注解
    if (!withExtensionAnnotation(type)) {
        throw new IllegalArgumentException("Extension type (" + type +
            ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
    }

    // 1. 先从缓存中找
    ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoadersMap.get(type);

    ExtensionScope scope = extensionScopeMap.get(type);
    if (scope == null) {
        SPI annotation = type.getAnnotation(SPI.class);
        scope = annotation.scope();
        extensionScopeMap.put(type, scope);
    }

    if (loader == null && scope == ExtensionScope.SELF) {
        // create an instance in self scope
        loader = createExtensionLoader0(type);
    }

    // 2. 缓存中没有,如果该ExtensionDirector存在parent,则通过parent的getExtensionLoader方法获取
    if (loader == null) {
        if (this.parent != null) {
            loader = this.parent.getExtensionLoader(type);
        }
    }

    // 3. 还是没有就创建一个
    if (loader == null) {
        loader = createExtensionLoader(type);
    }
    return loader;
}

获取ExtensionLoader比较简单,先从缓存中去取,没有就创建一个(由parent创建),创建的逻辑比较简单,就是New了一个ExtensionLoader对象,然后放入缓存中返回

看看第二步获取适配类,如下

代码语言:javascript
复制
public T getAdaptiveExtension() {
    checkDestroyed();
    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;
}

同样也是先从缓存中拿,拿不到再创建,这里第一次获取肯定没有缓存,看看创建的逻辑

代码语言:javascript
复制
private T createAdaptiveExtension() {
    try {
        // 1.获取适配类实例
        T instance = (T) getAdaptiveExtensionClass().newInstance();
        // 2.初始化之前的操作
        instance = postProcessBeforeInitialization(instance, null);
        // 3.依赖注入
        injectExtension(instance);
        // 4.初始化之后的操作
        instance = postProcessAfterInitialization(instance, null);
        // 5.初始化
        initExtension(instance);
        return instance;
    } catch (Exception e) {
        throw new IllegalStateException(
            "Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
    }
}

下面依次来看看各个步骤

1.获取适配类实例

在创建之前需要先调用getAdaptiveExtensionClass()获取适配类的class,如下

代码语言:javascript
复制
private Class<?> getAdaptiveExtensionClass() {
    getExtensionClasses();
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

首先通过getExtensionClasses()方法获取所有的Protocol接口的实现类,然后再通过createAdaptiveExtensionClass()方法生成适配类,生成适配类的逻辑比较简单,就是通过AdaptiveClassCodeGenerator动态生成代码,来看看获取所有的Protocol接口的实现类的方法,最终是通过loadExtensionClasses()获取,如下

代码语言:javascript
复制
private Map<String, Class<?>> loadExtensionClasses() throws InterruptedException {
    checkDestroyed();
    cacheDefaultExtensionName();

    Map<String, Class<?>> extensionClasses = new HashMap<>();

    for (LoadingStrategy strategy : strategies) {
        loadDirectory(extensionClasses, strategy, type.getName());

        // compatible with old ExtensionFactory
        if (this.type == ExtensionInjector.class) {
            loadDirectory(extensionClasses, strategy, ExtensionFactory.class.getName());
        }
    }

    return extensionClasses;
}

Dubbo提供了三种LoadingStrategy,如下

LoadingStrategy
LoadingStrategy

三种LoadingStrategy分别从不同的文件路径中加载实现类

DubboInternalLoadingStrategy:从classpath(包括jar包)中查找META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol文件加载实现类

DubboLoadingStrategy:从classpath(包括jar包)中查找META-INF/dubbo/org.apache.dubbo.rpc.Protocol文件加载实现类ServiceLoadingStrategy:从classpath(包括jar包)中查找META-INF/services/org.apache.dubbo.rpc.Protocol文件加载实现类

这样生成了protocolSPI适配类及其所有协议的实现类的加载

2&4.初始化之前之后的操作

这里将初始化之前之后的操作放在一起看,其实默认的逻辑不多,只是在初始化之后,如果实现了ExtensionAccessorAware接口,则将extensionDirector注入进去,ExtensionDirector 可以看成是ExtensionLoader工厂,可以获取每个接口的ExtensionLoader,如下

代码语言:javascript
复制
private T postProcessAfterInitialization(T instance, String name) throws Exception {
    if (instance instanceof ExtensionAccessorAware) {
        ((ExtensionAccessorAware) instance).setExtensionAccessor(extensionDirector);
    }
    if (extensionPostProcessors != null) {
        for (ExtensionPostProcessor processor : extensionPostProcessors) {
            instance = (T) processor.postProcessAfterInitialization(instance, name);
        }
    }
    return instance;
}

3.依赖注入

Dubbo的SPI相比于Java的SPI优点在于可以在Extension的适配类以及具体的Extension实例化之后进行依赖注入以及AOP,代码如下

代码语言:javascript
复制
private T injectExtension(T instance) {
    if (injector == 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.isAnnotationPresent(DisableInject.class)) {
                continue;
            }

            // When spiXXX implements ScopeModelAware, ExtensionAccessorAware,
            // the setXXX of ScopeModelAware and ExtensionAccessorAware does not need to be injected
            if (method.getDeclaringClass() == ScopeModelAware.class) {
                continue;
            }
            if (instance instanceof ScopeModelAware || instance instanceof ExtensionAccessorAware) {
                if (ignoredInjectMethodsDesc.contains(ReflectUtils.getDesc(method))) {
                    continue;
                }
            }

            Class<?> pt = method.getParameterTypes()[0];
            if (ReflectUtils.isPrimitives(pt)) {
                continue;
            }

            try {
                String property = getSetterProperty(method);
                Object object = injector.getInstance(pt, property);
                if (object != null) {
                    method.invoke(instance, object);
                }
            } catch (Exception e) {
                logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "",
                    "Failed to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(),
                    e);
            }
        }
    } catch (Exception e) {
        logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", e.getMessage(), e);
    }
    return instance;
}

代码比较简单,主要逻辑就是找到setter方法,然后通过injector获取到实例注入进去,injector是AdaptiveExtensionInjector的一个实例,获取注入的实例代码如下

代码语言:javascript
复制
public void initialize() throws IllegalStateException {
    ExtensionLoader<ExtensionInjector> loader = extensionAccessor.getExtensionLoader(ExtensionInjector.class);
    injectors = loader.getSupportedExtensions().stream()
        .map(loader::getExtension)
        .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
}

@Override
public <T> T getInstance(final Class<T> type, final String name) {
    return injectors.stream()
        .map(injector -> injector.getInstance(type, name))
        .filter(Objects::nonNull)
        .findFirst()
        .orElse(null);
}

就是通过injectors列表依次调用getInstance方法去获取,Dubbo中提供了三种默认的injector,如下

Injector
Injector

分别可以从注册到ScopeBeanFactory的Bean、其他的Extension、Spring中获取注入的对象

5.初始化,如果实现了Lifecycle接口,则调用其initialize方法

代码语言:javascript
复制
private void initExtension(T instance) {
    if (instance instanceof Lifecycle) {
        Lifecycle lifecycle = (Lifecycle) instance;
        lifecycle.initialize();
    }
}

这样ExtensionLoader及其对应的适配类就初始化完成了

2 获取Extension

获取Extension也是先从缓存中获取,获取不到就创建一个,所以直接看创建的代码,如下

代码语言:javascript
复制
private T createExtension(String name, boolean wrap) {
    Class<?> clazz = getExtensionClasses().get(name);
    if (clazz == null || unacceptableExceptions.contains(name)) {
        throw findException(name);
    }
    try {
        // 1.获取到对应Extension的Class对象实例化,和实例化适配类类似,可以通过ExtensionPostProcessor
        // 设置初始化之前和之后的操作,完了之后同样可以进行依赖注入
        T instance = (T) extensionInstances.get(clazz);
        if (instance == null) {
            extensionInstances.putIfAbsent(clazz, createExtensionInstance(clazz));
            instance = (T) extensionInstances.get(clazz);
            instance = postProcessBeforeInitialization(instance, name);
            injectExtension(instance);
            instance = postProcessAfterInitialization(instance, name);
        }
        // 2.如果需要AOP,则在此处进行,在原来的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);
                    boolean match = (wrapper == null) || ((ArrayUtils.isEmpty(
                        wrapper.matches()) || ArrayUtils.contains(wrapper.matches(),
                        name)) && !ArrayUtils.contains(wrapper.mismatches(), name));
                    if (match) {
                        instance = injectExtension(
                            (T) wrapperClass.getConstructor(type).newInstance(instance));
                        instance = postProcessAfterInitialization(instance, name);
                    }
                }
            }
        }

        // 3.最后调用lifecycle.initialize钩子函数,这里有个问题是如果加了代理之后的对象没有实现
        // Lifecycle接口,那么此处的initialize钩子函数不会执行
        initExtension(instance);
        return instance;
    } catch (Throwable t) {
        throw new IllegalStateException(
            "Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(),
            t);
    }
}

文末记录一个常见的面试题:说一说Java、Spring、Dubbo三者SPI机制的原理和区别

总的来说,Java的SPI实现的比较简单,并没有什么其它功能;Spring得益于自身的ioc和aop的功能,所以也没有实现太复杂的SPI机制,仅仅是对Java做了一点简化和优化;但是dubbo的SPI机制为了满足自身框架的使用要求,实现的功能就比较多,不仅将ioc和aop的功能集成到SPI机制中,还提供注入自适应、自动激活、按需加载、按需获取某个具体的实现等功能。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 获取ExtensionLoader
  • 2 获取Extension
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档