version

  • Spring – the projects/spring – boot / 1.5 x
  • GitHub

SpringBoot Boot process

The source code

Activation:

// The classic boot method, annotated with @springBootApplication, uses the run() method
@SpringBootApplication
public class MediationEsApplicationTemp {
    public static void main(String[] args) { SpringApplication.run(MediationEsApplicationTemp.class, args); }}Copy the code

The Initialize () method of SpringApplication.java

// Initialize creates and initializes the SpringApplication
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
	return new SpringApplication(sources).run(args);
}

// Here is the initialization of the SpringApplication object
@SuppressWarnings({ "unchecked", "rawtypes" })
private void initialize(Object[] sources) {
	/ / here is MediationEsApplicationTemp. Class in the collection
	// We'll use sources later to parse the startup class
	if(sources ! =null && sources.length > 0) {
		this.sources.addAll(Arrays.asList(sources));
	}
	// Check if it is a Web application. This parameter will be used later to initialize the environment
	this.webEnvironment = deduceWebEnvironment();
	/ / here is to load the meta-inf/spring. The fatcories ApplicationContextInitializer corresponding class, through the beanfactory initialization object creation
	/ / load reference ` SpringApplication. GetSpringFactoriesInstances ` ()
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	// As above, here is the class to load the ApplicationListener object
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// Get your stack information, that is, your startup call link, and find the method named "main", record the className of the main method.
	this.mainApplicationClass = deduceMainApplicationClass();
}
Copy the code

The run() method of springApplication.java

public ConfigurableApplicationContext run(String... args) {
	// Start a stopwatch timer
	StopWatch stopWatch = new StopWatch();
	stopWatch.start();
	// Tools for manipulating the context, such as setting the context ID, setting the parent application context, adding listeners, refreshing the container, closing it, checking whether it is active, etc
	ConfigurableApplicationContext context = null;
	// A fault analysis object that intercepts startup exceptions and converts them into more readable information
	FailureAnalyzers analyzers = null;
	// Use headless mode, which means that the server does not need a display device, such as a monitor, keyboard, mouse, etc., and let the application work in this state.
	configureHeadlessProperty();
	/ / get the meta-inf/spirng. Factories in SpringApplicationRunListener object, then packaged into SpringApplicationRunListeners return
	// The SpringEvent mechanism is used here
	EventPublish = EventPublish = EventPublish = EventPublish
	SpringApplicationRunListeners listeners = getRunListeners(args);
	// Send the Spring startup event ApplicationStartedEvent. Methods listening for this event receive a message
	listeners.starting();
   
	try {
		// Parse the args arguments passed in to the ApplicationArguments object
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		// Initialize the environment, according to the previous webEnvironment to determine whether the initialization of the Web environment object, or ordinary objects
		// The core here is to parse the args argument we passed into the environment object
		/ / will send a last ApplicationEnvironmentPreparedEvent events to monitor his object
		ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
		/ / print the banner
		Banner printedBanner = printBanner(environment);
		// Create a context container to determine whether a Web environment object or a normal object is initialized based on the previous webEnvironment
		context = createApplicationContext();
		// Load FailureAnalyzers from meta-INF /spring.factories and fill them in FailureAnalyzers
		analyzers = new FailureAnalyzers(context);
		// Initialize the spring context to initialize its parameters,
		/ / for spring context initialization operation, will be called spring. Factories in ApplicationContextInitializer implementation class, call them in the initialize () method to do initialization
		// In this step, the BeanDefinitionLoader user is created to read spring configuration and convert it to internal IOC container data
		// At the end, he will batch ApplicationPreparedEvent
		prepareContext(context, environment, listeners, applicationArguments, printedBanner);
		/ / call the Spring applicationContext. The refresh (); Refresh the Spring container
		refreshContext(context);
		// Call the ApplicationRunner/CommandLineRunner implementation method run() to perform the following operations
		afterRefresh(context, applicationArguments);
		// Group the SpringApplicationEvent event
		listeners.finished(context, null);
		// The stopwatch stops the time
		stopWatch.stop();
		// Displays springboot startup time logs
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		return context;
	}
	catch (Throwable ex) {
		handleRunFailure(context, listeners, analyzers, ex);
		throw newIllegalStateException(ex); }}Copy the code

