Dubbo源码分析-SPI实现源码解析(一)

Dubbo约定如下

  • SPI文件存储路径在META-INF/dubbo/internal目录下,并且文件名为接口名的全路径名接口包名+接口名

  • 每个SPI文件里面格式定义为:扩展名=具体类名,例如dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol

实现路径:

  • getExtensionLoader(Class<T> type) 就是为该接口new一个ExtensionLoader,然后缓存起来。

  • getAdaptiveExtension()获取一个拓展装饰类的对象,如果@Adaptive注解在类上就是一个装饰类,如果注释在方法上就是一个动态代理类,例如Protocol$Adaptive对象(如果是动态代理类,与dubbo中URL配合可以在运行时动态决定使用的实现类)。

  • getExtension(String name) 获取一个对象

这些方法都在ExtensionLoader中,ExtensionLoader是最核心的类,负责扩展点的加载和生命周期管理。

getExtensionLoader(Class<T> type)

getExtensionLoader方法 这是一个静态工厂方法,入参是一个可扩展的接口,返回一个该接口的ExtensionLoader实体类。通过这个实体类,可以根据name获得具体的扩展,也可以获得一个自适应扩展。

 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 interface!");
        }
        // 必须要有@SPI注解
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }
        // 从缓存中根据接口获取对应的ExtensionLoader
        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;
    }

如果缓存EXTENSION_LOADERS中没有获取到对应的ExtensionLoader则新建一个ExtensionLoader

ExtensionLoader结构体中有两个成员变量typeobjectFactory

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory =
                (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

type是我们传入的class类型,objectFactory是通过ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()获取的一个被@Adaptive标注的ExtensionFactory的实现类AdaptiveExtensionFactory

最后生成的ExtensionLoader会被放入EXTENSION_LOADERS中。

关于objectFactory的一些细节

  • objectFactory就是ExtensionFactory,它就是通过 ExtensionLoader.getExtensionLoader(ExtensionFactory.class)来实现的,但是它的objectFactory=null
    -objectFactory作用,它就是为dubbo的IOC提供所有对象。
  • objectFactory的IOC对象具体由ExtensionFactory的子类SpiExtensionFactorySpringExtensionFactory提供。

getAdaptiveExtension()

在对自适应拓展生成过程进行深入分析之前,我们先来看一下与自适应拓展息息相关的一个注解,即 @Adaptive 注解。该注解的定义如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Adaptive {
   String[] value() default {};
}

@Adaptive可以标注在类或者接口方法上

  1. 标注在类上getAdaptiveExtension会直接返回被@Adaptive注解实现类。

如上述所说的ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()返回的就是AdaptiveExtensionFactory

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

}
  1. 标注在接口的方法上,方法参数必定有URL或者传入的方法入参对象中有getUrl方法,dubbo动态编译生成如Protocol$Adaptive类,运行时会根据URL中的参数,决定使用哪个实现类。

Protocol获取自适应类的过程来解析getAdaptiveExtension执行过程

@SPI("dubbo")
public interface Protocol {
      ............. 删除多余代码
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
   ............删除多余代码
}
private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

