美文网首页
Dubbo系列--SPI的实现原理《一》

Dubbo系列--SPI的实现原理《一》

作者: Teddy_b | 来源:发表于2023-11-21 15:04 被阅读0次

在Dubbo的源码中存在大量的SPI代码,Dubbo用这种方式来获取接口的实现类,几个好处:

  • 面向接口编程

  • 代码扩展性好,可以自定义的点很多

  • 类对象在真正需要的时候再创建

主要的实现都在ExtensionLoader这个类中,每个接口都会对应一个ExtensionLoader对象,用于加载这个接口的实现类

@SPI(value = "dubbo", scope = ExtensionScope.FRAMEWORK)
public interface Protocol {
...
}

需要通过SPI方式加载的接口,需要用注解@SPI标志,value对应的是默认值,scope对应的是这个接口的作用域,这里可以先忽略

SPI流程

加载Class

Dubbo中存在三种LoadingStrategy,主要区别是加载的目录不同,我们主要看下这个实现类

public class DubboInternalLoadingStrategy implements LoadingStrategy {

    @Override
    public String directory() {
        return "META-INF/dubbo/internal/";
    }
    ...
}

这个加载的是META-INF/dubbo/internal/目录下的文件:

image.png

如需要加载Dubbo中有哪些协议Protocol的实现类,那么这里type=org.apache.dubbo.rpc.Protocol,加载的就是这个org.apache.dubbo.rpc.Protocol文件

通过类加载器加载项目中所有的META-INF/dubbo/internal/目录下的这个org.apache.dubbo.rpc.Protocol文件(包括jar包)

private void loadDirectoryInternal(Map<String, Class<?>> extensionClasses,
                                       LoadingStrategy loadingStrategy, String type)
        throws InterruptedException {
        String fileName = loadingStrategy.directory() + type;
        try {
            ...

            Map<ClassLoader, Set<java.net.URL>> resources = ClassLoaderResourceLoader.loadResources(
                fileName, classLoadersToLoad);
            resources.forEach(((classLoader, urls) -> {
                loadFromClass(extensionClasses, loadingStrategy.overridden(), urls, classLoader,
                    loadingStrategy.includedPackages(), loadingStrategy.excludedPackages(),
                    loadingStrategy.onlyExtensionClassLoaderPackages());
            }));
        }
      ...
    }

Dubbo内置了这些实现类

filter=org.apache.dubbo.rpc.cluster.filter.ProtocolFilterWrapper
qos=org.apache.dubbo.qos.protocol.QosProtocolWrapper
registry=org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol
service-discovery-registry=org.apache.dubbo.registry.integration.RegistryProtocol
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=org.apache.dubbo.rpc.support.MockProtocol
serializationwrapper=org.apache.dubbo.rpc.protocol.ProtocolSerializationWrapper
securitywrapper=org.apache.dubbo.rpc.protocol.ProtocolSecurityWrapper
invokercount=org.apache.dubbo.rpc.protocol.InvokerCountWrapper

dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol
rest=org.apache.dubbo.rpc.protocol.rest.RestProtocol
tri=org.apache.dubbo.rpc.protocol.tri.TripleProtocol
grpc=org.apache.dubbo.rpc.protocol.tri.GrpcProtocol

这样就可以把这些实现类的Class对象加载到内存中进行缓存了

实例化Class

在需要实例化的时候

private T createExtension(String name, boolean wrap) {
        Class<?> clazz = getExtensionClasses().get(name);
        ...
        try {
            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);
            }

            ...
            initExtension(instance);
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException(
                "Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(),
                t);
        }
    }

实例化分为几步:

  • 反射调用构造器创建对象

  • 前置处理postProcessBeforeInitialization,目前为空

  • 属性注入injectExtension,此时会遍历所有的set方法,由set方法推导出属性名称和属性类型,然后再通过SPI方式获取这个类型的实现类,再注入到属性中

  • 后置处理postProcessAfterInitialization,主要是注入Dubbo的模块属性

  • 初始化initExtension,如果实现了Lifecycle接口,调用initialize()方法

特殊的实例化

Adaptive

ExtensionLoader中可以通过这个方法获取接口的Adaptive类

private Class<?> getAdaptiveExtensionClass() {
        getExtensionClasses();
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        return cachedAdaptiveClass = createAdaptiveExtensionClass();
    }

