乐趣区

聊聊dubbo的AwaitingNonWebApplicationListener

本文主要研究一下 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
退出移动版