ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Dubbo-SPI机制

2022-08-24 09:00:46  阅读:243  来源: 互联网

标签:Dubbo name 扩展 SPI 机制 null type 加载


前言

SPI全称为Service Provider Interface,是Java提供的一种服务发现机制。SPI的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。 正因此特性,我们可以很容易的通过SPI机制为我们的程序提供拓展功能。今天文章就从Java SPI到Dubbo的SPI,让我们看看Dubbo做了扩展。

Java SPI

实现

  1. 定义一个接口和接口的实现;
/**
 * 动物
 *
 * @author wangtongzhou
 * @since 2022-06-12 11:26
 */
public interface Animal {

    String getName();
}


/**
 * 猫
 *
 * @author wangtongzhou
 * @since 2022-06-12 11:37
 */
public class Cat implements Animal {
    @Override
    public String getName() {
        System.out.println("我是一只猫");
        return "猫";
    }
}
  1. 在resources目录下创建META-INF/services目录;
  2. 在对应的目录下创建接口全路径的文件;
  3. 添加文件内容为实现接口的全路径;
  4. 使用ServiceLoader加载类相关内容;
/**
 * spi
 *
 * @author wangtongzhou
 * @since 2022-06-12 11:47
 */
public class Test {

    public static void main(String[] args) {
        ServiceLoader<Animal> animals=ServiceLoader.load(Animal.class);
        animals.forEach(Animal::getName);
    }
}

使用场景

  1. 数据库驱动加载接口实现类的加载,JDBC加载不同类型数据库的驱动;
  2. 日志门面接口实现类加载,SLF4J加载不同提供商的日志实现类
  3. SpringBoot中的自动配置原理,即SpringFactoriesLoader类读取配置文件 spring.factories 中的配置文件的这种方式是一种 SPI 的思想;
  4. Dubbo中也大量使用SPI的方式实现框架的扩展,不过它对Java提供的原生SPI做了封装;

总结

优点

使用Java SPI机制的优势是实现解耦,使得第三方服务模块的装配控制的逻辑与调用者的业务代码分离,而不是耦合在一起。应用程序可以根据实际业务情况启用框架扩展或替换框架组件。

缺点
  1. JDK 标准的 SPI 会一次性加载实例化扩展点的所有实现,也就是接口的实现类全部加载并实例化一遍。如果你并不想用某些实现类,它也被加载并实例化了,这就造成了浪费;
  2. 获取某个实现类的方式不够灵活,只能通过Iterator形式获取,不能根据某个参数来获取对应的实现类;

Dubbo SPI

为什么学习Dubbo SPI

Dubbo的SPI层所有的接口都可以基于Dubbo框架做定制性的二次开发,扩展功能。比如负载均衡、协议、注册中心等等都可以扩展,这个是Dubbo强大和灵活的所在。

Dubbo SPI优势

  1. 按需加载,Dubbo SPI配置文件采用KV格式存储,key 被称为扩展名,当我们在为一个接口查找具体实现类时,可以指定扩展名来选择相应的扩展实现,只实例化这一个扩展实现即可,无须实例化 SPI 配置文件中的其他扩展实现类,避免资源浪费,此外通过 KV 格式的 SPI 配置文件,当我们使用的一个扩展实现类所在的 jar 包没有引入到项目中时,Dubbo SPI 在抛出异常的时候,会携带该扩展名信息,而不是简单地提示扩展实现类无法加载。这些更加准确的异常信息降低了排查问题的难度,提高了排查问题的效率。;
  2. 增加扩展类的 IOC 能力,Dubbo 的扩展能力并不仅仅只是发现扩展服务实现类,而是在此基础上更进一步,如果该扩展类的属性依赖其他对象,则 Dubbo 会自动的完成该依赖对象的注入功能;
  3. 增加扩展类的 AOP 能力,Dubbo 扩展能力会自动的发现扩展类的包装类,完成包装类的构造,增强扩展类的功能;

使用

  1. 自定义SPI注解接口以及实现对应接口;
/**
 * 动物
 *
 * @author wangtongzhou
 * @since 2022-06-12 11:26
 */
@SPI
public interface Animal {

    String getName();
}


/**
 * 猫
 *
 * @author wangtongzhou
 * @since 2022-06-12 11:37
 */
