聊聊dubbo的SpringExtensionFactory

39次阅读

共计 5059 个字符,预计需要花费 13 分钟才能阅读完成。

本文主要研究一下 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

  • SpringExtensionFactory

正文完
 0