对应着两种加载方式:

  • 通过再实现类上指定@Adaptive注解:如加载org.apache.dubbo.common.extension.ExtensionInjector的Adaptive类

    @Adaptive
    public class AdaptiveExtensionInjector implements ExtensionInjector, Lifecycle {
    ...
    }
    
  • 所有实现类中均不存在@Adaptive注解时,动态生成一个实现类作为Adaptive类;此时需要接口中存在@Adaptive注解的方法,并且方法的入参中直接或者间接存在URL对象,如加载org.apache.dubbo.rpc.Protocol的Adaptive类,由于不存在@Adaptive注解类,因此会动态生成一个Adaptive类:

package org.apache.dubbo.rpc;

import org.apache.dubbo.rpc.model.ScopeModel;
import org.apache.dubbo.rpc.model.ScopeModelUtil;

public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {

          public void destroy()  {
                    throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface         org.apache.dubbo.rpc.Protocol is not adaptive method!");
          }

          public int getDefaultPort()  {
                    throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
          }

          public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
                      if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
                      if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
                      org.apache.dubbo.common.URL url = arg0.getUrl();
                      String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
                      if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
                       ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), org.apache.dubbo.rpc.Protocol.class);
                       org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)scopeModel
                                                                   .getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
                       return extension.export(arg0);
            }

            public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
                        if (arg1 == null) throw new IllegalArgumentException("url == null");
                        org.apache.dubbo.common.URL url = arg1;
                        String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
                        if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
                        ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), org.apache.dubbo.rpc.Protocol.class);
                        org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)scopeModel
                                                                  .getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
                        return extension.refer(arg0, arg1);
          }

          public java.util.List getServers()  {
                        throw new UnsupportedOperationException("The method public default java.util.List org.apache.dubbo.rpc.Protocol.getServers() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
          }
}

可以看到这个动态生成的Adaptive类只是一个代理类,就是根据URL中指定的协议名称、或者默认实现,代理到协议对应的具体实现类或者默认实现

再如ProxyFactory生成的Adaptive类

package org.apache.dubbo.rpc;

import org.apache.dubbo.rpc.model.ScopeModel;
import org.apache.dubbo.rpc.model.ScopeModelUtil;

public class ProxyFactory$Adaptive implements org.apache.dubbo.rpc.ProxyFactory {


         public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
                if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
                if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
                org.apache.dubbo.common.URL url = arg0.getUrl();
                String extName = url.getParameter("proxy", "javassist");
                if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
                ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), org.apache.dubbo.rpc.ProxyFactory.class);
                org.apache.dubbo.rpc.ProxyFactory extension =  (org.apache.dubbo.rpc.ProxyFactory)scopeModel
                                                              .getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
                return extension.getProxy(arg0);
          }


          public java.lang.Object getProxy(org.apache.dubbo.rpc.Invoker arg0, boolean arg1) throws org.apache.dubbo.rpc.RpcException {
                  if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
                  if (arg0.getUrl() == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
                  org.apache.dubbo.common.URL url = arg0.getUrl();
                  String extName = url.getParameter("proxy", "javassist");
                  if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
                  ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), org.apache.dubbo.rpc.ProxyFactory.class);
                  org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)scopeModel
                                                                 .getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
                  return extension.getProxy(arg0, arg1);
          }
            

            public org.apache.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, org.apache.dubbo.common.URL arg2) throws org.apache.dubbo.rpc.RpcException {
                    if (arg2 == null) throw new IllegalArgumentException("url == null");
                    org.apache.dubbo.common.URL url = arg2;
                    String extName = url.getParameter("proxy", "javassist");
                    if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.ProxyFactory) name from url (" + url.toString() + ") use keys([proxy])");
                    ScopeModel scopeModel = ScopeModelUtil.getOrDefault(url.getScopeModel(), org.apache.dubbo.rpc.ProxyFactory.class);
                    org.apache.dubbo.rpc.ProxyFactory extension = (org.apache.dubbo.rpc.ProxyFactory)scopeModel
                                                                   .getExtensionLoader(org.apache.dubbo.rpc.ProxyFactory.class).getExtension(extName);
                    return extension.getInvoker(arg0, arg1, arg2);
            }
}

生成的模式基本上都是一样的,我们可以自己实现相应的接口,然后加上@Adaptive注解,那么就会优先加载我们自己的实现类;

如果不存在Adaptive类,那么将会自动生成上述的代理类,来执行Dubbo默认的实现类,如ProxyFactory的默认实现是JavassistProxyFactory;

Protocol的默认实现是根据协议而来的,协议不同时会对应不同的默认实现,如果没有指定协议时,则协议默认是dubbo,那么对应的实现类就是DubboProtocol

Wrapper