public class Cat implements Animal {
    @Override
    public String getName() {
        System.out.println("我是一只猫");
        return "猫";
    }
}

  1. 需要在 resource 目录下配置 META-INF/dubbo 或者 META-INF/dubbo/internal 或者 META-INF/services,并基于 SPI 接口去创建一个文件;
  2. 文件名称和接口名称保持一致,文件内容与Java SPI有差异,内容是KEY 对应 Value;
  3. 使用ExtensionLoader调用;
/**
 * 测试
 *
 * @author wangtongzhou
 * @since 2022-06-12 17:32
 */
public class Test {
    public static void main(String[] args) {
        Animal animal= ExtensionLoader.getExtensionLoader(Animal.class).getExtension("cat");
        System.out.println(animal.getName());
    }
}

源码解析

通过上面的使用案例来看,Dubbo的某个接口被@SPI修饰的时候,就表示该接口是扩展接口,是通过ExtensionLoader.getExtensionLoader完成ExtensionLoader的初始化,然后再通过ExtensionLoader 的 getExtension 方法获取拓展类对象。接下来我们来整体看下@SPI加载过程。

Animal animal= ExtensionLoader.getExtensionLoader(Animal.class).getExtension("cat");

getExtensionLoader

首先是getExtensionLoader的方法,完成ExtensionLoader创建,该方法内部完成一些关于type的校验,然后根据EXTENSION_LOADERS缓存获取ExtensionLoader的实例,EXTENSION_LOADERS集合缓存了全部 ExtensionLoader 实例,其中的 Key 为扩展接口,Value 为加载其扩展实现的 ExtensionLoader 实例,如果不存在缓存的时候,则主动创建一个。

    @SuppressWarnings("unchecked")
    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!");
        }
        //必须@SPI
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
        }
        //初始化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;
    }

接下来我们看一下ExtensionLoader构造函数的初始化,关于ExtensionLoader是一个私有的构造函数,如果当前的 type=ExtensionFactory,那么objectFactory=null, 否则会创建一个自适应扩展点给objectFacotry,这里介绍一下objectFactory,objectFactory有是三种实现,如下图: 其中AdaptiveExtensionFactory 不实现任何具体的功能,而是用来适配 ExtensionFactory 的 SpiExtensionFactory 和 SpringExtensionFactory 这两种实现。前者用于创建自适应的拓展,后则是到 Spring 的 IOC 容器中获取所需拓展。AdaptiveExtensionFactory 会根据运行时的一些状态来选择具体调用 ExtensionFactory 的哪个实现。 关于objectFactory的实现为AdaptiveExtensionFactory,AdaptiveExtensionFactory 内部维护了一个ExtensionFactory 列表,用于存储SpiExtensionFactory 和 SpringExtensionFactory的实现。

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        //type为普通接口时,objectFactory实现为AdaptiveExtensionFactory
        //实现dubbo spi ioc功能
        objectFactory =
                (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

关于这三者实现如下,不同点在于AdaptiveExtensionFactory类上面增加@Adaptive注解,至于为什么实现是AdaptiveExtensionFactory,应该有两处决定的一个是LoadingStrategy对象,该对象进行排序,保障了DubboExternalLoadingStrategy目录下的文件优先加载,另外一个就是ExtensionFactory的配置文件,AdaptiveExtensionFactory的配置在前。

@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {

    //存储其他类型的 ExtensionFactory对象
    private final List<ExtensionFactory> factories;

    public AdaptiveExtensionFactory() {
        //获取拓展点接口ExtensionFactory对应的ExtensionLoader
        //每个拓展点接口都有一个对应的ExtensionLoader对象
        ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
        List<ExtensionFactory> list = new ArrayList<ExtensionFactory>();
        //getSupportedExtensions得到所有非@Adaptive标注
        //也非ExtensionFactory包装类的实现类的配置名
        //getExtension通过配置名得到对象
        for (String name : loader.getSupportedExtensions()) {
            list.add(loader.getExtension(name));
        }
        factories = Collections.unmodifiableList(list);
    }

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        //遍历所有其它ExtensionFactory实现类,调用它们的getExtension
        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}

public class SpiExtensionFactory implements ExtensionFactory {

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            //获取对应type的ExtensionLoader实例
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
            if (!loader.getSupportedExtensions().isEmpty()) {
                //获取适配器实现
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }

}


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) {

        //type必须是@SPI
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            return null;
        }
        //从Spring容器中获取Bean
        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;
    }

}

