聊聊dubbo的SpringExtensionFactory
阿新 • • 發佈:2019-12-31
序
本文主要研究一下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