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 load
META-INF/spring.factories
:SpringApplicationgetSpringFactoriesInstances()
methods- Spring loaded
META-INF/spring.factories
:SpringFactoriesLoaderloadFactoryNames()
methods- Event processing core code: EventPublishingRunListener
- FailureAnalysis: FailureAnalysis
Start the summary
- As you can see intuitively, a lot is used during Spring startup
spring.factories
Configuration, lots of Spring Events.In the run ()
An event is emitted at each stage of method execution, such as at startupApplicationStartedEvent
Initialization,environment
Triggered whenApplicationEnvironmentPreparedEvent
Initialization,Spring Context
The triggerApplicationPreparedEvent
Is triggered at the end of the startupSpringApplicationEvent
And 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 is
AutoConfigurationImportSelector
Of the classselectImports()
Method, and he gets the autowiring configuration by scanning the mate-info/Spring. factories files under all the JAR packagesselectImports()
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.