getAdaptiveExtension 方法首先会检查缓存,缓存未命中,则调用 createAdaptiveExtension方法创建自适应拓展。

    public T getAdaptiveExtension() {
        // 从缓存中获取自适应拓展
        Object instance = cachedAdaptiveInstance.get();
        //双重检查
        if (instance == null) {
            if (createAdaptiveInstanceError == null) {
                synchronized (cachedAdaptiveInstance) {
                    instance = cachedAdaptiveInstance.get();
                    if (instance == null) {
                        try {
                        // 创建自适应拓展
                            instance = createAdaptiveExtension();
                           // 设置自适应拓展到缓存中  
                           cachedAdaptiveInstance.set(instance);
                        } catch (Throwable t) {
                            createAdaptiveInstanceError = t;
                            throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }

        return (T) instance;
    }

重要的方法是createAdaptiveExtension

    private T createAdaptiveExtension() {
        try {
         // 获取自适应拓展类,并通过反射实例化
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }

createAdaptiveExtension 主要执行三个动作:

  • 调用 getAdaptiveExtensionClass 方法获取自适应拓展 Class 对象
  • 通过反射进行实例化
  • 调用injectExtension 方法向拓展实例中注入依赖

接下来,分析 getAdaptiveExtensionClass 方法的逻辑。

private Class<?> getAdaptiveExtensionClass() {
    // 通过 SPI 获取所有的拓展类
    getExtensionClasses();
    // cachedAdaptiveClass缓存中存储的是有Adaptive注解的类,如AdaptiveExtensionFactory,若缓存不为空,则直接返回缓存
    if (cachedAdaptiveClass != null) {
        return cachedAdaptiveClass;
    }
    // 创建自适应拓展类
    return cachedAdaptiveClass = createAdaptiveExtensionClass();
}

getAdaptiveExtensionClass 方法同样包含了三个逻辑,如下:

  • 调用 getExtensionClasses 获取所有的拓展类
  • 检查缓存cachedAdaptiveClass,若缓存不为空,则直接返回缓存(cachedAdaptiveClass缓存的是被@Adaptive注解的类)
  • 若缓存为空,则调用 createAdaptiveExtensionClass 创建自适应拓展类

首先从第一个逻辑说起,getExtensionClasses 这个方法用于获取某个接口的所有实现类其实就是解析META-INF/dubbo/internal/META-INF/dubbo/,META-INF/services/里面放置的文件。比如该方法可以获取 Protocol 接口的DubboProtocol、HttpProtocol、InjvmProtocol 等实现类。在获取实现类的过程中,如果某个某个实现类被 Adaptive 注解修饰了,如AdaptiveExtensionFactory,那么该类就会被赋值给 cachedAdaptiveClass 变量。此时,上面步骤中的第二步条件成立(缓存不为空),直接返回 cachedAdaptiveClass。如果所有的实现类均未被 Adaptive 注解修饰,那么执行第三步逻辑,创建自适应拓展类:

getExtensionClasses

先分析第一个逻辑getExtensionClasses获取所有的拓展类,代码如下:

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

getExtensionClasses 主要执行两个动作:

  • 检查缓存cachedClasses中是否有值。
  • 如果没有则loadExtensionClasses()获取所有的拓展类。
  • 最后将classes放入cachedClasses中。

接着看loadExtensionClasses()是如何获取所有的扩展类的。

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

loadExtensionClasses 主要做两件事情。

  • META-INF/dubbo/internal/,从META-INF/dubbo/(DubboSPI路径)和META-INF/services/(JDK SPI路径)获取所有扩展。

继续看loadDirectory方法

 private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
        String fileName = dir + type;
        try {
            Enumeration<java.net.URL> urls;
            ClassLoader classLoader = findClassLoader();
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    java.net.URL resourceURL = urls.nextElement();
                    //加载文件资源,其实就是SPI接口实现
                    loadResource(extensionClasses, classLoader, resourceURL);
                }
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", description file: " + fileName + ").", t);
        }
    }

loadDirectory 主要包含三个逻辑:

  • 获取一个类加载器。
  • 通过加载器,加载文件名为META-INF/dubbo/internal/ ,META-INF/dubbo/,META-INF/services/下的资源,如org.apache.dubbo.rpc.Protocol
  • 遍历urls获取META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol中的SPI实现。

解析的逻辑在loadResource代码如下:

 private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) {
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8"));
            try {
                String line;
                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();
                                line = line.substring(i + 1).trim();
                            }
                            if (line.length() > 0) {
                                loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name);
                            }
                        } 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);
                        }
                    }
                }
            } finally {
                reader.close();
            }
        } catch (Throwable t) {
            logger.error("Exception when load extension class(interface: " +
                    type + ", class file: " + resourceURL + ") in " + resourceURL, t);
        }
    }

loadResource 逻辑比较简单就是一行行读取,然后获取扩展名和扩展实现类,然后调用loadClass方法

继续看loadClass方法

    private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
                           boolean overridden) throws NoSuchMethodException {
        //判断扩展clazz,是不是type的子类
        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.");
        }
        //判断Adaptive是否注释在类上。如果是且`cachedAdaptiveClass`为空则为`cachedAdaptiveClass`赋值。
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz, overridden);
        } else if (isWrapperClass(clazz)) {
            //判断clazz构造函数中是否有其父类接口,在Protocol只有ProtocolFilterWrapper和ProtocolListenerWrapper构造函数中有Protocol属性
            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)) {
                //判断clazz是否有Activate注解
                cacheActivateClass(clazz, names[0]);
                for (String n : names) {
                    cacheName(clazz, n);
                    saveInExtensionClass(extensionClasses, clazz, n, overridden);
                }
            }
        }

