Since SpringApplication

To start SpringBoot, create a new class containing the main method and use SpringApplication.run to start the program:

@SpringBootApplication
public class AutoConfigApplication {

    public static void main(String[] args){ ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(AutoConfigApplication.class,args); }}Copy the code

SpringApplication. Run receives two parameters: primarySource, operation parameters (args), the code above using AutoConfigApplication. Class as primarySource. SpringApplication also has an instance method called run. Most of the startup of SpringBoot is done by the instance Run method, where constructing ApplicationContext is done by the createApplicationContext method:

protected ConfigurableApplicationContext createApplicationContext(a) { Class<? > contextClass =this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); }}catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); }}return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}
Copy the code

CreateApplicationContext constructs ApplicationContext from this.webApplicationType. Different environments use different instances. But all of the structure in this paper, the web environment will use AnnotationConfigApplicationContext class. Create AnnotationConfigApplicationContext will invoke the default constructor:

public AnnotationConfigApplicationContext(a) {
	this.reader = new AnnotatedBeanDefinitionReader(this);
	this.scanner = new ClassPathBeanDefinitionScanner(this);
}
Copy the code

AnnotationConfigApplicationContext default constructor to create two objects:

  • Reader (AnnotatedBeanDefinitionReader) : used to manually register beans
  • Scanner (ClassPathBeanDefinitionScanner) : used for scanning Component, the Repository, such as Service annotations

AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner registers some annotations processor, Registration way are AnnotationConfigUtils registerAnnotationConfigProcessors method of use:

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) {...if(! registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def =newRootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); }...return beanDefs;
	}
Copy the code

After final AnnotationConfigApplicationContext constructor completes ApplicationContext can have the following BeanDefinition:

After constructing the ApplicationContext SpringApplicaiton then loads primarySource, mentioned above primarySource at run time is passed (AutoConfigApplication. Class), No code will be attached during the loading process, just know that the ApplicaitonContext will end up with an AutoConfigApplication BeanDefinition:

summary

In general, Spring Application does these things:

  • Create AnnotationConfigApplicationContext
  • Load some processing after the annotation processor, such as: ConfigurationClassPostProcessor
  • willprimarySourceLoaded into the ApplicationContext

And the most important thing is, Now is a AnnotationConfigApplicationContext containing primarySource (AutoConfigApplication) and ConfigurationClassPostProcessor. Use a breakpoint to print the name of the bean in ApplicaitonContext before it refreshes.

When will @Configuration be resolved?

Even after a primarySource and ConfigurationClassPostProcessor processor, or need a executive’s entrance. ConfigurationClassPostProcessor is BeanDefinitionRegistryPostProcessor implementation class, BeanDefinitionRegistryPostProcessor as the ApplicationContext refresh operation is processed:

public void refresh(a) throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) { ... invokeBeanFactoryPostProcessors(beanFactory); . }}public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List
       
         beanFactoryPostProcessors)
        {.../ / find the name of all types of BeanDefinitionRegistryPostProcessor bean
	 	String[] postProcessorNames =
					beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true.false);
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
				processedBeans.add(ppName);
			}
		}
		sortPostProcessors(currentRegistryProcessors, beanFactory);
		registryProcessors.addAll(currentRegistryProcessors);
		/ / BeanDefinitionRegistryPostProcessor executioninvokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); . }private static void invokeBeanDefinitionRegistryPostProcessors( Collection
        postProcessors, BeanDefinitionRegistry registry) {

	for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
	    / / call postProcessBeanDefinitionRegistry methodpostProcessor.postProcessBeanDefinitionRegistry(registry); }}Copy the code

InvokeBeanDefinitionRegistryPostProcessors invokes BeanDefinitionRegistryPostProcessor postProcessBeanDefinitionRegistry method, Through the breakpoint debugging tools did confirm the ConfigurationClassPostProcessor in this step is processed:

Debug output postProcessors collection has a ConfigurationClassPostProcessor elements, illustrates the execution of the ConfigurationClassPostProcessor entry: no problem.

ConfigurationClassPostProcessor processor

ConfigurationClassPostProcessor first is whether the bean in the ApplicationContext @ Configuration annotation tag, ConfigurationClassParser is then used to parse the @ Configuration, parse ConfigurationClassPostProcessor @ the Configuration process roughly:

  1. Using ConfigurationClassUtils. CheckConfigurationClassCandidate check whether BeanDefinition @ the Configuration notes
  2. Sort @Configuration
  3. Use ConfigurationClassParser to parse information for @Configuration annotations
  4. Using analytical BeanDefinition ConfigurationClassBeanDefinitionReader
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); / / get all BeanDefinitio name String [] candidateNames = registry. GetBeanDefinitionNames (); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); / / if it is full, lite explain already processed the if (ConfigurationClassUtils. IsFullConfigurationClass (beanDef) | | ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); }} / / check for @ Configuration BeanDefinition annotation else if (ConfigurationClassUtils. CheckConfigurationClassCandidate (beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } // If (configcandidates.isempty ()) {return;} // If (configcandidates.isempty ()) {return; } // Sort @configuration configcandidates.sort ((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); . ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); Do {// Parse @configuration class parser. Parse (candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); / / read BeanDefinition if (this. Reader = = null) {this. Reader = new ConfigurationClassBeanDefinitionReader (registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); candidates.clear(); . } while (! candidates.isEmpty()); . }Copy the code

Finally, use the debugging tool to see that the AutoConfigApplication startup class in the example is not handled:

The configScript contains a BeanDefinition named autoConfigApplication, indicating that the autoConfigApplication will be resolved as a configuration class. But AutoConfigApplication does not use the @Configuration annotation, so why is it still considered a Configuration class? @Configuration is in the @SpringBootApplication annotation:

The @Configuration annotation listed on the red background is the meta-annotation of @SpringBootConfiguration.