SpringBoot startup process

Keywords: Java Spring Spring Boot


 

Boot class

@SpringBootApplication annotation

The following three annotations are included

  • SpringBootConfiguration: the boot class, the main Configuration class identified as springboot, has the function of @ Configuration annotation
  • @EnableAutoConfiguration: enables automatic configuration. You can exclude classes that do not need to apply automatic configuration through the exclude property
  • @ComponentScan: automatically scan and load qualified beans, and load the definitions of these beans into the spring container. By default, only the package and its sub packages of the boot class are scanned. You can specify the package to be scanned through the scanBasePackages property, which is equivalent to the basePackages property of @ ComponentScan

 

@Implementation principle of EnableAutoConfiguration automatic configuration

The automatic configuration of springboot is realized through the @ EnableAutoConfiguration annotation in @ SpringBootApplication,

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

@EnableAutoConfiguration contains 2 comments

  • @AutoConfigurationPackage: Specifies the package to apply autoconfiguration
  • @Import(AutoConfigurationImportSelector.class): use @ import to collect and register bean definitions related to specific scenarios
     

AutoConfigurationImportSelector class

/**
 * Return the auto-configuration class names that should be considered. 
 * By default: this method will load candidates using SpringFactoriesLoader with getSpringFactoriesLoaderFactoryClass().
 */
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	//Call the static method loadFactoryNames() of springfactoryesloader to load
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}

 

loadFactoryNames() method of springfactoryesloader

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	//Get class loader
    ClassLoader classLoaderToUse = classLoader;
    if (classLoader == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }

    String factoryTypeName = factoryType.getName();
    //Call the loadSpringFactories() method of the current class: use the class loader to load each META-INF/spring.factories file in the classpath
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

 

loadSpringFactories() method of SpringFactoriesLoader

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    Map<String, List<String>> result = (Map)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        HashMap result = new HashMap();

        try {
        	//Get the META-INF/spring.factories files in the classpath
            Enumeration urls = classLoader.getResources("META-INF/spring.factories");
			
			//Iterate over all META-INF/spring.factories files and parse their configurations
            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    String[] var10 = factoryImplementationNames;
                    int var11 = factoryImplementationNames.length;

                    for(int var12 = 0; var12 < var11; ++var12) {
                        String factoryImplementationName = var10[var12];
                        ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                            return new ArrayList();
                        })).add(factoryImplementationName.trim());
                    }
                }
            }

            result.replaceAll((factoryType, implementations) -> {
                return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
            });
            cache.put(classLoader, result);
            return result;
        } catch (IOException var14) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
        }
    }
}

 

Spring boot starter contains the dependency of spring boot autoconfigure. Spring boot autoconfigure class library provides some common automatic configurations for spring boot applications, such as jdbc, redis, mongodb, web, jpa, json, etc. The following is part of META-INF/spring.factories of spring boot autoconfigure

# AutoConfigureCache auto-configuration imports
org.springframework.boot.test.autoconfigure.core.AutoConfigureCache=\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration

# AutoConfigureDataCassandra auto-configuration imports
org.springframework.boot.test.autoconfigure.data.cassandra.AutoConfigureDataCassandra=\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration

# AutoConfigureDataJdbc auto-configuration imports
org.springframework.boot.test.autoconfigure.data.jdbc.AutoConfigureDataJdbc=\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration

key specifies the annotation, and value specifies the class to which the annotation is applied. These classes end in AutoConfiguration and are automatically configured classes.
 

Take an automatic configuration class org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration of JDBC as an example

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ JdbcTemplate.class, TransactionManager.class })  //Filter the configuration items conforming to the current configuration through the @ ConditionOn series annotation
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnSingleCandidate(DataSource.class)
	static class JdbcTransactionManagerConfiguration {

		@Bean
		@ConditionalOnMissingBean(TransactionManager.class)
		DataSourceTransactionManager transactionManager(Environment environment, DataSource dataSource,
				ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
			DataSourceTransactionManager transactionManager = createTransactionManager(environment, dataSource);
			transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
			return transactionManager;
		}

		private DataSourceTransactionManager createTransactionManager(Environment environment, DataSource dataSource) {
			return environment.getProperty("spring.dao.exceptiontranslation.enabled", Boolean.class, Boolean.TRUE)
					? new JdbcTransactionManager(dataSource) : new DataSourceTransactionManager(dataSource);
		}

	}

}

These automatic configuration classes configure a large number of default bean s and generate instances into the spring container.

 

Summary: when Spring Boot is started, obtain the values specified by EnableAutoConfiguration from META-INF/spring.factories under the classpath path, and import these values into the container as automatic configuration classes.

 

SpringApplication.run() method

main() method of boot class

public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
}

 

SpringApplication.run() actually calls the following method

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return (new SpringApplication(primarySources)).run(args);
}

This method does two things: instantiate SpringApplication and execute the run() method

 

Instantiating spring application actually calls the following methods, mainly doing four things

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.sources = new LinkedHashSet();
    this.bannerMode = Mode.CONSOLE;
    this.logStartupInfo = true;
    this.addCommandLineProperties = true;
    this.addConversionService = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = Collections.emptySet();
    this.isCustomEnvironment = false;
    this.lazyInitialization = false;
    this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
    this.applicationStartup = ApplicationStartup.DEFAULT;
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    //Infer the application type, whether it is a normal project or a web project
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //Gets and sets the initializer
    this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //Get and set listener
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    //Infer and set the main class
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

 

run() method

public ConfigurableApplicationContext run(String... args) {
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	//Create parent context BootstrapContext
	DefaultBootstrapContext bootstrapContext = createBootstrapContext();
	ConfigurableApplicationContext context = null;
	configureHeadlessProperty();
	//Instantiate SpringApplicationRunListener through reflection according to the configuration file spring.factories
	SpringApplicationRunListeners listeners = getRunListeners(args);
	//Publish event listening, create a new time multicast and register the ApplicationListener obtained at the beginning with the multicast
	listeners.starting(bootstrapContext, this.mainApplicationClass);
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		//Environment variables are prepared according to the type and based on the parent context
		ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
		configureIgnoreBeanInfo(environment);
		//Print banner
		Banner printedBanner = printBanner(environment);
		//Create application context
		context = createApplicationContext();
		context.setApplicationStartup(this.applicationStartup);
		//Prepare context
		prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
		//Refresh context
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		listeners.started(context);
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		//According to the configuration file spring.factories, instantiate the springbootexception reporter through reflection to record the exceptions during the startup of the springboot application
		handleRunFailure(context, ex, listeners);
		throw new IllegalStateException(ex);
	}

	try {
		//Broadcast run events
		listeners.running(context);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

 

There are two contexts in springboot

1,bootstrap

  • The parent context of the application is loaded before the application, and the priority is higher than the application (the properties set by the bootstrap will not be overwritten by the application)
  • It is mainly used to load configurations from external sources, such as springcloud config, load configurations from the configuration center, and encrypt / decrypt configuration files
     

2,application

  • Application context, which is mainly used for automatic configuration and spring container related operations

 

Execution of the run() method

 

springboot startup process

Preparation stage

  • Configure Spring Boot Bean
  • Infer application type
  • Inference boot class
  • Load application context initializer

Start up phase

  • Run the listener to listen for Spring Boot events
  • Create Spring application context

 

Framework initialization

Frame start

Automatic assembly

Posted by gunslinger008 on Wed, 13 Oct 2021 11:34:41 -0700