执行loadClass方法主要是为以下几个属性赋值

  1. cachedAdaptiveClass 如果这个类class含有adaptive注解就赋值,例如 AdaptiveExtensionFactory ,而例如Protocol在这个环节是没有的。
    2.cachedWrapperClasses 只有当该class无adaptive注解,并且构造函数包含目标接口(type)类型 例如Protocol里面的spi就只有ProtocolFilterWapperProtocolListenerWrapper能命中

  2. cachedActivates 被Activate注解的类

  3. 剩下的类就存储在cachedNamesextensionClassesextensionClasses是从外部传入的,之后它的值会放入cachedClasses缓存中。

至此加载SPI扩展的方法就执行完了。

createAdaptiveExtensionClass

回到getAdaptiveExtensionClass方法,像Protocol所有的实现类均未被 Adaptive 注解修饰,就会调用createAdaptiveExtensionClass生成一个动态类。相关代码如下

private Class<?> createAdaptiveExtensionClass() {
        //构建自适应拓展代码
        String code = createAdaptiveExtensionClassCode();
        ClassLoader classLoader = findClassLoader();
        //获取编译器实现类
        org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        // 编译代码,生成 Class
        return compiler.compile(code, classLoader);
    }

createAdaptiveExtensionClass 方法用于生成自适应拓展类,该方法首先会生成自适应拓展类的源码,然后通过 Compiler 实例(Dubbo 默认使用 javassist 作为编译器)编译源码,得到代理类 Class 实例。生成动态类的代码比较复杂,但是所有的动态类都是根据一个模板来生成的。

package <扩展点接口所在包>

public class <扩展点接口名>$Adpative implements <扩展点接口>{
   public <有@Adaptive注解的接口方法>(<方法参数>){
          if(是否有URL类型参数?)  使用该URL参数
          else if(是否方法类型上有URL属性) 使用该URL属性
          #<else 在加载扩展点生成自适应扩展点类时抛异常,即加载扩展点失败!>
          
          if(获取的URL == null){
             throw new IllegalArgumentException("url = null")
          }
          
          根据@Adaptive注解上声明的Key的顺序,从URL获取Value,作为实际扩展点名。
          如URL没有Value,则使用缺省扩展点实现。如没有扩展点,throw new IllegalArgumentException("Fail to get extension");
          
          扩展点实现调用方法,并返回结果
   }
   
   public <没有@Adaptive注解的接口方法>(<方法参数>){
      throw new UnsupportedOperationException("is not adaptive method!")
   }

}

下面是动态生成Protocol$Adpative的代码

package org.apache.dubbo.rpc;

import org.apache.dubbo.common.extension.ExtensionLoader;

public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
    private static final org.apache.dubbo.common.logger.Logger logger = org.apache.dubbo.common.logger.LoggerFactory
            .getLogger(ExtensionLoader.class);
    private java.util.concurrent.atomic.AtomicInteger count = new java.util.concurrent.atomic.AtomicInteger(0);

    public void destroy() {
        throw new UnsupportedOperationException(
                "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(
                "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.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("Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url("
                    + url.toString() + ") use keys([protocol])");
        org.apache.dubbo.rpc.Protocol extension = null;
        try {
            extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader
                    .getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        } catch (Exception e) {
            if (count.incrementAndGet() == 1) {
                logger.warn(
                        "Failed to find extension named " + extName
                                + " for type org.apache.dubbo.rpc.Protocol, will use default extension dubbo instead.",
                        e);
            }
            extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader
                    .getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension("dubbo");
        }
        return extension.refer(arg0, arg1);
    }

    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("Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url("
                    + url.toString() + ") use keys([protocol])");
        org.apache.dubbo.rpc.Protocol extension = null;
        try {
            extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader
                    .getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
        } catch (Exception e) {
            if (count.incrementAndGet() == 1) {
                logger.warn(
                        "Failed to find extension named " + extName
                                + " for type org.apache.dubbo.rpc.Protocol, will use default extension dubbo instead.",
                        e);
            }
            extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader
                    .getExtensionLoader(org.apache.dubbo.rpc.Protocol.class)
                    .getExtension("dubbo");
        }
        return extension.export(arg0);
    }
}

生成动态类之后,在createAdaptiveExtension方法会通过反射生成实例,并调用injectExtension方法进入IOC反转控制模式,实现依赖注入。

private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }

到此,关于getAdaptiveExtension自适应拓展的原理,实现就分析完了。有关动态编译和依赖注入的原理下一章再分析。

getExtension(String name)

上一节我们分析了Dubbo自适应扩展机制,下面我们从 ExtensionLoader 的 getExtension 方法作为入口,对拓展类对象的获取过程进行详细的分析。

  public T getExtension(String name) {
        
        if (StringUtils.isEmpty(name)) {
            throw new IllegalArgumentException("Extension name == null");
        }
        // 获取默认的拓展实现类
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
         // Holder,用于持有目标对象
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<Object>());
            holder = cachedInstances.get(name);
        }
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

