序
本文主要研究一下 dubbo 的 AwaitingNonWebApplicationListener
AwaitingNonWebApplicationListener
dubbo-spring-boot-project-2.7.3/dubbo-spring-boot-compatible/autoconfigure/src/main/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListener.java
public class AwaitingNonWebApplicationListener implements SmartApplicationListener {private static final String[] WEB_APPLICATION_CONTEXT_CLASSES = new String[]{
"org.springframework.web.context.WebApplicationContext",
"org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext"
};
private static final Logger logger = LoggerFactory.getLogger(AwaitingNonWebApplicationListener.class);
private static final Class<? extends ApplicationEvent>[] SUPPORTED_APPLICATION_EVENTS =
of(ApplicationReadyEvent.class, ContextClosedEvent.class);
private static final AtomicBoolean awaited = new AtomicBoolean(false);
private static final Lock lock = new ReentrantLock();
private static final Condition condition = lock.newCondition();
private static final ExecutorService executorService = newSingleThreadExecutor();
private static <T> T[] of(T... values) {return values;}
static AtomicBoolean getAwaited() {return awaited;}
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {return containsElement(SUPPORTED_APPLICATION_EVENTS, eventType);
}
@Override
public boolean supportsSourceType(Class<?> sourceType) {return true;}
@Override
public void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationReadyEvent) {onApplicationReadyEvent((ApplicationReadyEvent) event);
} else if (event instanceof ContextClosedEvent) {onContextClosedEvent((ContextClosedEvent) event);
}
}
@Override
public int getOrder() {return LOWEST_PRECEDENCE;}
protected void onApplicationReadyEvent(ApplicationReadyEvent event) {final ConfigurableApplicationContext applicationContext = event.getApplicationContext();
if (!isRootApplicationContext(applicationContext) || isWebApplication(applicationContext)) {return;}
await();}
private boolean isRootApplicationContext(ApplicationContext applicationContext) {return applicationContext.getParent() == null;
}
private boolean isWebApplication(ApplicationContext applicationContext) {
boolean webApplication = false;
for (String contextClass : WEB_APPLICATION_CONTEXT_CLASSES) {if (isAssignable(contextClass, applicationContext.getClass(), applicationContext.getClassLoader())) {
webApplication = true;
break;
}
}
return webApplication;
}
private static boolean isAssignable(String target, Class<?> type, ClassLoader classLoader) {
try {return ClassUtils.resolveClassName(target, classLoader).isAssignableFrom(type);
} catch (Throwable ex) {return false;}
}
protected void onContextClosedEvent(ContextClosedEvent event) {release();
shutdown();}
protected void await() {
// has been waited, return immediately
if (awaited.get()) {return;}
if (!executorService.isShutdown()) {executorService.execute(() -> executeMutually(() -> {while (!awaited.get()) {if (logger.isInfoEnabled()) {logger.info("[Dubbo] Current Spring Boot Application is await...");
}
try {condition.await();
} catch (InterruptedException e) {Thread.currentThread().interrupt();}
}
}));
}
}
protected void release() {executeMutually(() -> {while (awaited.compareAndSet(false, true)) {if (logger.isInfoEnabled()) {logger.info("[Dubbo] Current Spring Boot Application is about to shutdown...");
}
condition.signalAll();}
});
}
private void shutdown() {if (!executorService.isShutdown()) {
// Shutdown executorService
executorService.shutdown();}
}
private void executeMutually(Runnable runnable) {
try {lock.lock();
runnable.run();} finally {lock.unlock();
}
}
}
- AwaitingNonWebApplicationListener 实现了 SmartApplicationListener 接口,其 supportsEventType 支持 ApplicationReadyEvent.class, ContextClosedEvent.class
- onApplicationEvent 判断是 ApplicationReadyEvent 时执行 onApplicationReadyEvent 方法;判断是 ContextClosedEvent 时,执行 onContextClosedEvent 方法
- onApplicationReadyEvent 对于 rootApplicationContext 或者是 nonWebApplicationContext 执行 await 方法,该方法会注册一个异步线程去执行 condition.await();onContextClosedEvent 则执行 release 及 shutdown 方法,release 方法会更新 awaited 为 true,然后执行 condition.signalAll(),shutdown 方法则关闭 executorService
AwaitingNonWebApplicationListenerTest
dubbo-spring-boot-project-2.7.3/dubbo-spring-boot-compatible/autoconfigure/src/test/java/org/apache/dubbo/spring/boot/context/event/AwaitingNonWebApplicationListenerTest.java
public class AwaitingNonWebApplicationListenerTest {
@Test
public void init() {AtomicBoolean awaited = AwaitingNonWebApplicationListener.getAwaited();
awaited.set(false);
}
@Test
public void testSingleContextNonWebApplication() {new SpringApplicationBuilder(Object.class)
.web(false)
.run().close();
AtomicBoolean awaited = AwaitingNonWebApplicationListener.getAwaited();
Assert.assertTrue(awaited.get());
}
@Test
public void testMultipleContextNonWebApplication() {new SpringApplicationBuilder(Object.class)
.parent(Object.class)
.web(false)
.run().close();
AtomicBoolean awaited = AwaitingNonWebApplicationListener.getAwaited();
Assert.assertTrue(awaited.get());
}
}
- AwaitingNonWebApplicationListenerTest 验证了 SingleContextNonWebApplication 及 MultipleContextNonWebApplication
小结
- AwaitingNonWebApplicationListener 实现了 SmartApplicationListener 接口,其 supportsEventType 支持 ApplicationReadyEvent.class, ContextClosedEvent.class
- onApplicationEvent 判断是 ApplicationReadyEvent 时执行 onApplicationReadyEvent 方法;判断是 ContextClosedEvent 时,执行 onContextClosedEvent 方法
- onApplicationReadyEvent 对于 rootApplicationContext 或者是 nonWebApplicationContext 执行 await 方法,该方法会注册一个异步线程去执行 condition.await();onContextClosedEvent 则执行 release 及 shutdown 方法,release 方法会更新 awaited 为 true,然后执行 condition.signalAll(),shutdown 方法则关闭 executorService
doc
- AwaitingNonWebApplicationListener