The code references

  • SpringBoot Boot class: SpringApplication
  • SpringBoot loadMETA-INF/spring.factories :SpringApplication getSpringFactoriesInstances()methods
  • Spring loadedMETA-INF/spring.factories:SpringFactoriesLoader loadFactoryNames()methods
  • Event processing core code: EventPublishingRunListener
  • FailureAnalysis: FailureAnalysis

Start the summary

  • As you can see intuitively, a lot is used during Spring startupspring.factoriesConfiguration, lots of Spring Events.
  • In the run ()An event is emitted at each stage of method execution, such as at startupApplicationStartedEventInitialization,environmentTriggered whenApplicationEnvironmentPreparedEventInitialization,Spring ContextThe triggerApplicationPreparedEventIs triggered at the end of the startupSpringApplicationEventAnd so on.
  • refreshContext(context)Is an important method for which many of the previous operations were prepared, and will be called in this stepapplicationContext.refresh()The refresh starts the Spring container.

We can see a lot of boxing and unboxing code in the source code. Being familiar with these boxing and unboxing objects helps you understand the Spring startup process.

SpringBoot automatic assembly

The source code

Start the

@SpringBootApplication
public class HelloWebSecurityApplication {
	public static void main(String[] args) { SpringApplication.run(HelloWebSecurityApplication.class, args); }}Copy the code

In the Refresh () method of the Spring Context, auto-assembly is triggered

// org.springframework.context.support.AbstractApplicationContext#refresh
@Override
public void refresh(a) throws BeansException, IllegalStateException {
	// Automatic assembly is triggered from here
	registerBeanPostProcessors(beanFactory);
}
Copy the code

@ SpringBootApplication annotations

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
// automatically assemble annotations
@EnableAutoConfiguration
// Scan the configuration under the package where the class resides, equivalent to 
      
       .
      
// excludeFilters filter classes according to certain rules. Filtertype. CUSTOM indicates a CUSTOM filter rule
@ComponentScan( excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

	// Use the exclude attribute of EnableAutoConfiguration
	@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude")Class<? >[] exclude()default {};

	// Use the excludeName attribute of EnableAutoConfiguration
	@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")
	String[] excludeName() default {};

	// Use the basePackages attribute for ComponentScan
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};
    
	// Use the basePackageClasses attribute for ComponentScan
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")Class<? >[] scanBasePackageClasses()default {};

}
Copy the code

@ EnableAutoConfiguration annotations

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
// EnableAutoConfiguration Interpreters for annotations
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	// Check whether automatic configuration is enabled in environment variables
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
	// Exclude automatic configuration based on classClass<? >[] exclude()default {};
	// Exclude an automatic configuration based on className
	String[] excludeName() default {};
}
Copy the code

EnableAutoConfigurationImportSelector @ EnableAutoConfiguration interpreter

@Deprecated
public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector {
	
	// Check whether automatic configuration is enabled in the environment Settings.
	@Override
	protected boolean isEnabled(AnnotationMetadata metadata) {
		if (getClass().equals(EnableAutoConfigurationImportSelector.class)) {
			return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
		}
		return true; }}Copy the code

Superclass AutoConfigurationImportSelector EnableAutoConfigurationImportSelector interpreter

public class AutoConfigurationImportSelector implements DeferredImportSelector.BeanClassLoaderAware.ResourceLoaderAware.BeanFactoryAware.EnvironmentAware.Ordered {

	// Scan out the meta-INF /spring.factories configuration under all packages here
	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if(! isEnabled(annotationMetadata)) {return NO_IMPORTS;
		}
		try {
			// Load metadata
			AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
					.loadMetadata(this.beanClassLoader);
			// Load the annotation attributes, exclude, excludeName
			AnnotationAttributes attributes = getAttributes(annotationMetadata);
			// Scan the automatic configuration of meta-INF/spring. factories
			List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
			// Delete duplicate configurations
			configurations = removeDuplicates(configurations);
			/ / sorting
			configurations = sort(configurations, autoConfigurationMetadata);
			// Get the data from exclude
			Set<String> exclusions = getExclusions(annotationMetadata, attributes);
			// Check whether the exclude class exists and is loadable, and remove the invalid configuration
			checkExcludedClasses(configurations, exclusions);
			// Remove the exclude class
			configurations.removeAll(exclusions);
			// Call OnClassCondition in Spring. factories to filter the validation of the configuration class
			configurations = filter(configurations, autoConfigurationMetadata);
			/ / load the classpath jars in the meta-inf/spring. Factories file AutoConfigurationImportListener implementation class
			/ / triggers fireAutoConfigurationImportEvents events
			/ / when fireAutoConfigurationImportEvents event is triggered, print out has been registered with the spring context @ the Configuration of the annotation of class, print out the registration is blocked to the spring
			fireAutoConfigurationImportEvents(configurations, exclusions);
			return configurations.toArray(new String[configurations.size()]);
		}
		catch (IOException ex) {
			throw newIllegalStateException(ex); }}}Copy the code

