1. 程式人生 > 程式設計 >聊聊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