This is the 15th day of my participation in the August More text Challenge. For details, see: August More Text Challenge
Start the class
-
The SpringBoot application has a main entry, the main method, which calls the SpringApplication.run method to launch the program
-
@ SpringBootApplication comments:
- EnableAutoConfiguration: Automatically configures the Spring framework based on the declared dependencies of the application
- SpringBootConfiguration: Configuration class for the Spring IOC container in the form of JavaConfig
- @componentscan: ComponentScan that automatically discovers and assembles beans. By default, it scans packages in the path where the startup class is located
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(...) public @interfaceSpringBootApplication {... }Copy the code
The basic flow
-
The first part is to initialize the module and configure some basic environment variables, resources, constructors, and listeners
-
The second part implements the specific startup scheme of the application, including starting the monitoring module, loading the configuration environment module, and creating the context environment module
-
The third part is the automatic configuration module, which serves as the core of SpringBoot automatic configuration
1) to (2) collecting all kinds of conditions and callback interface, such as ApplictionContextInitializer, ApplictionListener - > notice started () 3. Create and prepare Environment -> Notify environmentPrepared() 4. Create and initialize ApplicationContext, ContextPrepared (); contextLoaded(); refresh ApplictionContext (); The end of theCopy the code
Implementation process
-
Start the static run method of the class
public static ConfigurableApplicationContext run(Class [] primarySources, String[] args) { return (new SpringApplication(primarySources)).run(args); } Copy the code
-
The SpringApplication constructor
public SpringApplication(ResourceLoader resourceLoader, Class ... primarySources) { // ... // Determine the application type, which is divided into responsive Web application, Servlet Web application, and non-Web application this.webApplicationType = WebApplicationType.deduceFromClasspath(); // ... // Set the initializer this.setInitializers( this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); // Set the listener this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); // Set the entry method this.mainApplicationClass = this.deduceMainApplicationClass(); } Copy the code
-
Set the initializer
this.setInitializers( this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); Copy the code
private <T> Collection<T> getSpringFactoriesInstances(Class
type, Class [] parameterTypes, Object... args) { ClassLoader classLoader = this.getClassLoader(); / /, depending on the type of type ApplicationContextInitializer. Class // Read the spring.factories from the meta-INF of the classpath and iterate over the key-value pairs Set<String> names = new LinkedHashSet( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // Instantiate objects according to names List<T> instances = this.createSpringFactoriesInstances( type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } Copy the codeThe spring.factories configuration file
org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener Copy the code
-
Setting the listener
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); Copy the code
org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer Copy the code
-
Run method
public ConfigurableApplicationContext run(String... args) { / / timer StopWatch stopWatch = new StopWatch(); stopWatch.start(); DefaultBootstrapContext bootstrapContext = this.createBootstrapContext(); // The application context to return ConfigurableApplicationContext context = null; // Set the java.awt.headless system property to true this.configureHeadlessProperty(); // Load the runtime listener SpringApplicationRunListeners listeners = this.getRunListeners(args); // Publish the start execution event listeners.starting(bootstrapContext, this.mainApplicationClass); try { // Handle the parameters in the launcher ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // Prepare the environment by passing in parameters based on the scanned listener objects and functions ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments); this.configureIgnoreBeanInfo(environment); / / set the Banner Banner printedBanner = this.printBanner(environment); // Create the Spring container context = this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); // Spring container preprocessing this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); / / - > trigger SpringApplicationRunListener contextPrepared execution this.refreshContext(context); // Spring container post processing this.afterRefresh(context, applicationArguments); stopWatch.stop(); // Logs are printed if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)) .logStarted(this.getApplicationLog(), stopWatch); } // Issue the end of startup event listeners.started(context); // Call the registered Runners, ApplicationRunner, and CommandLineRunner in turn this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, listeners); throw new IllegalStateException(var10); } try { // Publish the application context ready event listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null); throw newIllegalStateException(var9); }}Copy the code
-
The run method – loads the runtime listener
SpringApplicationRunListeners listeners = this.getRunListeners(args); Copy the code
// Get the listener that is running the listener, and send the corresponding event to the listener at the corresponding stage private SpringApplicationRunListeners getRunListeners(String[] args) { Class<? >[] types =new Class[]{SpringApplication.class, String[].class}; return new SpringApplicationRunListeners( logger, this.getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args), this.applicationStartup); } Copy the code
SpringApplicationRunListener class:
public interface SpringApplicationRunListener { // This is called immediately after the run method is called and can be used for very early initialization default void starting(ConfigurableBootstrapContext bootstrapContext) { starting(); } // When the environment is ready default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { environmentPrepared(environment); } // Call ApplicationContext when it is ready before the resource is loaded default void contextPrepared(ConfigurableApplicationContext context) {}// called when the application context is loaded but before it is refreshed default void contextLoaded(ConfigurableApplicationContext context) {}// The context has been refreshed and the application started and all CommandLineRunner and ApplicationRunner have not been called before default void started(ConfigurableApplicationContext context) {}// The context has been refreshed and the application started and all CommandLineRunner and ApplicationRunner have been called default void running(ConfigurableApplicationContext context) {}// called when the startup process fails default void failed(ConfigurableApplicationContext context, Throwable exception) {}}Copy the code
-
Run method – Environment preparation
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // Create Environment objects with different implementations based on different Web types ConfigurableEnvironment environment = getOrCreateEnvironment(); // Configure the environment this.configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); // The sending environment is ready to complete the event listeners.environmentPrepared(bootstrapContext, environment); DefaultPropertiesPropertySource.moveToEnd(environment); // Configure the activeProfile in the Environment object based on the spring.profiles. Active property in the command line parameters this.configureAdditionalProfiles(environment); // Bind the spring.main property of the environment to the SpringApplication object this.bindToSpringApplication(environment); // If the user manually sets webApplicationType using the spring.main.web-application-type property if (!this.isCustomEnvironment) { // Convert the environment object to the user set webApplicationType related type, because inherit the same parent class, directly strong environment = new EnvironmentConverter(getClassLoader()) .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; } Copy the code
-
The run method – prepareContext
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // Set the context context.setEnvironment(environment); this.postProcessApplicationContext(context); / / execution spring. Factories ApplicationContextInitializer object initialize method this.applyInitializers(context); // The publishing context prepares the completion event to all listeners listeners.contextPrepared(context); bootstrapContext.close(context); if (this.logStartupInfo) { this.logStartupInfo(context.getParent() == null); this.logStartupProfileInfo(context); } ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if(printedBanner ! =null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding( this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor( new LazyInitializationBeanFactoryPostProcessor()); } // Load the resource Set<Object> sources = this.getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // Load the bean into the context object this.load(context, sources.toArray(new Object[0])); // Send the context loading completion event listeners.contextLoaded(context); } Copy the code
-
Run method – refreshContext
private void refreshContext(ConfigurableApplicationContext context) { // Register hooks when the JVM is stopped if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException var3) { } } this.refresh((ApplicationContext)context); } protected void refresh(ConfigurableApplicationContext applicationContext) { applicationContext.refresh(); } Copy the code
Call the ConfigurableApplicationContext refresh method
public interface ConfigurableApplicationContext extends ApplicationContext.Lifecycle.Closeable { void refresh(a) throws BeansException, IllegalStateException; } Copy the code
There are three: ConfigurableApplicationContext implementation class
AbstractApplictionContext, ServletWebServerApplicationContext, ReactiveWebServerApplicationContext
AbstractApplictionContext is an abstract class, the other two class inherited AbstractApplictionContext
public void refresh(a) throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // Step 1: Prepare the preparation for updating the upper and lower prepareRefresh(); // Step 2: Get the BeanFactory inside the context ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Step 3: Prepare the BeanFactory prepareBeanFactory(beanFactory); try { // Step 4: Allow post-processing of bean factories in context subclasses postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); // Step 5: Call the factory BeanFactoryPostProcessor registered as a bean in the context invokeBeanFactoryPostProcessors(beanFactory); // Step 6: Register the interceptor created by the interceptor bean registerBeanPostProcessors(beanFactory); beanPostProcess.end(); // Step 7: initialize internationalization related MessageSource initMessageSource(); // Step 8: Initialize the container event announcer for publishing events initApplicationEventMulticaster(); // Step 9: Initialize some special beans onRefresh(); // Step 10: Register all listeners with the event broadcasters created in the first two steps registerListeners(); // Step 11: Finish the bean initialization (instantiate all singletons BeanDefinition) finishBeanFactoryInitialization(beanFactory); // Step 12: afterRefresh finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy singletons that have been created to avoid resident resource occupancy destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); throw ex; } finally { // Reset the public cache because some of the metadata used to create the bean may no longer be neededresetCommonCaches(); contextRefresh.end(); }}}Copy the code
The realization of the ServletWebServerApplicationContext:
@Override public final void refresh(a) throws BeansException, IllegalStateException { try { super.refresh(); } catch (RuntimeException ex) { WebServer webServer = this.webServer; if(webServer ! =null) { webServer.stop(); } throwex; }}Copy the code
Automatic configuration
-
@EnableAutoConfiguration
EnableAutoConfiguration is part of the @SpringBootApplication
@Import({AutoConfigurationImportSelector.class}) public @interfaceEnableAutoConfiguration {... }Copy the code
AutoConfigurationImportSelector SpringFactoriesLoader will all eligible @ Configuration Configuration class is loaded into the IOC container
-
AutoConfigurationImportSelector
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // Load the spring.factories configuration information List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); // ... return configurations; } Copy the code
-
SpringFactoriesLoader.loadFactoryNames
SpringFactoriesLoader is an abstract class with static properties defined in the class that define the path to which it loads resources
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; Copy the code
There are also three static methods:
- LoadFactories: Loads and instantiates the specified factoryClass
- LoadFactoryNames: Loads a collection of names for the specified factoryClass
- InstantiateFactory: Instantiates the specified factoryClass
Many xxxAutoConfiguration classes are configured with spring.factories. When loaded into the container, these configuration classes are full of @conditional annotations indicating the valid conditions, so that the corresponding content can be automatically configured under appropriate conditions
-
The sample
Hello.java
public class Hello { private String msg; public String getMsg(a) { return msg; } public void setMsg(String msg) { this.msg = msg; }}Copy the code
HelloProperties.java
@ConfigurationProperties(prefix = "hello") public class HelloProperties { private String msg; public String getMsg(a) { return msg; } public void setMsg(String msg) { this.msg = msg; }}Copy the code
HelloAutoConfiguration.java
/ / configuration class @Configuration // Add HelloProperties to the IOC container @EnableConfigurationProperties(HelloProperties.class) // Determine whether the Hello class exists in the classpath. If so, the class bean will be instantiated @ConditionalOnClass(Hello.class) // The configuration determines whether to instantiate beans of this class @ConditionalOnProperty(prefix="hello", value="enabled", matchIfMissing = true) public class HelloAutoConfiguration { @Autowired private HelloProperties helloProperties; @Bean // If there is no Hello bean in the container, configure a Hello bean @ConditionalOnMissingBean(Hello.class) public Hello hello(a) { Hello hello = new Hello(); hello.setMsg(helloProperties.getMsg()); returnhello; }}Copy the code
Create a new meta-INF /spring.factories in the resource directory
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.example.demo.pojo.HelloAutoConfiguration Copy the code
application.properties
hello.enabled=true hello.msg=abcde Copy the code
The configuration takes effect after the test
@SpringBootTest class DemoApplicationTests { @Autowired Hello hello; @Test void contextLoads(a) { System.out.println(hello.getMsg()); }}Copy the code