/**
   * Actually performs context closing: publishes a ContextClosedEvent and destroys the singletons
   * in the bean factory of this application context.
   *
   * <p>Called by both {@code close()} and a JVM shutdown hook, if any.
   *
   * @see org.springframework.context.event.ContextClosedEvent
   * @see #destroyBeans()
   * @see #close()
   * @see #registerShutdownHook()
   */
  protected void doClose() {
    if (this.active.get() && this.closed.compareAndSet(false, true)) {
      if (logger.isInfoEnabled()) {
        logger.info("Closing " + this);
      }

      LiveBeansView.unregisterApplicationContext(this);

      try {
        // Publish shutdown event.
        publishEvent(new ContextClosedEvent(this));
      } catch (Throwable ex) {
        logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
      }

      // Stop all Lifecycle beans, to avoid delays during individual destruction.
      try {
        getLifecycleProcessor().onClose();
      } catch (Throwable ex) {
        logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
      }

      // Destroy all cached singletons in the context's BeanFactory.
      destroyBeans();

      // Close the state of this context itself.
      closeBeanFactory();

      // Let subclasses do some final clean-up if they wish...
      onClose();

      this.active.set(false);
    }
  }