First post a very good picture, SpringBoot boot structure diagram, picture from SpringBoot boot process analysis. The analysis in this article is based on Spring Boot 2.1.*, and the non-Spring code only has the following Boot main function:
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication application = newSpringApplication(AppServer.class); application.run(args); }}Copy the code
The constructor
SpringApplication constructor instantiates the initialization of the context of various interfaces – ApplicationContextInitializer and listeners – ApplicationListener, note the instantiation, This is not done through annotations and package sweeping as usual with Spring Components, but through a loading method that is independent of the Spring context so that various configurations can be done before Spring is finished booting.
The Spring workaround is to log the fully qualified name of the interface as the key and the fully qualified name of the implementation class as the value in the meta-INF/Spring.Factories file of the project. The SpringFactoriesLoader utility class provides static methods for class loading and caching. The Spring. factories are the core configuration file for Spring Boot, as explained below.
The other thing that is interesting is the two generalized methods, one of the main goals of the Spring Boot project is to automate the configuration, and one of the ways that Spring Boot is going to judge is to check whether the core classes are present in the system or not.
public SpringApplication(ResourceLoader resourceLoader, Class
... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();// What web container is enabled by the core class
// Instantiate the initializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// instantiate the listener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
Copy the code
Run
After initialization, we enter the run method, which completes all Spring startup processes: Prepare the Environment — publish event — create context, bean — refresh context — end, interspersed with a lot of listener action, and much of the logic is performed by the implementation class of the various listeners, so before analyzing the run method, let’s take a look at the role of the various core listeners and interfaces.
ConfigurableApplicationContext
Have to say, using IDEA analysis source code is really convenient, directly generated interface UML class diagram:
As opposed to read-onlyApplicationContext
In terms ofConfigurableApplicationContext
Interfaces are provided to configure context, such as setting the Environment, listeners, aspect classes, hooks to close context, and interfaces to refresh context. By default, the interface is read-only and preceded by the interface nameConfigurable
This corresponds to a new interface that provides a configurable interface — a form of inheritance found in many of Spring’s configuration-related interfaces, for exampleConfigurableEnvironment
andEnvironment
,ConfigurablePropertyResolver
andPropertyResolver
,ConfigurableBeanFactory
andBeanFactory
And so on.
Lifecycle is the start/stop interface for controlling the Lifecycle and querying the current running status. ApplicationContext is the central configuration interface for the configuration context. Inherits many other configuration interfaces, which themselves provide a read-only interface to query context archive information such as ids, application names, and a factory for building autowaging beans (the comments officially say that this interface provides a factory for registering beans outside of context, But it turns out to be the same object as the factory that @autowired got in the program…) . Simply write down the superclass interface that ApplicationContext inherits.
EnvironmentCapable
Provides an Environment interface.MessageSource
Internationalize the resource interface.ApplicationEventPublisher
Event publisher.ResourcePatternResolver
Resource loader.HierarchicalBeanFactory
,ListableBeanFactory
Both inherit the root interface of the bean containerBeanFactory
In another blog, Spring’s Bean Factory analysis.ConfigurableEnvironment
Business code is typically written using read-only interfacesEnvironment
The interface is an abstraction of the running program environment and is central to saving the system configuration, which is editable during startupConfigurableEnvironment
. The UML class diagram of the interface is shown below, providing the merge parent environment, addactive profile
And interfaces for setting up the way configuration files are parsed.
One of the more important methods is MutablePropertySources getPropertySources(); This method returns an editable PropertySources that can be set up if there is a need to customize the environment’s PropertySources during startup.
EventPublishingRunListener
The listener is actually a is used for broadcasting radio Spring events, the realizing methods of SpringApplicationRunListener interface is packing a Spring and broadcast events, such as:
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}
Copy the code
Can see there are two types of broadcasting, one kind is when Spring are still in the start, through the inside of the listener SimpleApplicationEventMulticaster broadcast radio apparatus; One is to call the interface provided by the context directly to broadcast when the internal broadcaster is available after Spring has started.
Continue analyzing Run
Once you know some of the core interfaces, you can start Debug mode to Run the Run method. Since there are many method calls involved, the following code will split the source code with the method signature first.
First opened a stopwatch to statistics the startup time and printing (if open control word), log statement some need to use in the behind of variables, and then began to initialize SpringApplicationRunListener types of listeners, SpringApplicationRunListeners to the listener List for packaging, such as call. Starting () iterates through all listeners to call its internal. When starting () method.
public ConfigurableApplicationContext run(String. args){
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();// Enable the Settings so that the system does not have IO devices.
SpringApplicationRunListeners listeners = getRunListeners(args);// Initialize the listenerlisteners.starting(); .private SpringApplicationRunListeners getRunListeners(String[] args){ Class<? >[] types =newClass<? >[] { SpringApplication.class,String[].class };/ / SpringApplicationRunListener constructor parameter types
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<? >[] parameterTypes,Object. args) {
ClassLoader classLoader = getClassLoader();// Get the classloader from the current thread
//Spring's classloader loads classes from the registry file meta-INF/spring.Factories using the specified classloader, which returns the fully qualified name of the implementation class of the corresponding type
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);/ / instantiate
//Spring's ordering tool, which sorts classes that inherit the Ordered interface or @priority flag
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
Copy the code
Debugging, found that only EventPublishingRunListener registered for SpringApplicationRunListener implementation class, said before the registry is a used for broadcasting radio Spring events, If you go into the constructor and you see what listeners are bound to the broadcast, the role of each listener doesn’t go too far. Suffice it to say that if there is something in your project that needs to be integrated into the Spring framework, Can register SpringApplicationRunListener \ ApplicationListener implementation class, listen to Spring’s start events and perform integration logic. Of course, there are other methods, for example, Creating a Custom Starter with Spring Boot.
Moving on to the run method, the focus here is on the logic of preparing the Environment. First, Spring creates a ConfigurableEnvironment based on the web container type. The customizePropertySources method is overridden by the Environment of the different Web container types. This method injects different propertySources, such as Servlet Context Init params and other related parameters if an embedded Servlet container is enabled. The configuration write logic is then performed on the new Environment, mainly by writing the parameters set to the SpringApplication from the main method to the Environment. Then release ApplicationEnvironmentPreparedEvent events, do some binding after returned to the Environment.
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);// Encapsulate the arguments to the main method
// Initialize the parameters to fill the Environment
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
configureIgnoreBeanInfo(environment);Introspector.getbeaninfo (Class
beanClass, int flags).private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
// Create new \ to get the current Environment instance
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());// Configure parameters
listeners.environmentPrepared(environment);// Publish events
bindToSpringApplication(environment);// bind "spring.main" to the current application as SpEL
if (!this.isCustomEnvironment) {Convert the type of the environment, but the type is going to be the same as the generalized one here
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
/ / will existing configuration encapsulated into ConfigurationPropertySourcesPropertySource, seems to do SpEL, look not to understand ~
ConfigurationPropertySources.attach(environment);
return environment;
}
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {/ / the default open, will be injected into a set of conversion tool, such as StringToDurationConverter
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService(ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);// If main starts with default arguments or command line arguments, write them to environment
configureProfiles(environment, args);// If a profile is set when main starts, it is written to the ActiveProfiles of the environment
}
Copy the code
Moving on to the Run method, which creates a Spring Context instance, see another blog Spring Boot Context analysis for details. In short, different Context instances are created depending on the type of Web container.
Banner printedBanner = printBanner(environment);// Print the slogan
context = createApplicationContext();// Create a context instance
/ / exception to broadcast, the default have org. Springframework. Boot. Diagnostics. FailureAnalyzers
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, newClass[] { ConfigurableApplicationContext.class }, context); .Copy the code
Moving on to the run method, the next step is to finish loading the context you just created. Loading process to fill the Environment and to set parameters, then execute the registration to the spring. The factories ApplicationContextInitializer edge, if oneself realization aspect should pay attention to what the context has some information is now. Then release ApplicationContextInitializedEvent events, and then load the beans, and finally release ApplicationPreparedEvent events.
prepareContext(context, environment, listeners, applicationArguments,printedBanner); .private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
// If the application has beanNameGenerator, resourceLoader, it is injected into the context, and the conversion tool is also injected into the context
postProcessApplicationContext(context);
applyInitializers(context);// Call the initialized section
listeners.contextPrepared(context);/ / release ApplicationContextInitializedEvent event
if (this.logStartupInfo) {/ / log
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);// Inject arguments to the main method
if(printedBanner ! =null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
// Whether to allow overwriting if the bean name is the same, the default is false, and the same exception will be raised
((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
/ / here for the class is BootstrapImportSelectorConfiguration, rather than start to write their own, this class is registered in before BootstrapApplicationListener injection monitoring method
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));Load sources into the context
listeners.contextLoaded(context);// Publish the ApplicationPreparedEvent event
}
Copy the code
Back to the run method, after the context is instantiated and configured, the context is refreshed.
refreshContext(context); . AbstractApplicationContextpublic void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// The web container initializes its property and copies the listener
prepareRefresh();
// This returns the BeanFactory of the context
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/ / the beanFactory inject some standard components, such as ApplicationContextAwareProcessor, such as this
prepareBeanFactory(beanFactory);
try {
// A hook for the implementation class, such as injecting BeanPostProcessors, which is an empty method
postProcessBeanFactory(beanFactory);
// Call the aspect method
invokeBeanFactoryPostProcessors(beanFactory);
// Register the aspect bean
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
/ / bean factory registered a key of applicationEventMulticaster radio apparatus
initApplicationEventMulticaster();
// An empty hook for the implementation class to perform other refresh work
onRefresh();
// Register the listener with the listener
registerListeners();
// Instantiate uninstantiated beans
finishBeanFactoryInitialization(beanFactory);
/ / clear the cache, injection DefaultLifecycleProcessor, release ContextRefreshedEvent
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...resetCommonCaches(); }}}Copy the code
Back to the run method, the final logic is to publish the event that has been started and call the listener’s method.
. afterRefresh(context, applicationArguments);// A hook for the implementation class, in this case an empty method.
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);// Issue the ApplicationStartedEvent event
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);// Publish the ApplicationReadyEvent event
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
Copy the code