Analysis of SpringBoot startup process
It has been two years since we used spring boot for development, but we haven't thoroughly understood its relevant source code. In the near future, we can carefully read and analyze the relevant source code. In this paper, we start our analysis and study from the start-up process of spring boot. In the process of analysis, there may be some places that are not quite right. Please comment and point out.
First of all, look at the sequence diagram of spring boot. The sequence diagram is not very comprehensive. There are many details not included in the startup process:
The starting entry code of SpringBoot is as follows:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
The code to start the entry is very simple. After clicking run, we will see that the method in the springapplication class is actually called:
/** * Static helper that can be used to run a {@link SpringApplication} from the * specified source using default settings. * @param source the source to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Object source, String... args) { return run(new Object[] { source }, args); }
Continue to the relevant methods:
/** * Static helper that can be used to run a {@link SpringApplication} from the * specified sources using default settings and user supplied arguments. * @param sources the sources to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return new SpringApplication(sources).run(args); }
We know that this method will call the construction method of SpringApplication and run the run method through comments. Let's go to the construction method of SpringApplication first:
/** * Create a new {@link SpringApplication} instance. The application context will load * beans from the specified sources (see {@link SpringApplication class-level} * documentation for details. The instance can be customized before calling * {@link #run(String...)}. * @param sources the bean sources * @see #run(Object, String[]) * @see #SpringApplication(ResourceLoader, Object...) */ public SpringApplication(Object... sources) { initialize(sources); }
This construction method will initialize the related resources. If you go further, you will find that initialize is a private method:
private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources));// 1 } this.webEnvironment = deduceWebEnvironment(); // 2 setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class)); //3 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 4 this.mainApplicationClass = deduceMainApplicationClass(); // 5 }
##In the initialize method, you do the following:
###1. Set sources to the sources property of SpringApplication
###2. Determine whether it is a web program( javax.servlet.Servlet and org.springframework.web.context.ConfigurableWebApplicationContext Must exist in the classloader) and be set to the webEnvironment property
###3. From spring.factories Find the class whose key is ApplicationContextInitializer in the file and set it to the initializers property of SpringApplication after instantiation. This process is to find out all the application initializers
###4. From spring.factories Find the class whose key is ApplicationListener in the file and set it to the listeners property of SpringApplication after instantiation. This process is to find out all the application event listeners
###5. Find out the main class
In addition to 1, we successively enter:
###2. To determine whether it is a web program, the code is as follows:
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext" }; private boolean deduceWebEnvironment() { for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return false; } } return true; } public abstract class ClassUtils { ...... public static boolean isPresent(String className, ClassLoader classLoader) { try { forName(className, classLoader); return true; } catch (Throwable ex) { // Class or one of its dependencies is not present... return false; } } ...... public static Class<?> forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError { Assert.notNull(name, "Name must not be null"); Class<?> clazz = resolvePrimitiveClassName(name); if (clazz == null) { clazz = commonClassCache.get(name); } if (clazz != null) { return clazz; } // "java.lang.String[]" style arrays if (name.endsWith(ARRAY_SUFFIX)) { String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length()); Class<?> elementClass = forName(elementClassName, classLoader); return Array.newInstance(elementClass, 0).getClass(); } // "[Ljava.lang.String;" style arrays if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) { String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1); Class<?> elementClass = forName(elementName, classLoader); return Array.newInstance(elementClass, 0).getClass(); } // "[[I" or "[[Ljava.lang.String;" style arrays if (name.startsWith(INTERNAL_ARRAY_PREFIX)) { String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length()); Class<?> elementClass = forName(elementName, classLoader); return Array.newInstance(elementClass, 0).getClass(); } ClassLoader clToUse = classLoader; if (clToUse == null) { clToUse = getDefaultClassLoader(); } try { return (clToUse != null ? clToUse.loadClass(name) : Class.forName(name)); } catch (ClassNotFoundException ex) { int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR); if (lastDotIndex != -1) { String innerClassName = name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1); try { return (clToUse != null ? clToUse.loadClass(innerClassName) : Class.forName(innerClassName)); } catch (ClassNotFoundException ex2) { // Swallow - let original exception get through } } throw ex; } } ...... }
##3. Set the initializers property of SpringApplication
ApplicationContextInitializer, application initializer, do some initialization work
First get the instance collection of ApplicationContextInitializer
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<String>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } ...... /** * Sets the {@link ApplicationContextInitializer} that will be applied to the Spring * {@link ApplicationContext}. * @param initializers the initializers to set */ public void setInitializers( Collection<? extends ApplicationContextInitializer<?>> initializers) { this.initializers = new ArrayList<ApplicationContextInitializer<?>>(); this.initializers.addAll(initializers); }
public abstract class SpringFactoriesLoader { public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; ...... public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); List<String> result = new ArrayList<String>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } } ...... }
This step is essentially from spring.factories Find the class whose key is ApplicationContextInitializer in the file and set it to the initializers property of SpringApplication after instantiation.
##4. Set the listeners property of SpringApplication
ApplicationListener, application event listener,
The application events here include application started event, application failed event, application prepared event, etc.
The application event listener is bound to the listening event. For example, configserver bootstrap applicationlistener is only bound to ApplicationEnvironmentPreparedEvent event, liquibaaseservicelocatorapplicationlistener is only bound to ApplicationStartedEvent event, LoggingApplicationListener is bound to all events, etc.
This step is actually the same as step 3, except that the listener property is set in this step, and its code is basically the same as step 3
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {}); } private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<String>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } /** * Sets the {@link ApplicationListener}s that will be applied to the SpringApplication * and registered with the {@link ApplicationContext}. * @param listeners the listeners to set */ public void setListeners(Collection<? extends ApplicationListener<?>> listeners) { this.listeners = new ArrayList<ApplicationListener<?>>(); this.listeners.addAll(listeners); }
public abstract class SpringFactoriesLoader { public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; ...... public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); List<String> result = new ArrayList<String>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } } ...... }
This step is essentially from spring.factories Find the class whose key is ApplicationListener in the file and set it to the listener property of SpringApplication after instantiation.
spring.factories The contents of the document are as follows:
# PropertySource Loaders org.springframework.boot.env.PropertySourceLoader=\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ org.springframework.boot.env.YamlPropertySourceLoader # Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListener # Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener,\ org.springframework.boot.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.logging.LoggingApplicationListener # Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor # Failure Analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer # FailureAnalysisReporters org.springframework.boot.diagnostics.FailureAnalysisReporter=\ org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
##5. Find the main method
private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
Before analyzing the run method, take a look at some of the event and listener concepts in spring application.
First, it introduces the SpringApplicationRunListeners class and SpringApplicationRunListener class.
Spring applicationrunlisteners internally holds the spring applicationrunlisteners collection and a Log log class. For batch execution of the spring application run listener listener.
Spring application run listener looks at the name and knows that it is used to listen for the execution of the run method of spring application.
It defines five steps:
Started (the run method is executed immediately; the corresponding event type is ApplicationStartedEvent)
Environmentprepared (called before the creation of ApplicationContext and when the environment information is ready; the corresponding event type is ApplicationEnvironmentPreparedEvent)
contextPrepared(ApplicationContext is created and called once before source is loaded; there is no specific corresponding event).
contextLoaded(ApplicationContext is created and loaded and called before refresh; the type of the corresponding event is ApplicationPreparedEvent).
Finished (before the end of run method is called; the type of the corresponding event is ApplicationReadyEvent or ApplicationFailedEvent).
At present, SpringApplicationRunListener has only one implementation class eventpublishingrunnlistener, which encapsulates the listening process into SpringApplicationEvent events and lets the implementation class simpleapplicationeventmulticasting of the internal property (property name is multicasting) applicationeventmulticasting interface broadcast out. The broadcast event object will be owned by the listeners in SpringApplication Sex.
So the relationship between the spring application run listener and the application listener is connected through the spring application event broadcast by the application event multicaster.
##After the completion of the above five steps, you can really enter the startup process:
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { // Construct a task execution observer StopWatch stopWatch = new StopWatch(); // Start task viewer, mainly recording the start time stopWatch.start(); ConfigurableApplicationContext context = null; FailureAnalyzers analyzers = null; // Configure Headless mode configureHeadlessProperty(); // Get SpringApplicationRunListeners. There is only one eventpublishing runlistener inside SpringApplicationRunListeners listeners = getRunListeners(args); // As analyzed above, it will be encapsulated as a SpringApplicationEvent event and broadcast to listeners in SpringApplication // Here, the listener accepting the ApplicationStartedEvent event will perform the corresponding operation listeners.started(); try { // Construct an application parameter holding class ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // Prepare relevant environment ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); // Print banner Banner printedBanner = printBanner(environment); // Create Spring container context = createApplicationContext(); // Initialize FailureAnalyzers analyzers = new FailureAnalyzers(context); // Preparing the Spring container prepareContext(context, environment, listeners, applicationArguments,printedBanner); // Refresh Spring container refreshContext(context); // Perform additional actions after container creation afterRefresh(context, applicationArguments); // Broadcast the ApplicationReadyEvent event to the corresponding listener for execution listeners.finished(context, null); // End of execution, record execution time stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } return context;// Return to Spring container } catch (Throwable ex) { // If an error is reported in this process, some abnormal operations will be performed, and then the ApplicationFailedEvent event will be broadcast to the corresponding listener for execution handleRunFailure(context, listeners, analyzers, ex); throw new IllegalStateException(ex); } }
Let's see how the main steps in the startup process are carried out
(the main steps include: get the listener and open it - > construct the default application parameter holding class - > prepare to configure the relevant environment - > create the Spring container - > prepare before container initialization - > refresh the container - > additional operations after container creation - > broadcast events to the listener - > return to the container):
###(1) Get the listener and turn on listening:
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; // Find all SpringApplicationRunListener listeners and return return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)); } class SpringApplicationRunListeners { ...... // lsnrctl start public void started() { for (SpringApplicationRunListener listener : this.listeners) { listener.started(); } ...... } public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { ...... // Broadcast it @Override public void started() { this.initialMulticaster .multicastEvent(new ApplicationStartedEvent(this.application, this.args)); } ...... } public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { ...... @Override public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); // Using thread pool to turn on listener for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { Executor executor = getTaskExecutor(); if (executor != null) { executor.execute(new Runnable() { @Override public void run() { invokeListener(listener, event); } }); } else { invokeListener(listener, event); } } } ...... // Listeners are implemented by implementing the ApplicationListener interface protected void invokeListener(ApplicationListener listener, ApplicationEvent event) { ErrorHandler errorHandler = getErrorHandler(); if (errorHandler != null) { try { listener.onApplicationEvent(event); } catch (Throwable err) { errorHandler.handleError(err); } } else { try { listener.onApplicationEvent(event); } catch (ClassCastException ex) { if (ex.getMessage().startsWith(event.getClass().getName())) { // Possibly a lambda-defined listener which we could not resolve the generic event type for LogFactory.getLog(getClass()).debug("Non-matching event type for listener: " + listener, ex); } else { throw ex; } } } } }
###(2) Construct the default application parameter holding class
public class DefaultApplicationArguments implements ApplicationArguments { ...... public DefaultApplicationArguments(String[] args) { Assert.notNull(args, "Args must not be null"); this.source = new Source(args); this.args = args; } private static class Source extends SimpleCommandLinePropertySource { Source(String[] args) { super(args); } ...... } ...... } public class SimpleCommandLinePropertySource extends CommandLinePropertySource<CommandLineArgs> { public SimpleCommandLinePropertySource(String... args) { super(new SimpleCommandLineArgsParser().parse(args)); } ...... } public abstract class CommandLinePropertySource<T> extends EnumerablePropertySource<T> { ...... public CommandLinePropertySource(T source) { super(COMMAND_LINE_PROPERTY_SOURCE_NAME, source); } ...... } public abstract class EnumerablePropertySource<T> extends PropertySource<T> { public EnumerablePropertySource(String name, T source) { super(name, source); } ...... } public abstract class PropertySource<T> { ...... public PropertySource(String name, T source) { Assert.hasText(name, "Property source name must contain at least one character"); Assert.notNull(source, "Property source must not be null"); this.name = name; this.source = source; } ...... }
You can see that the initialization of the final resource actually goes to the PropertySource abstract class
###(3) Prepare to configure the relevant environment
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Get or create the configuration of the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); // Configure environment configureEnvironment(environment, applicationArguments.getSourceArgs()); listeners.environmentPrepared(environment); if (isWebEnvironment(environment) && !this.webEnvironment) { environment = convertToStandardEnvironment(environment); } return environment; } private ConfigurableEnvironment getOrCreateEnvironment() { // If there is currently an active configuration, return directly if (this.environment != null) { return this.environment; } // If it is a web application, create and return the configuration of a web application if (this.webEnvironment) { return new StandardServletEnvironment(); } // Create a standard configuration if none return new StandardEnvironment(); } // Configure environment protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { // Configure property resources configurePropertySources(environment, args); // Configuration profile configureProfiles(environment, args); } // Configure property resources protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) { MutablePropertySources sources = environment.getPropertySources(); if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) { sources.addLast( new MapPropertySource("defaultProperties", this.defaultProperties)); } if (this.addCommandLineProperties && args.length > 0) { String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME; if (sources.contains(name)) { PropertySource<?> source = sources.get(name); CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource(new SimpleCommandLinePropertySource( name + "-" + args.hashCode(), args)); composite.addPropertySource(source); sources.replace(name, composite); } else { sources.addFirst(new SimpleCommandLinePropertySource(args)); } } } // Configuration profile protected void configureProfiles(ConfigurableEnvironment environment, String[] args) { environment.getActiveProfiles(); Set<String> profiles = new LinkedHashSet<String>(this.additionalProfiles); profiles.addAll(Arrays.asList(environment.getActiveProfiles())); environment.setActiveProfiles(profiles.toArray(new String[profiles.size()])); }
From the above code, you can specify that the main work of this step is to deal with environment related configuration
###(4) Create Spring container
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context.annotation.AnnotationConfigApplicationContext"; public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext"; protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { // Determine and create the default web application container or default container contextClass = Class.forName(this.webEnvironment? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS); } catch (ClassNotFoundException ex) { throw new IllegalStateException("Unable create a default ApplicationContext please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass); }
To create a container using BeanUtils:
public static <T> T instantiate(Class<T> clazz) throws BeanInstantiationException { Assert.notNull(clazz, "Class must not be null"); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { return clazz.newInstance(); } catch (InstantiationException ex) { throw new BeanInstantiationException(clazz, "Is it an abstract class?", ex); } catch (IllegalAccessException ex) { throw new BeanInstantiationException(clazz, "Is the constructor accessible?", ex); } }
###(5) Preparation before container initialization
Including: setting the container environment, default container, initializing the container, starting the log, registering and starting the singleton bean, loading resources, broadcasting the ApplicationPreparedEvent event event to the corresponding listener for listening
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // Setting up the container's environment context.setEnvironment(environment); // Preset the container: get the bean from the BeanFactory of the container and register it, set the resource loader of the container, and set the class loader of the container postProcessApplicationContext(context); // Initializing the application initializer applyInitializers(context); // Preparing context information for listeners listeners.contextPrepared(context); // start log if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans // Register a specific startup instance context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments); // Print banner if (printedBanner != null) { context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); } // Load the sources // load resources Set<Object> sources = getSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[sources.size()])); // Broadcast the ApplicationPreparedEvent event event to the corresponding listener for listening listeners.contextLoaded(context); } protected void postProcessApplicationContext(ConfigurableApplicationContext context) { if (this.beanNameGenerator != null) { //Get the bean from the BeanFactory of the container and register it context.getBeanFactory().registerSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator); } if (this.resourceLoader != null) { //Set the container's resource loader if (context instanceof GenericApplicationContext) { ((GenericApplicationContext) context) .setResourceLoader(this.resourceLoader); } if (context instanceof DefaultResourceLoader) { // Set the classloader of the container ((DefaultResourceLoader) context) .setClassLoader(this.resourceLoader.getClassLoader()); } } } // Initializing the application initializer protected void applyInitializers(ConfigurableApplicationContext context) { // Traverse each initializer and call the corresponding initialize method for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } } // Broadcast the ApplicationPreparedEvent event event to the corresponding listener for listening 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)); }
##(6) Refresh container
There are many things to do inside the refresh method of Spring container: for example, the setting of BeanFactory, the execution of BeanFactoryPostProcessor interface, the execution of BeanPostProcessor interface, the resolution of automatic configuration class, the resolution of condition annotation, the initialization of internationalization, etc.
private void refreshContext(ConfigurableApplicationContext context) { // Refresh container refresh(context); // Register hook to close container if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); } // The refresh container actually calls the refresh method in the AbstractApplicationContext class public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext, DisposableBean { ...... public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { //Prepare for the refresh of application context: set the start time, set the container to be not closed, set the container to be the currently activated container, record the refresh log, initialize the placeholder in the property source (in fact, do nothing) and verify the necessary properties, etc prepareRefresh(); // Let the subclass refresh the internal bean factory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare bean factory prepareBeanFactory(beanFactory); try { // Allows postprocessing of bean factories in context subclasses postProcessBeanFactory(beanFactory); // Calling the factory handler to register the bean in the context invokeBeanFactoryPostProcessors(beanFactory); // Intercepts the bean created and registers it in the bean processor registerBeanPostProcessors(beanFactory); // Initialize some internationalization related properties in the Spring container. initMessageSource(); // Initialize event listener to context initApplicationEventMulticaster(); // Initializing other special bean s into specific context subclasses onRefresh(); // Check and register the listener registerListeners(); // Instantiate all legacy (non delay initialized) singletons finishBeanFactoryInitialization(beanFactory); // Last step: publish response event finishRefresh(); }catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + ex); } // Destroy the created instance to avoid resource waste destroyBeans(); // Reset activation flag cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset the common internal cache in Spring core, because we may no longer need the metadata of the singleton bean resetCommonCaches(); } } }
###Prepare for application context refresh
//Prepare for the refresh of application context: set the start time, set the container to be not closed, set the container to be the currently activated container, record the refresh log, initialize the placeholder in the property source (in fact, do nothing) and verify the necessary properties, etc protected void prepareRefresh() { // Set start time this.startupDate = System.currentTimeMillis(); // Set container not closed this.closed.set(false); // Set container to be the currently active container this.active.set(true); // Record refresh log if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); } // Placeholder in initialization property source initPropertySources(); // Verify necessary properties getEnvironment().validateRequiredProperties(); // Set to allow early collection of event listeners to be available at broadcast time this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>(); }
###Let the subclass refresh the internal bean factory
// protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; } } // The classes involved in refreshing the beanFactory inside the subclass are: GenericApplicationContext and AbstractRefreshableApplicationContext // GenericApplicationContext class: protected final void refreshBeanFactory() throws IllegalStateException { if (!this.refreshed.compareAndSet(false, true)) { throw new IllegalStateException( "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once"); } this.beanFactory.setSerializationId(getId()); } // AbstractRefreshableApplicationContext class: protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { // Destroy bean destroyBeans(); // Close beanFactory closeBeanFactory(); } try { // Create beanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); // Specifies an id for serialization purposes to deserialize this BeanFactory back to the BeanFactory object from that id as needed beanFactory.setSerializationId(getId()); // Customized internal beanFactory used by container customizeBeanFactory(beanFactory); // Load bean definition given by beanFactory loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } } // Judge whether there is beanFactory protected final boolean hasBeanFactory() { synchronized (this.beanFactoryMonitor) { return (this.beanFactory != null); } } // Close beanFactory protected final void closeBeanFactory() { synchronized (this.beanFactoryMonitor) { this.beanFactory.setSerializationId(null); this.beanFactory = null; } } // Create beanFactory protected DefaultListableBeanFactory createBeanFactory() { return new DefaultListableBeanFactory(getInternalParentBeanFactory()); } // Customized internal beanFactory used by container protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { if (this.allowBeanDefinitionOverriding != null) { beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.allowCircularReferences != null) { beanFactory.setAllowCircularReferences(this.allowCircularReferences); } } // The destroy bean is implemented in AbstractApplicationContext: protected void destroyBeans() { getBeanFactory().destroySingletons(); }
##Prepare beanFactory
####Get the BeanFactory(Spring Bean container) from the Spring container and make relevant settings to prepare for subsequent use:
#####1. Set classloader (used to load beans), expression resolver (to parse some expressions in bean definition), and attribute edit register (to register Attribute Editor)
#####2. Add ApplicationContextAwareProcessor, the BeanPostProcessor. Cancel the automatic injection of five interfaces, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware, ApplicationContextAware and EnvironmentAware. Because ApplicationContextAwareProcessor has done the implementation of these five interfaces
#####3. Set the bean corresponding to the special type. BeanFactory corresponds to the BeanFactory just obtained; the beans corresponding to the three interfaces ResourceLoader, ApplicationEventPublisher and ApplicationContext are all set as the current Spring container
#####4. Beans that inject some other information, such as environment, systemProperties, etc
// Prepare bean factory protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // Set classloader (used to load beans), expression resolver (to parse some expressions in bean definition), and attribute edit register (to register Attribute Editor) beanFactory.setBeanClassLoader(getClassLoader()); beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // Add ApplicationContextAwareProcessor, the BeanPostProcessor. Cancel the automatic injection of five interfaces, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware, ApplicationContextAware and EnvironmentAware. Because ApplicationContextAwareProcessor has done the implementation of these five interfaces beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); // Set the bean corresponding to the special type beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); // Register the bean s used by the early preprocessor to detect the internal ApplicationListeners beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // Detect LoadTimeWeaver and prepare to weave (if any, this should be for slicing) if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // Set temporary classloader for type matching beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } // Beans that inject some other information, such as environment, systemProperties, etc if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } }
##Postprocessing the bean factory in the context subclass
After BeanFactory is set, some subsequent BeanFactory operations can be performed.
Different Spring containers do different operations. For example, the GenericWebApplicationContext container will add a servletcontextawarprocessor to BeanFactory to call the setServletContext or setServletConfig method when initializing a bean of type ServletContextAware (the same principle as applicationcontextawarprocessor).
####postProcessBeanFactory method corresponding to annotationconfigeembeddedwebapplicationcontext:
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { // Calling the implementation of the parent class EmbeddedWebApplicationContext super.postProcessBeanFactory(beanFactory); // Check the basePackages property. If it is set, ClassPathBeanDefinitionScanner will be used to scan the beans under the basePackages package and register if (this.basePackages != null && this.basePackages.length > 0) { this.scanner.scan(this.basePackages); } // Check the annotatedClasses property. If it is set, AnnotatedBeanDefinitionReader will be used to register these beans if (this.annotatedClasses != null && this.annotatedClasses.length > 0) { this.reader.register(this.annotatedClasses); } }
The implementation of the parent class EmbeddedWebApplicationContext:
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { // bean to register WebApplicationContextServletContextAwareProcessor beanFactory.addBeanPostProcessor( new WebApplicationContextServletContextAwareProcessor(this)); // Unregister the bean of ServletContextAware beanFactory.ignoreDependencyInterface(ServletContextAware.class); }
####The postProcessBeanFactory method corresponding to AbstractRefreshableWebApplicationContext:
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { // Register the bean of ServletContextAwareProcessor beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig)); // Unregister the bean s of ServletContextAware and ServletConfigAware beanFactory.ignoreDependencyInterface(ServletContextAware.class); beanFactory.ignoreDependencyInterface(ServletConfigAware.class); // Register the scope of web application ("request", "session", "global session", "application") WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext); // Register web application environment related beans ("contextparameters", "contextattributes") WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig); }
// Register scope ("request", "session", "global session", "application") public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) { beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope()); beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false)); beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true)); if (sc != null) { ServletContextScope appScope = new ServletContextScope(sc); beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope); // Register as ServletContext attribute, for ContextCleanupListener to detect it. sc.setAttribute(ServletContextScope.class.getName(), appScope); } beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory()); beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory()); beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory()); beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory()); if (jsfPresent) { FacesDependencyRegistrar.registerFacesDependencies(beanFactory); } } // Register web application environment related beans ("contextparameters", "contextattributes") public static void registerEnvironmentBeans(ConfigurableListableBeanFactory bf, ServletContext servletContext, ServletConfig servletConfig) { if (servletContext != null && !bf.containsBean(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME)) { bf.registerSingleton(WebApplicationContext.SERVLET_CONTEXT_BEAN_NAME, servletContext); } if (servletConfig != null && !bf.containsBean(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME)) { bf.registerSingleton(ConfigurableWebApplicationContext.SERVLET_CONFIG_BEAN_NAME, servletConfig); } if (!bf.containsBean(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME)) { Map<String, String> parameterMap = new HashMap<String, String>(); if (servletContext != null) { Enumeration<?> paramNameEnum = servletContext.getInitParameterNames(); while (paramNameEnum.hasMoreElements()) { String paramName = (String) paramNameEnum.nextElement(); parameterMap.put(paramName, servletContext.getInitParameter(paramName)); } } if (servletConfig != null) { Enumeration<?> paramNameEnum = servletConfig.getInitParameterNames(); while (paramNameEnum.hasMoreElements()) { String paramName = (String) paramNameEnum.nextElement(); parameterMap.put(paramName, servletConfig.getInitParameter(paramName)); } } bf.registerSingleton(WebApplicationContext.CONTEXT_PARAMETERS_BEAN_NAME, Collections.unmodifiableMap(parameterMap)); } if (!bf.containsBean(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME)) { Map<String, Object> attributeMap = new HashMap<String, Object>(); if (servletContext != null) { Enumeration<?> attrNameEnum = servletContext.getAttributeNames(); while (attrNameEnum.hasMoreElements()) { String attrName = (String) attrNameEnum.nextElement(); attributeMap.put(attrName, servletContext.getAttribute(attrName)); } } bf.registerSingleton(WebApplicationContext.CONTEXT_ATTRIBUTES_BEAN_NAME, Collections.unmodifiableMap(attributeMap)); } }
####postProcessBeanFactory method in EmbeddedWebApplicationContext:
As above, the work is basically to register some specific beans or unregister some specific beans
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this)); beanFactory.ignoreDependencyInterface(ServletContextAware.class); }
####postProcessBeanFactory method in GenericWebApplication:
Register some specific beans or unregister some specific beans, set application scope and environment related beans
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext)); beanFactory.ignoreDependencyInterface(ServletContextAware.class); WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext); WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext); }
####postProcessBeanFactory method in ResourceAdapterApplicationContext:
Register or unregister specific bean s
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { beanFactory.addBeanPostProcessor(new BootstrapContextAwareProcessor(this.bootstrapContext)); beanFactory.ignoreDependencyInterface(BootstrapContextAware.class); beanFactory.registerResolvableDependency(BootstrapContext.class, this.bootstrapContext); // JCA WorkManager resolved lazily - may not be available. beanFactory.registerResolvableDependency(WorkManager.class, new ObjectFactory<WorkManager>() { @Override public WorkManager getObject() { return bootstrapContext.getWorkManager(); } }); }
####postProcessBeanFactory method in StaticWebApplicationContext:
Register some specific beans or unregister some specific beans, set application scope and environment related beans
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig)); beanFactory.ignoreDependencyInterface(ServletContextAware.class); beanFactory.ignoreDependencyInterface(ServletConfigAware.class); WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext); WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig); }
###Calling the factory handler to register the bean in the context
Find the processor in the Spring container that implements the BeanFactoryPostProcessor interface and execute it. The Spring container is delegated to the invokeBeanFactoryPostProcessors method of the PostProcessorRegistrationDelegate.
Two interfaces are introduced:
1. BeanFactoryPostProcessor: used to modify the definition of beans that already exist in the Spring container, and use ConfigurableListableBeanFactory to process beans
2. BeanDefinitionRegistryPostProcessor: inherits beanfactory postprocessor. It has the same function as beanfactory postprocessor, but uses BeanDefinitionRegistry to process beans
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor) if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
Find the bean of type BeanDefinitionRegistryPostProcessor from the Spring container (these processors are registered in the container when the container is first created by constructing AnnotatedBeanDefinitionReader), and then execute them according to the priority. The priority logic is as follows:
1. The BeanDefinitionRegistryPostProcessor that implements the PriorityOrdered interface finds all of them, sorts them, and executes them in turn
2. Find out the BeanDefinitionRegistryPostProcessor that implements the Ordered interface, and then execute it in turn after sorting
3. BeanDefinitionRegistryPostProcessor that does not implement the priorityorded and Ordered interfaces finds and executes one by one
Next, find the implementation class of the BeanFactoryPostProcessor interface from the Spring container, and then execute it (if the processor has already been executed, ignore it). The search rule here is the same as the above search for BeanDefinitionRegistryPostProcessor, first find PriorityOrdered, then Ordered, and finally neither.
What needs to be explained here is that the ConfigurationClassPostProcessor is the highest priority processor to be executed (implementing the PriorityOrdered interface). This ConfigurationClassPostProcessor will go to BeanFactory to find all beans with @ Configuration annotation, and then use ConfigurationClassParser to parse this class. There is a map < ConfigurationClass in the ConfigurationClassParser, The configurationClasses attribute of the ConfigurationClass > type is used to save the parsed class. ConfigurationClass is a encapsulation of the Configuration class to be parsed. The annotation information of the Configuration class, the method modified by @ Bean annotation, the information modified by @ ImportResource annotation, and the ImportBeanDefinitionRegistrar are stored in this encapsulation class.
Another reason that ConfigurationClassPostProcessor is processed first is that if there is a custom beanfactory PostProcessor in the program, the PostProcessor must be resolved through ConfigurationClassPostProcessor before it can be found and executed by Spring container. (if the configuration class PostProcessor is not executed first, the Processor will not be parsed. If it is not parsed, it will not be executed.).
class PostProcessorRegistrationDelegate { public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { // First find out the bean of type BeanDefinitionRegistryPostProcessor Set<String> processedBeans = new HashSet<String>(); if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<BeanFactoryPostProcessor>(); List<BeanDefinitionRegistryPostProcessor> registryPostProcessors =new LinkedList<BeanDefinitionRegistryPostProcessor>(); for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { BeanDefinitionRegistryPostProcessor registryPostProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; registryPostProcessor.postProcessBeanDefinitionRegistry(registry); registryPostProcessors.add(registryPostProcessor); } else { regularPostProcessors.add(postProcessor); } } // The FactoryBean is not initialized here, so that all regular beans are not initialized and are handed to the bean Factory for post initialization String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); // First, call BeanDefinitionRegistryPostProcessors that implement the PriorityOrdered interface List<BeanDefinitionRegistryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>(); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(beanFactory, priorityOrderedPostProcessors); registryPostProcessors.addAll(priorityOrderedPostProcessors); invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry); // Next, call BeanDefinitionRegistryPostProcessors that implement the Ordered interface postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); List<BeanDefinitionRegistryPostProcessor> orderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>(); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(beanFactory, orderedPostProcessors); registryPostProcessors.addAll(orderedPostProcessors); invokeBeanDefinitionRegistryPostProcessors(orderedPostProcessors, registry); // Finally, call other BeanDefinitionRegistryPostProcessors until they do not appear boolean reiterate = true; while (reiterate) { reiterate = false; postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName)) { BeanDefinitionRegistryPostProcessor pp = beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class); registryPostProcessors.add(pp); processedBeans.add(ppName); pp.postProcessBeanDefinitionRegistry(registry); reiterate = true; } } } // Now, call postProcessBeanFactory to call back all processed beans invokeBeanFactoryPostProcessors(registryPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); }else { // Call factory to process all instances registered in container invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); } // The FactoryBean is not initialized here, so that all regular beans are not initialized and are handed to the bean Factory for post initialization String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); // Separate between BeanFactoryPostProcessors that implement PriorityOrdered, Ordered, and the rest. List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); List<String> orderedPostProcessorNames = new ArrayList<String>(); List<String> nonOrderedPostProcessorNames = new ArrayList<String>(); for (String ppName : postProcessorNames) { if (processedBeans.contains(ppName)) { // skip - already processed in first phase above }else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); }else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); }else { nonOrderedPostProcessorNames.add(ppName); } } // First, call BeanFactoryPostProcessors that implement the PriorityOrdered interface sortPostProcessors(beanFactory, priorityOrderedPostProcessors); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); // Next, call BeanFactoryPostProcessors that implement the Ordered interface List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); for (String postProcessorName : orderedPostProcessorNames) { orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } sortPostProcessors(beanFactory, orderedPostProcessors); invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory); // Finally, call all other BeanFactoryPostProcessors List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); for (String postProcessorName : nonOrderedPostProcessorNames) { nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); } invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); // Clear metadata cache beanFactory.clearMetadataCache(); } }
###Intercepts the bean created and registers it in the bean processor
The bean of the BeanPostProcessor interface found in the Spring container and set to the BeanFactory property. After that, the bean processor will be called when the bean is instantiated.
This method is delegated to the registerbanpostprocessors method of the PostProcessorRegistrationDelegate class. The process here is similar to invokeBeanFactoryPostProcessors:
1. First, find out the BeanPostProcessor that implements the PriorityOrdered interface and sort it, then add it to the BeanPostProcessor collection of BeanFactory
2. Find out the BeanPostProcessor that implements the Ordered interface, sort it and add it to the BeanPostProcessor collection of BeanFactory
3. BeanPostProcessor that does not implement the PriorityOrdered and Ordered interfaces is added to the BeanPostProcessor collection of BeanFactory
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this); }
public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) { String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false); // Register BeanPostProcessorChecker record bean creates message when BeanPostProcessor is instantiated int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length; beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount)); // Separate between BeanPostProcessors that implement PriorityOrdered, Ordered, and the rest. List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>(); List<BeanPostProcessor> internalPostProcessors = new ArrayList<BeanPostProcessor>(); List<String> orderedPostProcessorNames = new ArrayList<String>(); List<String> nonOrderedPostProcessorNames = new ArrayList<String>(); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); priorityOrderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } }else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { orderedPostProcessorNames.add(ppName); }else { nonOrderedPostProcessorNames.add(ppName); } } // First, register the BeanPostProcessors that implement the PriorityOrdered interface sortPostProcessors(beanFactory, priorityOrderedPostProcessors); registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors); // Next, register the BeanPostProcessors that implement the Ordered interface List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>(); for (String ppName : orderedPostProcessorNames) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); orderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } } sortPostProcessors(beanFactory, orderedPostProcessors); registerBeanPostProcessors(beanFactory, orderedPostProcessors); // Now, register all regular BeanPostProcessors List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>(); for (String ppName : nonOrderedPostProcessorNames) { BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); nonOrderedPostProcessors.add(pp); if (pp instanceof MergedBeanDefinitionPostProcessor) { internalPostProcessors.add(pp); } } registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors); // Finally, re register all internal BeanPostProcessors sortPostProcessors(beanFactory, internalPostProcessors); registerBeanPostProcessors(beanFactory, internalPostProcessors); // Re register post processor to detect internal bean s as listeners and move them to the end of the processing chain beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext)); }
###Initialize some internationalization related properties in the Spring container.
protected void initMessageSource() { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) { this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class); // Make MessageSource aware of parent MessageSource. if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) { HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource; if (hms.getParentMessageSource() == null) { // Only set parent context as parent MessageSource if no parent MessageSource registered already. hms.setParentMessageSource(getInternalParentMessageSource()); } } if (logger.isDebugEnabled()) { logger.debug("Using MessageSource [" + this.messageSource + "]"); } } else { // Use empty MessageSource to be able to accept getMessage calls. DelegatingMessageSource dms = new DelegatingMessageSource(); dms.setParentMessageSource(getInternalParentMessageSource()); this.messageSource = dms; beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource); if (logger.isDebugEnabled()) { logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME + "': using default [" + this.messageSource + "]"); } } }
###Initializing other special bean s into specific context subclasses
A template method, different Spring containers do different things.
For example, in the annotationconfigembedded web applicationcontext, the createEmbeddedServletContainer method will be called to create the built-in Servlet container.
At present, spring boot only supports three built-in Servlet containers:
1,Tomcat
2,Jetty
3,Undertow
###Initialize broadcast event listener
protected void initApplicationEventMulticaster() { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class); if (logger.isDebugEnabled()) { logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]"); } }else { this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster); if (logger.isDebugEnabled()) { logger.debug("Unable to locate ApplicationEventMulticaster with name '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "': using default [" + this.applicationEventMulticaster + "]"); } } }
###Register listener
Add time listener in Spring container and time listener in BeanFactory to event broadcaster.
Then, if there is an early event, broadcast it.
protected void registerListeners() { // Register statically specified listeners first for (ApplicationListener<?> listener : getApplicationListeners()) { getApplicationEventMulticaster().addApplicationListener(listener); } // The FactoryBean is not initialized here, so that all regular beans are not initialized and are handed to the bean Factory for post initialization String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) { getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } // Publish early application events Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents; this.earlyApplicationEvents = null; if (earlyEventsToProcess != null) { for (ApplicationEvent earlyEvent : earlyEventsToProcess) { getApplicationEventMulticaster().multicastEvent(earlyEvent); } } }
Instantiate all legacy (non delay initialized) bean s
Instantiate all instances registered but not instantiated in BeanFactory (lazy load does not need to be instantiated).
For example, the classes parsed in the invokeBeanFactoryPostProcessors method according to various annotations will be initialized at this time.
The instantiation process various beanpostprocessors begin to work.
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // Initialize conversion service for this context. if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) { beanFactory.setConversionService(beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)); } // Register a default embedded value resolver if no bean post-processor // (such as a PropertyPlaceholderConfigurer bean) registered any before: // at this point, primarily for resolution in annotation attribute values. if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(new StringValueResolver() { @Override public String resolveStringValue(String strVal) { return getEnvironment().resolvePlaceholders(strVal); } }); } // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { getBean(weaverAwareName); } // Stop using the temporary ClassLoader for type matching. beanFactory.setTempClassLoader(null); // Allow for caching all bean definition metadata, not expecting further changes. beanFactory.freezeConfiguration(); // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); }
###Publish response event
Other things to do after refresh.
1. Initialize the lifecycle processor and set it to the Spring container (lifecycle processor)
2. Call the onRefresh method of the life cycle processor. This method will find out the classes in the Spring container that implement the SmartLifecycle interface and call the start method
3. Issue the ContextRefreshedEvent event to inform the corresponding ApplicationListener to respond
4. Call the registerApplicationContext method of LiveBeansView: if JMX related properties are set, this method will be called
5. Publish the EmbeddedServletContainerInitializedEvent event to inform the corresponding ApplicationListener to respond
####The Spring container's refresh process is the introduction of the above 11 methods. There are still a lot of contents. Here is just a brief introduction.
##(7) Additional actions after container creation
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { callRunners(context, args); } private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<Object>(); // Find out the implementation class of the ApplicationRunner interface in the Spring container runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); // Find out the implementation class of CommandLineRunner interface in Spring container runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); // Sort runners AnnotationAwareOrderComparator.sort(runners); // Traverse the runners and execute in turn for (Object runner : new LinkedHashSet<Object>(runners)) { // If it is ApplicationRunner, make the run method call of ApplicationRunner if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } // If it is CommandLineRunner, call the run method of CommandLineRunner if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } }
##(8) Broadcast the ApplicationReadyEvent event to the corresponding listener for listening
public void finished(ConfigurableApplicationContext context, Throwable exception){ // Loop execution of traversal Lister for (SpringApplicationRunListener listener : this.listeners) { callFinishedListener(listener, context, exception); } } private void callFinishedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context, Throwable exception) { try { listener.finished(context, exception); } catch (Throwable ex) { if (exception == null) { ReflectionUtils.rethrowRuntimeException(ex); } if (this.log.isDebugEnabled()) { this.log.error("Error handling failed", ex); } else { String message = ex.getMessage(); message = (message == null ? "no error message" : message); this.log.warn("Error handling failed (" + message + ")"); } } }
##(9) Return to container
##Summary
What happens when SpringBoot starts, no matter what method it invocation, constructs an instance of SpringApplication, and then calls the run method of this instance, which means that SpringBoot is started.
###Before the run method call, that is to say, the initialization work will be carried out during the construction of SpringApplication. During the initialization, the following things will be done:
1. Set the parameter sources to the SpringApplication property, which can be any type of parameter. In the example of this article, the sources is the class object of MyApplication
2. Determine whether it is a web program, and set it to the boolean property of webEnvironment
3. Find out all initializers. There are five by default, which are set to the initializers property
4. Find out all application listeners. There are 9 by default, which are set to the listeners property
5. Find the main class to run
After the ###SpringApplication structure is completed, the run method is invoked to start SpringApplication. When the run method executes, it will do the following things:
1. Construct a StopWatch to observe the execution of spring application
2. Find out all spring application runlisteners and encapsulate them in spring application runlisteners to listen for the execution of run methods. In the process of listening, it will be encapsulated as an event and broadcast to let the application listener generated in the initialization process listen
3. Construct the Spring container (ApplicationContext), and return
(1) To create a Spring container, determine whether it is a web environment. If yes, construct annotationconfigeembeddedwebapplicationcontext, otherwise construct AnnotationConfigApplicationContext
(2) The initializer generated during initialization starts to work at this time
(3) Refresh of Spring container (complete bean parsing, execution of various processor interfaces, parsing of condition annotation, etc.)
4. Find out the implementation classes of the ApplicationRunner and CommandLineRunner interfaces from the Spring container, sort them and execute them in turn