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; }
- 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.
- Print banner
- 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
- 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(); }
- 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(); } } }
-
afterRefresh
There is no implementation here, Spring left us with extension points - Stop the timer that was started before and send the ApplicationStartedEvent event
- 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); } } }
- exception handling
- Send ApplicationReadyEvent Event Event Event