dubbo系列之-SPI依赖注入-2021-01-10

背景

接着前面的分析 org.apache.dubbo.common.extension.ExtensionLoader#injectExtension(instance)的功能。我们再次回忆下 前面我们已经分析了spi 是怎么创建实现类的流程,其实到这里spi的功能按理说就结束了,但是dubbo作为一款流行的框架,要提高适用性肯定不会仅限于此,injectExtension 主要扩展了spring 的依赖注入,以及支持 spi 的包装器。

源码分析

1.1

//org.apache.dubbo.common.extension.ExtensionLoader#injectExtension
private T injectExtension(T instance) {
    if (objectFactory != null) {
        //遍历实现类中的方法,如果是set方法则继续执行
        for (Method method : instance.getClass().getMethods()) {
            if (isSetter(method)) {
                if (method.getAnnotation(DisableInject.class) != null) {
                    continue;
                }
                Class<?> pt = method.getParameterTypes()[0];
                if (ReflectUtils.isPrimitives(pt)) {
                    continue;
                }
                //解析类似setUsername 将set和驼峰去了返回 username
                String property = getSetterProperty(method);
                //异步源码分析 1.2
                Object object = objectFactory.getExtension(pt, property);
                if (object != null) {
                    //通过反射调用set方法进行赋值
                    method.invoke(instance, object);
                }
            }
        }
    }
    return instance;
}

1.2

//Object object = objectFactory.getExtension(pt, property);
//org.apache.dubbo.common.extension.ExtensionFactory#getExtension
@SPI
public interface ExtensionFactory {
    <T> T getExtension(Class<T> type, String name);

很明显这是一个扩展点,在执行objectFactory.getExtension() 的时候我们需要找到接口的实现类才能继续往下debug,我们瞄一眼 objectFactory 属性的定义

private final ExtensionFactory objectFactory;
private ExtensionLoader(Class<?> type) {
    //type是我们传入的Job 接口 所以赋值为 ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()
    objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}

在ExtensionLoader初始化的时候会给该属性赋值为 ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension(),上篇文章我们分析过,通过方法 getAdaptiveExtension 将返回该扩展点的自适应扩展点实现,我们先看看接口实现

image
image

我们看到其中一个类 AdaptiveExtensionFactory 带有类注解 @Adaptive 自然走到了类扩展点实现,而不是方法扩展点,那么 getAdaptiveExtension() 方法返回的就是 AdaptiveExtensionFactory 对象,继续debug

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 把 ExtensionFactory 的所有实现类都加载出来了包括
    //SpiExtensionFactory 和 SpringExtensionFactory
    factories = Collections.unmodifiableList(list);
}

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

上面代码第12行,遍历factories中的2个扩展点,调用扩展点的 getExtension(type, name) 方法,我们举个例子,在前面demo中ApolloConfigCenter 实现类中加入一个User对象

image

那么解析完,对应的type,name 分别为User<class>,hupuUser<String>,我们先进到 SpringExtensionFactory 的getExtension实现中去

1.2.1 SpringExtensionFactory
public <T> T getExtension(Class<T> type, String name) {
    //属性类型跳过SPI注解
    if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
        return null;
    }
    //遍历spirng的ApplicationContext容器,具体解析到1.2.1.1
    for (ApplicationContext context : CONTEXTS) {
        if (context.containsBean(name)) {
            //使用过spring的应该知道很熟悉的方法,调用 getBean 得到bean
            Object bean = context.getBean(name);
            if (type.isInstance(bean)) {
                return (T) bean;
            }
        }
    }
   if (Object.class == type) {
        return null;
    }
    //这边一样的道理,如果通过name获取不到,尝试通过Class 的方式获取
    for (ApplicationContext context : CONTEXTS) {
        try {
            return context.getBean(type);
        } catch (NoUniqueBeanDefinitionException multiBeanExe) {
            logger.warn("Find more than 1 spring extensions (beans) of type " + type.getName() + ", will stop auto injection. Please make sure you have specified the concrete parameter type and there's only one extension of that type.");
        } catch (NoSuchBeanDefinitionException noBeanExe) {
            if (logger.isDebugEnabled()) {
                logger.debug("Error when get spring extension(bean) for type:" + type.getName(), noBeanExe);
            }
        }
    }
    return null;
}