conclusion

  • As you can see, the core processing of autowiring in Springboot isAutoConfigurationImportSelectorOf the classselectImports()Method, and he gets the autowiring configuration by scanning the mate-info/Spring. factories files under all the JAR packages
  • selectImports()The exclude method removes some of the auto-configuration classes based on the exclude parameter. Many of the previous steps gracefully bring the exclude parameter to the classselectImports()Methods.
  • Autowiring is triggered when the Spring container starts.

Write your own starter

For example, writing a Jedis-based Redis Starter does only two things:

  • Expose the Jedis configuration and be able to read it in starter.
  • Create a Jedis object or tool based on the configuration and make it available to the caller.

Configure the Jedis property

// Write the property configuration
@ConfigurationProperties("zkn.redis")
public class RedisProperties {
    private String host;
    private Integer port = 6379;
    private Integer database = 0;
    private String password;
    private Integer timeout = 2000;
    private Integer maxActive = 8;
    private Integer maxTotal = 8;
	
    / / omit the Get/Set
}

// Add spring-configuration-metadata.json to meta-INF.
{
  "hints": []."groups": [{"name": "zkn.redis"."type": "cn.zkn.spring.boot.config.redis.property.RedisProperties"."sourceType": "cn.zkn.spring.boot.config.redis.property.RedisProperties"}]."properties": [{"sourceType": "cn.zkn.spring.boot.config.redis.property.RedisProperties"."name": "zkn.redis.host"."type": "java.lang.String"
    },
    {
      "sourceType": "cn.zkn.spring.boot.config.redis.property.RedisProperties"."name": "zkn.redis.port"."type": "java.lang.Integer"
    },
    {
      "sourceType": "cn.zkn.spring.boot.config.redis.property.RedisProperties"."name": "zkn.redis.database"."type": "java.lang.Integer"
    } 
    // Other attributes omitted...]}Copy the code

Expose yourself to Redis services or tools

// Write the auto-configuration class and read the configuration, initialize JedisPool, and expose your own service via @bean annotations
@Configuration
@ConditionalOnClass(RedisService.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {

    @Autowired
    private RedisProperties redisProperties;

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "zkn.redis", value = "enabled", havingValue = "true")
    public RedisService multiRedisConfigBeanPostProcessor(a) {
        JedisPool jedisPool = configJedisPool(redisProperties);
        return new RedisService(jedisPool);
    }


    /** * Configure the Jedis connection pool **@paramRedisProperties Redis configuration information *@returnJedis connection pool */
    private JedisPool configJedisPool(RedisProperties redisProperties) {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(redisProperties.getMaxTotal());
        jedisPoolConfig.setMaxIdle(redisProperties.getMaxActive());
        jedisPoolConfig.setBlockWhenExhausted(true);
        jedisPoolConfig.setTestOnCreate(true);
        jedisPoolConfig.setTestOnBorrow(true);
        jedisPoolConfig.setTestOnReturn(true);
        jedisPoolConfig.setTestWhileIdle(true);

        return newJedisPool(jedisPoolConfig, redisProperties.getHost(), redisProperties.getPort(), redisProperties.getTimeout(), redisProperties.getPassword(), redisProperties.getDatabase()); }}// Own Service
public class RedisService {
    private Jedis jedis ;
    public RedisService(JedisPool jedisPool) {
        jedis = jedisPool.getResource();
    }
    public Jedis getJedis(a) {
        return jedis;
    }
    
    // Write your own implementation here
}



"// Add spring.factories to the meta-inf file.
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  cn.zkn.spring.boot.config.redis.RedisAutoConfiguration
Copy the code

conclusion

Starter itself is easy to write and scales well, but complex designs, such as the use of multiple Redis instances, require Spring-related extension points and are more complex to write.