Wrapper顾名思义就是再对象外面再包装一层

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) 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);
            }

           
            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);
                        }
                    }
                }
            }

            // Warning: After an instance of Lifecycle is wrapped by cachedWrapperClasses, it may not still be Lifecycle instance, this application may not invoke the lifecycle.initialize hook.
            initExtension(instance);
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException(
                "Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(),
                t);
        }
    }

相比普通的实例化过程,Wrapper对象的实例化过程就多了一步指定wrap=true

再SPI方式加载实现类的时候,会特殊处理这种实现类

protected boolean isWrapperClass(Class<?> clazz) {
        Constructor<?>[] constructors = clazz.getConstructors();
        for (Constructor<?> constructor : constructors) {
            if (constructor.getParameterTypes().length == 1 && constructor.getParameterTypes()[0] == type) {
                return true;
            }
        }
        return false;
    }

这种实现类存在一个构造器,构造器只有一个入参,并且参数类型就是这个接口类型(类比下Java中的IO类),看个具体的例子:

public class ProtocolFilterWrapper implements Protocol {
    private final Protocol protocol;

    public ProtocolFilterWrapper(Protocol protocol) {
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }
}

这个org.apache.dubbo.rpc.Protocol的实现类就有一个构造函数,它只有一个入参,并且类型就是Protocol,这种就会被识别为Wrapper类,并加入到wrapperClassesList这个缓存中,用于对具体的对象进行包装,如果包装类比较多的话,会进行简单的排序,然后形成这种大肠包小肠的效果

image.png

BeanFactory

Dubbo中除了通过SPI方式获取对象,还有一种方式,是通过BeanFactory获取对象,这一点和Spring中的类似

再Dubbo初始化的时候,就会创建一个BeanFactory

protected void initialize() {
        synchronized (instLock) {
            this.extensionDirector = new ExtensionDirector(parent != null ? parent.getExtensionDirector() : null, scope, this);
            this.extensionDirector.addExtensionPostProcessor(new ScopeModelAwareExtensionProcessor(this));
            this.beanFactory = new ScopeBeanFactory(parent != null ? parent.getBeanFactory() : null, extensionDirector);

            // Add Framework's ClassLoader by default
            ClassLoader dubboClassLoader = ScopeModel.class.getClassLoader();
            if (dubboClassLoader != null) {
                this.addClassLoader(dubboClassLoader);
            }
        }
    }

然后通过一系列的Initializer再初始化的时候往BeanFactory中注册Bean,具体包括哪些Initializer呢?也是通过SPI方式去加载实现类的,如:

public class Fastjson2ScopeModelInitializer implements ScopeModelInitializer {

    @Override
    public void initializeFrameworkModel(FrameworkModel frameworkModel) {
        ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory();
        beanFactory.registerBean(Fastjson2CreatorManager.class);
        beanFactory.registerBean(Fastjson2SecurityManager.class);
    }
}
注册Bean
public <T> T getOrRegisterBean(String name, Class<T> type) {
        T bean = getBean(name, type);
        if (bean == null) {
            // lock by type
            synchronized (type) {
                bean = getBean(name, type);
                if (bean == null) {
                    bean = createAndRegisterBean(name, type);
                }
            }
        }
        registeredClasses.add(type);
        return bean;
    }

private <T> T createAndRegisterBean(String name, Class<T> clazz) {
        checkDestroyed();
        T instance = getBean(name, clazz);
        if (instance != null) {
            throw new ScopeBeanException("already exists bean with same name and type, name=" + name + ", type=" + clazz.getName());
        }
        try {
            instance = instantiationStrategy.instantiate(clazz);
        } catch (Throwable e) {
            throw new ScopeBeanException("create bean instance failed, type=" + clazz.getName(), e);
        }
        registerBean(name, instance);
        return instance;
    }

注册Bean包括几步:

  • 通过双重判断方式同步的获取Bean,确保不会重复创建Bean

  • Bean不存在的时候通过反射创建一个Bean

  • 将Bean加入到缓存

  • 初始化一些Dubbo的模块及SPI获取方式等基础属性,其它属性不会进行初始化,把SPI方式都给你了,需要啥自己去通过SPI方式获取吧,授人以鱼不如授人以渔

总结

再Dubbo中这两种获取对象的方式很多,也是Dubbo中最主要的两种获取对象的方式了,留个印象对理解Dubbo有好处

参考

相关文章

网友评论

      本文标题:Dubbo系列--SPI的实现原理《一》

      本文链接:https://www.haomeiwen.com/subject/ztpswdtx.html