Dubbo系列五之ExtensionLoader

2024-09-11 18:32:57 浏览数 (2)

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,如下

InjectorInjector

分别可以从注册到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机制中,还提供注入自适应、自动激活、按需加载、按需获取某个具体的实现等功能。

0 人点赞