千家信息网

Dubbo的SPI机制介绍以及Dubbo的IOC依赖注入实例

发表于:2025-02-03 作者:千家信息网编辑
千家信息网最后更新 2025年02月03日,这篇文章主要讲解了"Dubbo的SPI机制介绍以及Dubbo的IOC依赖注入实例",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Dubbo的SPI机制介
千家信息网最后更新 2025年02月03日Dubbo的SPI机制介绍以及Dubbo的IOC依赖注入实例

这篇文章主要讲解了"Dubbo的SPI机制介绍以及Dubbo的IOC依赖注入实例",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Dubbo的SPI机制介绍以及Dubbo的IOC依赖注入实例"吧!

1、Dubbo的IOC例子

@Testpublic void test1(){    ExtensionLoader loader = ExtensionLoader.getExtensionLoader(AdaptiveExt.class);    AdaptiveExt adaptiveExtension = loader.getExtension("dubbo");    URL url = URL.valueOf("test://localhost/test");    adaptiveExtension.echo("d", url);}public class DubboAdaptiveExt implements AdaptiveExt {    // dubbo中有依赖AdaptiveExt类型的变量    private AdaptiveExt adaptiveExt;    public void setAdaptiveExt(AdaptiveExt adaptiveExt) {        this.adaptiveExt = adaptiveExt;    }    @Override    public String echo(String msg, URL url) {        System.out.println(this.adaptiveExt.echo(msg, url));        return "dubbo";    }}
// 此时ThriftAdaptiveExt上面是标注了@Adaptive注解的@Adaptivepublic class ThriftAdaptiveExt implements AdaptiveExt {    @Override    public String echo(String msg, URL url) {        return "thrift";    }}

2、Dubbo的IOC需要用到的ExtensionFactory

Spring的IOC中,给生成的bean注入依赖,是调用context.getBean(name)去获得要注入的bean.Dubbo的IOC类似,它通过ExtensionFactory类型的变量objectFactory去dubbo中获取bean,核心代码objectFactory.getExtension(pt, property).下面先分析一下objectFactory的创建过程.objectFactory需要用到SpringExtensionFactory和SpiExtensionFactory.先看一下ExtenionFactory的实现类,如下图.下面通过源码分析objectFactory的生成过程.

这里的getExtensionLoader()详细分析可以参见: Dubbo的SPI机制分析1-SPI加载class

ExtensionLoader loader = ExtensionLoader.getExtensionLoader(AdaptiveExt.class);public static  ExtensionLoader getExtensionLoader(Class type) {    // 删去一些不必要的代码,详细分析可以看前面几篇分析    // 从缓存中获取与拓展类对应的ExtensionLoader    ExtensionLoader loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);    if (loader == null) {        // 若缓存未命中,则创建一个新的实例,创建新的实例时会走        EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));        loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);    }    return loader;} private ExtensionLoader(Class type) {    this.type = type;    // 这里的type是AdaptiveExt.class,所以会执行后面的代码,加载并创建SpiExtensionFactory和SpringExtensionFactory    objectFactory = (type == ExtensionFactory.class ? null :                              ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());}
public T getAdaptiveExtension() {    Object instance = cachedAdaptiveInstance.get();    if (instance == null) {        if (createAdaptiveInstanceError == null) {            synchronized (cachedAdaptiveInstance) {                instance = cachedAdaptiveInstance.get();                if (instance == null) {                    try {                        // 创建自适应拓展代理类对象并放入缓存,这里创建的就是ExtensionFactory的自适应拓展对象                        instance = createAdaptiveExtension();                        cachedAdaptiveInstance.set(instance);                    } catch (Throwable t) {                        // 抛异常                    }                }            }        }    }    return (T) instance;}
 private T createAdaptiveExtension() {    try {        // 分为3步:1是创建自适应拓展代理类Class对象,2是通过反射创建对象,3是给创建的对象按需依赖注入        return injectExtension((T) getAdaptiveExtensionClass().newInstance());    } catch (Exception e) {        // 抛异常    }}

getExtensionClasses()方法详细分析可以参见: Dubbo的SPI机制分析1-SPI加载class

private Class getAdaptiveExtensionClass() {    // 这里前面文章已经分析过了,它会去加载默认目录下的ExtensionFactory的实现类,总共有3个,目录是    // META-INF/dubbo/internal/,该目录对应两个文件,文件内容见下,由于AdaptiveExtensionFactory上面    // 标注了@Adaptive注解,所以它优先级最高,它就是ExtensionFactory的默认实现类    getExtensionClasses();    // 如果有标注了@Adaptive注解实现类,那么cachedAdaptiveClass不为空,直接返回    if (cachedAdaptiveClass != null) {        // 这里直接返回,cachedAdaptiveClass = AdaptiveExtensionFactory.class        return cachedAdaptiveClass;    }    // 不会再走这一步    return cachedAdaptiveClass = createAdaptiveExtensionClass();}
文件1内容:// 其中AdaptiveExtensionFactory上面标注了@Adaptive注解adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactoryspi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory文件2内容:spring=com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
@Adaptivepublic class AdaptiveExtensionFactory implements ExtensionFactory {
// 分析完了getAdaptiveExtensionClass(),它是返回AdaptiveExtensionFactory,接下来newInstance会调用它默认的构造方法return injectExtension((T) getAdaptiveExtensionClass().newInstance());@Adaptivepublic class AdaptiveExtensionFactory implements ExtensionFactory {    // 里面维护SpringExtensionFactory和SpiExtensionFactory    private final List factories;    public AdaptiveExtensionFactory() {        ExtensionLoader loader =                                     ExtensionLoader.getExtensionLoader(ExtensionFactory.class);        List list = new ArrayList();        for (String name : loader.getSupportedExtensions()) {            // 分别给SpringExtensionFactory和SpiExtensionFactory创建对象并放入list中            list.add(loader.getExtension(name));        }        factories = Collections.unmodifiableList(list);    }}

3、Dubbo的IOC源码分析

// 上面已经分析过了第一行代码,这里面会创建ExtensionFactory类型的变量objectFactory,这里面维护了一个list,// list里面有SpringExtensionFactory和SpiExtensionFactory类型的实例,Dubbo的IOC获取bean就是通过这两个变量去获取的ExtensionLoader loader = ExtensionLoader.getExtensionLoader(AdaptiveExt.class);AdaptiveExt adaptiveExtension = loader.getExtension("dubbo");public T getExtension(String name) {    // 删去一些代码    if (instance == null) {        synchronized (holder) {            instance = holder.get();            if (instance == null) {                // 创建拓展实例                instance = createExtension(name);                holder.set(instance);            }        }    }    return (T) instance;}
private T createExtension(String name) {    // 从配置文件中加载所有的拓展类,可得到"配置项名称"到"配置类"的映射关系表    // 这里我们指定了名字dubbo,并不是通过getAdaptiveExtension方法去获得自适应拓展类,这点要区分    // 所以这里拿到的是com.alibaba.dubbo.demo.provider.adaptive.impl.DubboAdaptiveExt    Class clazz = getExtensionClasses().get(name);    if (clazz == null) {        throw findException(name);    }    try {        // 也是尝试先从缓存获取,获取不到通过反射创建一个并放到缓存中        T instance = (T) EXTENSION_INSTANCES.get(clazz);        if (instance == null) {            // 这里直接通过反射创建DubboAdaptiveExt的实例,然后给他依赖注入            EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());            instance = (T) EXTENSION_INSTANCES.get(clazz);        }                // 依赖注入        injectExtension(instance);                return instance;    } }
private T injectExtension(T instance) { // 这里为了排版好看,删去一些异常捕捉抛出代码 // objectFactory就是我们前面分析的,它里面维护了SpringExtensionFactory和SpiExtensionFactory类型的实例 if (objectFactory != null) {    // 遍历DubboAdaptiveExt实例的所有方法,寻找set开头且参数为1个,且方法权限为public的方法    for (Method method : instance.getClass().getMethods()) {        if (method.getName().startsWith("set") && method.getParameterTypes().length == 1                                               && Modifier.isPublic(method.getModifiers())) {                        // 获取参数类型,这里是AdaptiveExt.class            Class pt = method.getParameterTypes()[0];                        // 获取属性名,这里是adaptiveExt            String property = method.getName().length() > 3 ?                               method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";            // 获取容器中AdaptiveExt.class类型的名字为adaptiveExt的实例            Object object = objectFactory.getExtension(pt, property);            // 获取之后通过反射赋值            if (object != null) {                 method.invoke(instance, object);            }         }       } } return instance;}
@Adaptivepublic class AdaptiveExtensionFactory implements ExtensionFactory {    private final List factories;    @Override    public  T getExtension(Class type, String name) {        // 遍历factory中所有的ExtensionFactory,先从SpiExtensionFactory中获取,获取不到在去Spring容器中获取        for (ExtensionFactory factory : factories) {            T extension = factory.getExtension(type, name);            if (extension != null) {                return extension;            }        }        return null;    }}

这里获取自适应拓展可以参考: Dubbo的SPI机制分析2-Adaptive详解

public class SpiExtensionFactory implements ExtensionFactory {    @Override    public  T getExtension(Class type, String name) {        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {            ExtensionLoader loader = ExtensionLoader.getExtensionLoader(type);            if (!loader.getSupportedExtensions().isEmpty()) {                // 先看SpiExtensionFactory怎么获取,它是通过getAdaptiveExtension()去自适应获取,根本                // 没有用到name,所以这里返回ThriftAdaptiveExt的实例                return loader.getAdaptiveExtension();            }        }        return null;    }}
public class SpringExtensionFactory implements ExtensionFactory {    // 可以看到Spring是先根据名字去取,取不到再根据类型去取    @Override    public  T getExtension(Class type, String name) {        for (ApplicationContext context : contexts) {            if (context.containsBean(name)) {                Object bean = context.getBean(name);                if (type.isInstance(bean)) {                    return (T) bean;                }            }        }        for (ApplicationContext context : contexts) {            try {                return context.getBean(type);            }        }        return null;    }}
// 所以这段代码输出:thrift@Testpublic void test1(){    ExtensionLoader loader = ExtensionLoader.getExtensionLoader(AdaptiveExt.class);    AdaptiveExt adaptiveExtension = loader.getExtension("dubbo");    URL url = URL.valueOf("test://localhost/test");    adaptiveExtension.echo("d", url);}

4、测试通过URL依赖注入

 /** * 测试通过URL依赖注入,将ThriftAdaptiveExt类上面的注解注释掉,同时给AdaptiveExt方法加上注解@Adaptive("t") */@Testpublic void test5(){    ExtensionLoader loader = ExtensionLoader.getExtensionLoader(AdaptiveExt.class);    Map map = new HashMap<>();    // t这个key就是根据@Adaptive("t")定的,两者要一致    map.put("t", "cloud");    URL url = new URL("", "", 1, map);    AdaptiveExt adaptiveExtension = loader.getExtension("dubbo");    adaptiveExtension.echo(" ", url);}

上述代码输出spring cloud,创建完DubboAdaptiveExt的实例给其注入依赖时,调用injectExtension(instance),因为没有了@Adaptive标注的类,所以需要Dubbo自己生成自适应拓展代理类Class,生成过程可以参考: Dubbo的SPI机制分析2-Adaptive详解.生成的代理类中有这样一句关键代码: String extName = url.getParameter("t", "dubbo"),因为url中有这个t参数,所以最后会调用cloud所对应的SpringCloudAdaptiveExt的echo方法,输出spring cloud.

感谢各位的阅读,以上就是"Dubbo的SPI机制介绍以及Dubbo的IOC依赖注入实例"的内容了,经过本文的学习后,相信大家对Dubbo的SPI机制介绍以及Dubbo的IOC依赖注入实例这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

0