sequence
This article focuses on dubbo’s 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);
}
Copy the code
- The ExtensionFactory interface defines the getExtension method
SpringExtensionFactory
Dubbo – 2.7.3 / dubbo – config/dubbo – config – spring/SRC/main/Java/org/apache/dubbo/config/spring/extension/SpringExtensionFacto ry.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(); }}}}Copy the code
- SpringExtensionFactory implements the ExtensionFactory method, which provides the addApplicationContext static method to add the ApplicationContext, Also registered SHUTDOWN_HOOK_LISTENER; The getExtension method first gets the bean from ApplicationContext based on name, then gets the bean from ApplicationContext based on Type, or returns NULL
SpringExtensionFactoryTest
Dubbo – 2.7.3 / dubbo – config/dubbo – config – spring/SRC/test/Java/org/apache/dubbo/config/spring/extension/SpringExtensionFacto ryTest.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() {
returnnew HelloServiceImpl(); }}Copy the code
- SpringExtensionFactoryTest springExtensionFactory init approach to add context1 and context2; The extension is then verified to be fetched based on SPI, based on name, and based on Type
summary
SpringExtensionFactory implements the ExtensionFactory method, which provides the addApplicationContext static method to add the ApplicationContext, Also registered SHUTDOWN_HOOK_LISTENER; The getExtension method first gets the bean from ApplicationContext based on name, then gets the bean from ApplicationContext based on Type, or returns NULL
doc
- SpringExtensionFactory