上面了逻辑比较简单首先判断是否使用默认扩展,如果不是查找缓存,若未命中则调用createExtension创建拓展对象。

 private T createExtension(String name) {
        //1. 在上一节中我们也讲述过这个方法,它的作用就是从配置文件中加载所有的拓展类
        Class<?> clazz = getExtensionClasses().get(name);
        if (clazz == null) {
            throw findException(name);
        }
        try {
        // 2. 查找缓存EXTENSION_INSTANCES中,是否有扩展实例,没有则通过反射创建拓展对象。
            T instance = (T) EXTENSION_INSTANCES.get(clazz);
            if (instance == null) {
            // 通过反射创建实例
                EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            // 向实例中注入依赖
            injectExtension(instance);
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;
            if (CollectionUtils.isNotEmpty(wrapperClasses)) {
               // 循环创建 Wrapper 实例
                for (Class<?> wrapperClass : wrapperClasses) {
                   // 将当前 instance 作为参数传给 Wrapper 的构造方法,并通过反射创建 Wrapper 实例。
                // 然后向 Wrapper 实例中注入依赖,最后将 Wrapper 实例再次赋值给 instance 变量
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
                    type + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }

createExtension 方法的逻辑稍复杂一下,主要执行以下四个步骤:

  1. 通过 getExtensionClasses 获取所有的拓展类(在上一节中我们已经讲解过这个方法)。
  2. 查找缓存EXTENSION_INSTANCES中,是否有扩展实例,没有则通过反射创建拓展对象。
  3. 将拓展对象包裹在相应的 Wrapper 对象中,以Protocol为例,它最终被getExtensionClasses解析出的wrapper类ProtocolFilterWrapperProtocolListenerWrapper包裹,
  4. 调用injectExtension向拓展对象中注入依赖。

第一个步骤是加载拓展类的关键,第三和第四个步骤是 Dubbo IOC 与 AOP 的具体实现。

有关getExtensionClasses分析,请看上一节,下面着重分析第三和第四步骤,先分析

Dubbo IOC

  private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
            // 遍历目标类的所有方法
                for (Method method : instance.getClass().getMethods()) {
                 // 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public
                    if (method.getName().startsWith("set")
                            && method.getParameterTypes().length == 1
                            && Modifier.isPublic(method.getModifiers())) {
                            //判断是否需要自动注入
                        if (method.getAnnotation(DisableInject.class) != null) {
                            continue;
                        }
                         // 获取 setter 方法参数类型
                        Class<?> pt = method.getParameterTypes()[0];
                        if (ReflectUtils.isPrimitives(pt)) {
                            continue;
                        }
                        try {
                          // 获取属性名,比如 setName 方法对应属性名 name
                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                            // 从 ObjectFactory 中获取依赖对象
                            Object object = objectFactory.getExtension(pt, property);
                            if (object != null) {
                             // 通过反射调用 setter 方法设置依赖
                                method.invoke(instance, object);
                            }
                        } catch (Exception e) {
                            logger.error("fail to inject via method " + method.getName()
                                    + " of interface " + type.getName() + ": " + e.getMessage(), e);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }

在上面代码中,objectFactory 变量的类型为 AdaptiveExtensionFactoryAdaptiveExtensionFactory 内部维护了一个 ExtensionFactory 列表,用于存储其他类型的 ExtensionFactory。Dubbo 目前提供了两种 ExtensionFactory,分别是 SpiExtensionFactorySpringExtensionFactory。前者用于创建自适应的拓展,后者是用于从 Spring 的 IOC 容器中获取所需的拓展。Dubbo的IOC反转控制,就是从spi和spring里面提取对象赋值。

Dubbo AOP

下面只截取了createExtension部分代码

  if (CollectionUtils.isNotEmpty(wrapperClasses)) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }

Protocol中wrapperClass类ProtocolFilterWrapperProtocolListenerWrapper这两个类中的构造函数都有Protocol参数,

public class ProtocolFilterWrapper implements Protocol {

    private final Protocol protocol;

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

Dubbo AOP 获取wrapperClass然通过反射生成对象,进行AOP,然后调用injectExtension进行IOC.最后将值重新赋给instance,所以getExtension方法最后返回的是一个wrapper类。

好了Dubbo SPI实现源码解析讲完了,本篇文章就先到这里了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342