getAdaptiveExtension

先来看下整体的时序图,重点看createAdaptiveExtension, createAdaptiveExtension方法内部有个核心的方法getAdaptiveExtensionClass,关于这个方法我们可以看到分成两步,一个种是通过getExtensionClasses加载,另外一种通过编译器生成代码;

    @SuppressWarnings("unchecked")
    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();
    }
getExtensionClasses

首先来看下getExtensionClasses方法,该方法获取所有的扩展类,主要是通过调用loadExtensionClasses来实现的,根据扩展名从 cachedClasses 缓存中获取扩展实现类。如果 cachedClasses 未初始化,则会扫描 SPI 目录获取查找相应的 SPI 配置文件,然后加载其中的扩展实现类,最后将扩展名和扩展实现类的映射关系记录到 cachedClasses 缓存中。 在Dubbo中按照SPI配置文件的用途,将其分成了三类目录。 ● META-INF/services/ 目录:该目录下的 SPI 配置文件用来兼容 JDK SPI ; ● META-INF/dubbo/ 目录:该目录用于存放用户自定义 SPI 配置文件; ● META-INF/dubbo/internal/ 目录:该目录用于存放 Dubbo 内部使用的 SPI 配置文件;

    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从配置文件中加载扩展类信息,cacheDefaultExtensionName逻辑是在把SPI注解中的默认值放到缓存中去,loadDirectory从一个配置文件中扩展类信息;这里介绍一下Loading这个对象,该对象有是三个默认实现,采用的是Java SPI方式加载,同时都继承了Prioritized优先级接口,整体的继承图如下: 此处三个对象也对应了加载的目录的地址:

    private Map<String, Class<?>> loadExtensionClasses() {
        //缓存@SPI组件信息
        cacheDefaultExtensionName();

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


        //加载 META-INF/dubbo/internal META-INF/dubbo META-INF/services/
        //对应目录下的配置信息
        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 void cacheDefaultExtensionName() {
        //获取注解SPI接口
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        if (defaultAnnotation == null) {
            return;
        }
        //如果存在默认值 将值赋值给cachedDefaultName
        String value = defaultAnnotation.value();
        if ((value = value.trim()).length() > 0) {
            //@SPI value 只能是一个 此处相当于在进行参数校验
            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];
            }
        }
    }
loadResource

loadResource加载配置文件中的内容,主要的逻辑就是跳过“#”注释的内容,根据配置文件中的key=value的形式去分割,然后去加载value通过反射加载类,然后调用 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和实现类
                                name = line.substring(0, i).trim();
                                clazz = line.substring(i + 1).trim();
                            } else {
                                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);
        }
    }

loadClass 方法操作了不同的缓存,比如 cachedAdaptiveClass、cachedWrapperClasses 和 cachedNames 等等。

    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.");
        }
        //判断类是否加载Adaptive注解
        if (clazz.isAnnotationPresent(Adaptive.class)) {
            cacheAdaptiveClass(clazz, overridden);
            //是否是扩展类,是的话就加入 cachedWrapperClasses 属性
        } else if (isWrapperClass(clazz)) {
            cacheWrapperClass(clazz);
        } else {
            //检测是否有默认构造起
            clazz.getConstructor();
            if (StringUtils.isEmpty(name)) {
                //未配置扩展名,自动生成,主要用于兼容java SPI的配置。
                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) {
                    //存储Class到名字的映射关系
                    cacheName(clazz, n);
                    //存储名字到Class的映射关系
                    saveInExtensionClass(extensionClasses, clazz, n, overridden);
                }
            }
        }
    }

getExtension

接下来我们看下getExtension方法,该方法入参是配置文件的key,name是cat,如果name为true,返回getDefaultExtension(), 对于Holder只是用来存放对象,cachedInstances用来存放对象的缓存,缓存了 ExtensionLoader 加载的扩展名与扩展实现对象之间的映射关系;当holder中没有存在对象的时候,采用双重锁检查机制,通过createExtension创建扩展类,并放入Holder对象中;

    @SuppressWarnings("unchecked")
    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 Holder<Object> getOrCreateHolder(String name) {
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            //如果name对应的Holder对象为null 就创建一个空Holder对象
            cachedInstances.putIfAbsent(name, new Holder<>());
            holder = cachedInstances.get(name);
        }
        return holder;
    }
