专栏首页码匠的流水账聊聊dubbo的SpringExtensionFactory
原创

聊聊dubbo的SpringExtensionFactory

本文主要研究一下dubbo的SpringExtensionFactory

ExtensionFactory

dubbo-2.7.3/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionFactory.java

@SPI
public interface ExtensionFactory {
​
    /**
     * Get extension.
     *
     * @param type object type.
     * @param name object name.
     * @return object instance.
     */
    <T> T getExtension(Class<T> type, String name);
​
}
  • ExtensionFactory接口定义了getExtension方法

SpringExtensionFactory

dubbo-2.7.3/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactory.java

public class SpringExtensionFactory implements ExtensionFactory {
    private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class);
​
    private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>();
    private static final ApplicationListener SHUTDOWN_HOOK_LISTENER = new ShutdownHookListener();
​
    public static void addApplicationContext(ApplicationContext context) {
        CONTEXTS.add(context);
        if (context instanceof ConfigurableApplicationContext) {
            ((ConfigurableApplicationContext) context).registerShutdownHook();
            DubboShutdownHook.getDubboShutdownHook().unregister();
        }
        BeanFactoryUtils.addApplicationListener(context, SHUTDOWN_HOOK_LISTENER);
    }
​
    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) {
​
        //SPI should be get from SpiExtensionFactory
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            return null;
        }
​
        for (ApplicationContext context : CONTEXTS) {
            if (context.containsBean(name)) {
                Object bean = context.getBean(name);
                if (type.isInstance(bean)) {
                    return (T) bean;
                }
            }
        }
​
        logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + type.getName());
​
        if (Object.class == type) {
            return null;
        }
​
        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);
                }
            }
        }
​
        logger.warn("No spring extension (bean) named:" + name + ", type:" + type.getName() + " found, stop get bean.");
​
        return null;
    }
​
    private static class ShutdownHookListener implements ApplicationListener {
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ContextClosedEvent) {
                DubboShutdownHook shutdownHook = DubboShutdownHook.getDubboShutdownHook();
                shutdownHook.doDestroy();
            }
        }
    }
}
  • SpringExtensionFactory实现了ExtensionFactory方法,它提供了addApplicationContext静态方法来添加ApplicationContext,同时注册了SHUTDOWN_HOOK_LISTENER;其getExtension方法首先根据name从ApplicationContext中获取bean,获取不到则在根据type再去ApplicationContext中获取bean,获取不到则返回null

SpringExtensionFactoryTest

dubbo-2.7.3/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/extension/SpringExtensionFactoryTest.java

@Configuration
public class SpringExtensionFactoryTest {
​
    private SpringExtensionFactory springExtensionFactory = new SpringExtensionFactory();
    private AnnotationConfigApplicationContext context1;
    private AnnotationConfigApplicationContext context2;
​
    @BeforeEach
    public void init() {
        context1 = new AnnotationConfigApplicationContext();
        context1.register(getClass());
        context1.refresh();
        context2 = new AnnotationConfigApplicationContext();
        context2.register(BeanForContext2.class);
        context2.refresh();
        SpringExtensionFactory.addApplicationContext(context1);
        SpringExtensionFactory.addApplicationContext(context2);
    }
​
    @Test
    public void testGetExtensionBySPI() {
        Protocol protocol = springExtensionFactory.getExtension(Protocol.class, "protocol");
        Assertions.assertNull(protocol);
    }
​
    @Test
    public void testGetExtensionByName() {
        DemoService bean = springExtensionFactory.getExtension(DemoService.class, "bean1");
        Assertions.assertNotNull(bean);
    }
​
    @Test
    public void testGetExtensionByTypeMultiple() {
        try {
            springExtensionFactory.getExtension(DemoService.class, "beanname-not-exist");
        } catch (Exception e) {
            e.printStackTrace();
            Assertions.assertTrue(e instanceof NoUniqueBeanDefinitionException);
        }
    }
​
    @Test
    public void testGetExtensionByType() {
        HelloService bean = springExtensionFactory.getExtension(HelloService.class, "beanname-not-exist");
        Assertions.assertNotNull(bean);
    }
​
    @AfterEach
    public void destroy() {
        SpringExtensionFactory.clearContexts();
        context1.close();
        context2.close();
    }
​
    @Bean("bean1")
    public DemoService bean1() {
        return new DemoServiceImpl();
    }
​
    @Bean("bean2")
    public DemoService bean2() {
        return new DemoServiceImpl();
    }
​
    @Bean("hello")
    public HelloService helloService() {
        return new HelloServiceImpl();
    }
}
  • SpringExtensionFactoryTest的init方法给springExtensionFactory添加了context1和context2;之后验证了根据SPI、根据name、根据type来获取extension

小结

SpringExtensionFactory实现了ExtensionFactory方法,它提供了addApplicationContext静态方法来添加ApplicationContext,同时注册了SHUTDOWN_HOOK_LISTENER;其getExtension方法首先根据name从ApplicationContext中获取bean,获取不到则在根据type再去ApplicationContext中获取bean,获取不到则返回null

doc

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 聊聊dubbo的SpringExtensionFactory

    dubbo-2.7.3/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Extensi...

    codecraft
  • 聊聊spring cloud eureka的instanceEnabledOnit属性

    本文主要研究下spring cloud eureka的instanceEnabledOnit属性

    codecraft
  • 聊聊rocketmq的RollingFileAppender

    org/apache/rocketmq/logging/inner/LoggingBuilder.java

    codecraft
  • 聊聊dubbo的SpringExtensionFactory

    dubbo-2.7.3/dubbo-common/src/main/java/org/apache/dubbo/common/extension/Extensi...

    codecraft
  • editormd实现Markdown编辑器写文章功能

    想在项目里引入Markdown编辑器实现写文章功能,网上找到一款开源的插件editormd.js

    用户1208223
  • ElasticSearch(7.2.2)-es集群索引分⽚管理

    cwl_java
  • 谷歌跨界音乐圈:AI用上千乐器数据创造出人类没听过的声音,来听听看!

    大数据文摘
  • 关于跨语种语言模型的讨论

    最近,一个预先训练的模型被证明可以改善下游问题。Lample和Conneau提出了两个新的培训目标来培训跨语言语言模型(XLM)。这种方法可以实现跨语言自然语言...

    AiTechYun
  • Kaggle刚刚上线了机器学习课程,我们帮你做了个测评

    大数据文摘
  • 《挑战30天C++入门极限》新手入门:C/C++中数组和指针类型的关系

    landv

扫码关注云+社区

领取腾讯云代金券