What exactly is SpringApplication run ning?

Keywords: Java Spring SpringBoot

In the last article What exactly did SpringApplication run (top) In this article, we continue by analyzing the first half of the run method below.

    public ConfigurableApplicationContext run(String... args) {
            //. . . 
            //Continue above
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(
                    SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            listeners.started(context);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, listeners, exceptionReporters, ex);
            throw new IllegalStateException(ex);
        }
        listeners.running(context);
        return context;
    }
  1. Get the system property spring.beaninfo.ignore
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
        if (System.getProperty(
                CachedIntrospectionResults."spring.beaninfo.ignore") == null) {
            Boolean ignore = environment.getProperty("spring.beaninfo.ignore",
                    Boolean.class, Boolean.TRUE);
            System.setProperty(CachedIntrospectionResults."spring.beaninfo.ignore",
                    ignore.toString());
        }
    }

But the role of this property is unknown.

  1. Print banner
  2. Create an ApplicationContext from the current environment
protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, "
                                + "please specify an ApplicationContextClass",
                        ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

Based on our Servlet environment, the ApplicationContext created is the AnnotationConfigServlet WebServerApplicationContext

  1. Load SpringBootExceptionReporter, which contains components related to exception handling after SpringBoot fails to start
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Set<String> names = new LinkedHashSet<>(
                SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

The 10 prepareContext block is still longer

private void prepareContext(ConfigurableApplicationContext context,
        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    context.setEnvironment(environment);
    postProcessApplicationContext(context);
    applyInitializers(context);
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }

    context.getBeanFactory().registerSingleton("springApplicationArguments",
            applicationArguments);                               
    if (printedBanner != null) {
        context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
    }

    // Load the sources
    Set<Object> sources = getAllSources();                     
    Assert.notEmpty(sources, "Sources must not be empty");       
    load(context, sources.toArray(new Object[0]));
    listeners.contextLoaded(context);
}
1. First line, replace all environment s in context
public void setEnvironment(ConfigurableEnvironment environment) {
    super.setEnvironment(environment);            // Set environment for context
    this.reader.setEnvironment(environment);    // conditionEvaluator property of reader property instantiating context
    this.scanner.setEnvironment(environment);    // Set the environment property of the context's scanner property
}
2. Post-processing of context
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    if (this.beanNameGenerator != null) {
        context.getBeanFactory().registerSingleton(
                AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                this.beanNameGenerator);
    }
    if (this.resourceLoader != null) {
        if (context instanceof GenericApplicationContext) {
            ((GenericApplicationContext) context)
                    .setResourceLoader(this.resourceLoader);
        }
        if (context instanceof DefaultResourceLoader) {
            ((DefaultResourceLoader) context)
                    .setClassLoader(this.resourceLoader.getClassLoader());
        }
    }
}

This default beanNameGenerator and resourceLoader are both empty and will only replace beans in the container if we customize both objects
3. Execute all the initialize methods of the ApplicationContextInitializer

protected void applyInitializers(ConfigurableApplicationContext context) {
    for (ApplicationContextInitializer initializer : getInitializers()) {
        Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
                initializer.getClass(), ApplicationContextInitializer.class);
        Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
        initializer.initialize(context);
    }
}
4. `listeners.contextPrepared(context)` This is an empty method, not implemented, an extension point for Spring
 5. Print profile
 6. Register bean s:`springApplicationArguments`
7. Publish Events
public void contextLoaded(ConfigurableApplicationContext context) {
        for (ApplicationListener<?> listener : this.application.getListeners()) {
            if (listener instanceof ApplicationContextAware) {
                ((ApplicationContextAware) listener).setApplicationContext(context);
            }
            context.addApplicationListener(listener);
        }
        this.initialMulticaster.multicastEvent(
                new ApplicationPreparedEvent(this.application, this.args, context));
    }

This not only publishes the ApplicationPreparedEvent event, but also injects the context container into the listener that implements the ApplicationContextAware interface
8. load, which actually creates a BeanDefinitionLoader object

protected void load(ApplicationContext context, Object[] sources) {
        if (logger.isDebugEnabled()) {
            logger.debug(
                    "Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }
        BeanDefinitionLoader loader = createBeanDefinitionLoader(
                getBeanDefinitionRegistry(context), sources);
        if (this.beanNameGenerator != null) {
            loader.setBeanNameGenerator(this.beanNameGenerator);
        }
        if (this.resourceLoader != null) {
            loader.setResourceLoader(this.resourceLoader);
        }
        if (this.environment != null) {
            loader.setEnvironment(this.environment);
        }
        loader.load();
    }
  1. Initialization of container refreshContext
    The last call to the refresh method of the AbstractApplicationContext class is this method. Because it is too long to expand here, interested students can refer to this article: Annotation-based SpringIOC source resolution
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Record container startup time, mark'Started'status, check environment variables
      prepareRefresh();
      // Initialize BeanFactory container, register BeanDefinition
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // Set up a class loader for BeanFactory, add several BeanPostProcessor s, and manually register several special bean s
      prepareBeanFactory(beanFactory);
      try {
         // Extension Points
         postProcessBeanFactory(beanFactory);
         // Call the postProcessBeanFactory method of the BeanFactoryPostProcessor implementation classes
         invokeBeanFactoryPostProcessors(beanFactory);
         // Register implementation classes for BeanPostProcessor
         registerBeanPostProcessors(beanFactory);
         // Initialize MessageSource
         initMessageSource();
         // Initialize event broadcaster
         initApplicationEventMulticaster();
         // Extension Points
         onRefresh();
         // Register event listeners
         registerListeners();
         // Initialize all singleton beans
         finishBeanFactoryInitialization(beanFactory);
         // Broadcast events
         finishRefresh();
      }
      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }
         // Destroy an initialized Bean
         destroyBeans();
         // Set'active'status
         cancelRefresh(ex);
         throw ex;
      }
      finally {
         // Clear Cache
         resetCommonCaches();
      }
   }
}
  1. afterRefresh
    There is no implementation here, Spring left us with extension points
  2. Stop the timer that was started before and send the ApplicationStartedEvent event
  3. Call the implementation classes of the ApplicationRunner and CommandLineRunner interfaces in the system, and refer to this article for their use: Several ways to execute the specified method at Java project startup
private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<>();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        for (Object runner : new LinkedHashSet<>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }
  1. exception handling
  2. Send ApplicationReadyEvent Event Event Event

Posted by DedMousie on Tue, 24 Sep 2019 10:06:20 -0700