createExtension

接下来我们看下createExtension,该方法完成了 SPI 配置文件的查找以及相应扩展实现类的实例化,同时还实现了自动装配以及自动 Wrapper 包装等功能。核心功能大致上可以分为5个步骤:

  1. 通过 getExtensionClasses 获取所有扩展类,然后根据扩展名从 cachedClasses 缓存中获取扩展实现类。如果 cachedClasses 未初始化,则会扫描前面介绍的三个 SPI 目录获取查找相应的 SPI 配置文件,然后加载其中的扩展实现类,最后将扩展名和扩展实现类的映射关系记录到 cachedClasses 缓存中;
  2. 根据扩展实现类从 EXTENSION_INSTANCES 缓存中查找相应的实例。如果没有查询到,通过反射创建扩展实现对象;
  3. 对扩展对象采用自动装配扩展实现对象中的属性,也就是我们说的IOC;
  4. 判断扩展对象是否是包装类,将扩展对象包裹进对应的 Wrapper 对象中;
  5. 如果扩展实现类实现了 Lifecycle 接口,在 initExtension() 方法中会调用 initialize() 方法进行初始化;
    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);
            }
            //向对象中注入依赖的属性 IOC自动装配
            injectExtension(instance);


            if (wrap) {
                //创建Wrapper扩展对象
                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作为参数创建wrapper实例,然后向wrapper实例中注入属性值
                            //并将wrapper实例赋值给instance
                            instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                        }
                    }
                }
            }
            //Lifecycle初始化
            initExtension(instance);
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
                    type + ") couldn't be instantiated: " + t.getMessage(), t);
        }
    }

至此就完成类加载加载以及初始化的整个过程,此处主要理解Dubbo如何将类加载的整个过程,整个过程中使用策略模式、适配器模式应用都是值得我们学习的,其中createAdaptiveExtensionClass方法没有进行介绍,这部分我们放到@Adaptive注解的介绍。

injectExtension(Dubbo IOC)

injectExtension就是Dubbo IOC的体现,主要是通过 setter 方法实现依赖注入。步骤就是获取实例的所有方法,在遍历的过程中看看是否名字有 setter 字段。如果有就通过 ObjectFactory 获取依赖对象,然后通过反射调用 setter 方法将依赖设置到目标对象当中。简单来说,自动装配属性就是在加载一个扩展点的时候,将其依赖的扩展点一并加载,并进行装配。

    private T injectExtension(T instance) {

        //如果为空直接返回
        if (objectFactory == null) {
            return instance;
        }

        try {
            //反射获取所有方法
            for (Method method : instance.getClass().getMethods()) {
                //如果是不是set方法跳过
                if (!isSetter(method)) {
                    continue;
                }
                //如果有 @DisableInject 注解的化就不会进行注入
                if (method.getAnnotation(DisableInject.class) != null) {
                    continue;
                }
                Class<?> pt = method.getParameterTypes()[0];
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }

                try {
                    //根据setter方法的名称确定属性名称 
                    String property = getSetterProperty(method);
                    //加载并实例化扩展实现
                    Object object = objectFactory.getExtension(pt, property);
                    if (object != null) {
                        //调用setter方法进行装配
                        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;
    }

结束

从四月之后就比较少更新公众号,原因是处理一些生活上的事,完成了今年生活上一个重要的目标,从该篇文章开始恢复每周一更,后续文章分享的规划应该会从三个方面:

  1. Java框架方面的内容,Dubbo源码以及Netty部分内容解读,Netty后续会做一个小小的项目;
  2. 准备刷刷算法相关内容,计划采用Go语言来刷算法;
  3. 云原生技术持续关注与学习,分享istio相关内容; 欢迎大家点点关注,点点赞!

标签:Dubbo,name,扩展,SPI,机制,null,type,加载
来源: https://www.cnblogs.com/wtzbk/p/16618487.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有