1.2.1.1
private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>();

//org...bbo.config.spring.extension.SpringExtensionFactory#addApplicationContext
public static void addApplicationContext(ApplicationContext context) {
    CONTEXTS.add(context);//将context收集到set中
    if (context instanceof ConfigurableApplicationContext) {
        ((ConfigurableApplicationContext) context).registerShutdownHook();
        DubboShutdownHook.getDubboShutdownHook().unregister();
    }
    BeanFactoryUtils.addApplicationListener(context, SHUTDOWN_HOOK_LISTENER);
}

//org.apache.dubbo.config.spring.ServiceBean#setApplicationContext
public void setApplicationContext(ApplicationContext applicationContext) {
    //ServiceBean 是dubbo的类,实现了 ApplicationContextAware 
    //这样在dubbo开始初始化的时候,就会被调用到将ApplicationContext 对象赋值给了
    //SpringExtensionFactory的CONTEXTS,并且还支持多个/多层容器
    this.applicationContext = applicationContext;
    SpringExtensionFactory.addApplicationContext(applicationContext);
    supportedApplicationListener = addApplicationListener(applicationContext, this);
}

1.2.2 SpiExtensionFactory

看完 SpringExtensionFactory 的实现,我们看看SpiExtensionFactory的实现,这个比较简单

public class SpiExtensionFactory implements ExtensionFactory {

    @Override
    public <T> T getExtension(Class<T> type, String name) {
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type);
            if (!loader.getSupportedExtensions().isEmpty()) {
            //返回扩展点的自适应扩展点
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }

}

还是ApolloConfigCenter ,SpiExtensionFactory 的含义就说如果ApolloConfigCenter类中有扩展点的属性,则会将扩展点的自适应扩展点赋值

image

如上,login 是一个扩展点,有weixin,phone等实现,如果我们在 ApolloConfigCenter 这样定义之后,spi 将会给login变量附上值。

流程deug

为了验证上面分析,我们运行debug,这次不只是单独测试,而是会结合spring容器。

//dubbo-provider.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <bean id="myUser" class="com.poizon.study.provider.User"/>
</beans>

//com.poizon.study.provider.spi.NacosConfigCenter
public class NacosConfigCenter implements ConfigCenter {
    private User myUser;//并没有带任何注解
    public User getMyUser() {return myUser;}
    public void setMyUser(User myUser) { this.myUser = myUser;}
    private Protocol protocol; //并没有带任何注解
    public void setProtocol(Protocol protocol) {  this.protocol = protocol;}

    @Override
    public String get(String key) {
        System.out.println(protocol);
        return "nacos " + myUser.getName();
    }
}

//测试demo
public static void main(String[] args) throws IOException {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("dubbo-provider.xml");
    ctx.start();

    ExtensionLoader<ConfigCenter> extensionLoader = ExtensionLoader.getExtensionLoader(ConfigCenter.class);
    ConfigCenter weixin = extensionLoader.getExtension("nacos");
    String key = weixin.get("key");
    System.out.println(key);
    //打印
    //org.apache.dubbo.rpc.Protocol$Adaptive@5a9d6f02
    //nacos 忠犬八公

    System.in.read();
}

很清晰,NacosConfigCenter的myUser 属性已经赋值为spring中的user bean,protocol 属性已经赋值为Protocol接口的自适应扩展点。

总结

我们接着扩展,如果实现类和属性是继承同一个接口,那么就可以理解为包装器,类似这样用法dubbo里面有很多,比如Cluster中的MockClusterWrapper,Protocol中的ProtocolFilterWrapper 等等,这些我们会在后面介绍到,关于spi就介绍到这。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容