springboot2.1.7 startup analysis (three) SpringApplication run

table of Contents

 

context = createApplicationContext()

 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context)

prepareContext(context, environment, listeners, applicationArguments, printedBanner)

refreshContext(context)

afterRefresh(context, applicationArguments)

listeners.started(context)

callRunners(context, applicationArguments)

listeners.running(context)


context = createApplicationContext()

Because it is a servlet, it will instantiate the context of org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext. GenericApplicationContext is its parent class, so it will execute the GenericApplicationContext parameterless constructor, which is why the beanFactory in the ApplicationContext we see is DefaultListableBeanFactory.

public AnnotationConfigServletWebServerApplicationContext() {
   //用于以编程方式注册带注释的Bean类
   this.reader = new AnnotatedBeanDefinitionReader(this);
//一个bean定义扫描器,它检测类路径上的bean候选者,并使用给定的注册表( BeanFactory或ApplicationContext )注册相应的bean定义。
//通过可配置的类型过滤器检测候选类。 默认的过滤器包括用Spring的@Component , @Repository , @Service @Controller或@Controller型注释的@Component 
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}
public GenericApplicationContext() {
   this.beanFactory = new DefaultListableBeanFactory();
}

 new AnnotatedBeanDefinitionReader(this) will finally call the following constructor 

	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		Assert.notNull(environment, "Environment must not be null");
		this.registry = registry;
		this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
	}

 AnnotationConfigUtils.registerAnnotationConfigProcessors: Register all relevant annotation post processors in the given registry. Pick a few important brief descriptions:

  • ConfigurationClassPostProcessor: (Important, follow-up refresh will focus on) After that, the processors are sorted by priority, because any Bean method declared in the @Configuration class must register its corresponding Bean definition before any other BeanFactoryPostProcessor is executed. This is very important.
  • AutowiredAnnotationBeanPostProcessor: Simply put, it processes the injected annotations defined by spring. @Autowired @Value etc.
  • CommonAnnotationBeanPostProcessor: Simply put, it is to process some annotations under javax.annotation. @Resource etc.

new ClassPathBeanDefinitionScanner(this) will finally call the following constructor 

	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment, @Nullable ResourceLoader resourceLoader) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		this.registry = registry;

		if (useDefaultFilters) {
            //为@Component注册默认过滤器。这将隐式注册具有@Component元注释的所有注释,包括@Repository , @Service 和@Controller构造型注释
			registerDefaultFilters();
		}
		setEnvironment(environment);
        //设置ResourceLoader以用于资源位置。 这通常是ResourcePatternResolver实现。默认值为PathMatchingResourcePatternResolver ,也能够通过ResourcePatternResolver接口解析资源模式。
		setResourceLoader(resourceLoader);
	}

 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,new Class[] { ConfigurableApplicationContext.class }, context)

Get the SpringBootExceptionReporter from the META-INF/spring.factories file and instantiate it by reflection. At present, there is only one instance of FailureAnalyzers. The FailureAnalyzers constructor will load and instantiate the FailureAnalyzer from META-INF/spring.factories. The FailureAnalyzer is used to analyze the failure and Provide diagnostic information that can be displayed to the user.

prepareContext(context, environment, listeners, applicationArguments, printedBanner)

	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		applyInitializers(context);
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
	}

context.setEnvironment(environment) source code:

	public void setEnvironment(ConfigurableEnvironment environment) {
		super.setEnvironment(environment);
		this.reader.setEnvironment(environment);
		this.scanner.setEnvironment(environment);
	}

The environment is set for reader and scanner when initializing the context (in the AnnotationConfigServletWebServerApplicationContext constructor call chain above), but the environment set at that time is not instantiated from the prepareEnvironment in the SpringApplication at the beginning, but the context itself is instantiated, which is equivalent to two Environment. Here replace all the relevant environment in the context with the environment created in SpringApplication. After this method, only the environment in SpringApplication will exist, and the original environment in the context will be recycled.

  • postProcessApplicationContext(context): Apply any related post-processing ApplicationContext. SpringApplication subclasses can apply other processing as needed (this method can be overridden)
  • applyInitializers(context): execute the initialize method of the ApplicationContextInitializer type instance when instantiating SpringApplication. The main thing is to register two BeanFactoryPostProcessors (CachingMetadataReaderFactoryPostProcessor, ConfigurationWarningsPostProcessor), add two ApplicationListeners (ServerPortInfoApplicationContextInitializer, ConditionEvaluationReportListener), register a singleton bean (ContextId), etc.
  • Set<Object> sources = getAllSources(): returns an immutable collection of all sources that will be added to the ApplicationContext when run(String...) is called. Currently there is only the startup class (set the startup class class to primarySources when SpringApplication is instantiated)
  • load(context, sources.toArray(new Object[0])): Load the current sourcebean definition into the context.
    protected void load(ApplicationContext context, Object[] sources) {
    		if (logger.isDebugEnabled()) {
    			logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    		}
    		BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    		......
    		loader.load();
    	}
    
    BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
    
    		this.sources = sources;
    		this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
    		this.xmlReader = new XmlBeanDefinitionReader(registry);
    		if (isGroovyPresent()) {
    			this.groovyReader = new GroovyBeanDefinitionReader(registry);
    		}
    		this.scanner = new ClassPathBeanDefinitionScanner(registry);
    		this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
    	}

    The load method first creates a new BeanDefinitionLoader, which loads the bean into the specified BeanDefinitionRegistry (AnnotationConfigServletWebServerApplicationContext inherits from BeanDefinitionRegistry). It can be seen that BeanDefinitionLoader contains various instance objects for loading bean definitions.

    private int load(Object source) {
    		Assert.notNull(source, "Source must not be null");
    		if (source instanceof Class<?>) {
    			return load((Class<?>) source);
    		}
    		if (source instanceof Resource) {
    			return load((Resource) source);
    		}
    		if (source instanceof Package) {
    			return load((Package) source);
    		}
    		if (source instanceof CharSequence) {
    			return load((CharSequence) source);
    		}
    		throw new IllegalArgumentException("Invalid source type " + source.getClass());
    	}

    As can be seen from the above load method, the source types of bean definitions that can be loaded include: Class, Resource, Package, and CharSequence. Class is processed by AnnotatedBeanDefinitionReader, Resource is processed by XmlBeanDefinitionReader, Package is processed by ClassPathBeanDefinitionScanner, and CharSequence is special, which is processed in the order of Class, Resource, and Package, and which processing is successful depends on which processing.

     The current source is of type calss, so it is processed by AnnotatedBeanDefinitionReader. Eventually, the doRegisterBean method of AnnotatedBeanDefinitionReader will be called.

    doRegisterBean: Register a bean from a given bean class and derive its metadata from the annotations in the class declaration.

<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {

		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

		abd.setInstanceSupplier(instanceSupplier);
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		abd.setScope(scopeMetadata.getScopeName());
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		if (qualifiers != null) {
			for (Class<? extends Annotation> qualifier : qualifiers) {
				if (Primary.class == qualifier) {
					abd.setPrimary(true);
				}
				else if (Lazy.class == qualifier) {
					abd.setLazyInit(true);
				}
				else {
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				}
			}
		}
		for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
			customizer.customize(abd);
		}

		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}

 The call chain of new AnnotatedGenericBeanDefinition(annotatedClass) is as follows.

1. The AnnotatedGenericBeanDefinition bean definition first sets the type beanClass of the current bean

2. The implementation class of the abstract metadata interface ClassMetadata of the instantiated class. Simply put, the interface is to obtain various information of the class through the class. For example, whether it is abstract, whether it is final, etc. Among them, StandardAnnotationMetadata will get the annotations on the class. StandardClassMetadata will store the class type

public AnnotatedGenericBeanDefinition(Class<?> beanClass) {
		setBeanClass(beanClass);
		this.metadata = new StandardAnnotationMetadata(beanClass, true);
	}
public StandardAnnotationMetadata(Class<?> introspectedClass, boolean nestedAnnotationsAsMap) {
		super(introspectedClass);
		this.annotations = introspectedClass.getAnnotations();
		this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
	}
public StandardClassMetadata(Class<?> introspectedClass) {
		Assert.notNull(introspectedClass, "Class must not be null");
		this.introspectedClass = introspectedClass;
	}

 conditionEvaluator

Processing @Conditional annotations, so it will not be skipped.

ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd)

abd.setScope(scopeMetadata.getScopeName())

Describe the scope characteristics of Spring managed beans, including scope names and scoped-proxy behavior. The default is singleton, and no scope proxy is created by default. The scope here is singleton. resolveScopeMetadata will obtain the AnnotationAttributes of the specified type annotation through the metadata information defined by the current bean. Obtaining AnnotationAttributes will call the following method of AnnotatedElementUtils, where annotationName is the className of @Scope type.

public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element,
			String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {

		AnnotationAttributes attributes = searchWithGetSemantics(element, null, annotationName,
				new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap));
		AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap);
		return attributes;
	}

The source code annotation of the above method: Get the first annotation of the specified annotationName in the annotation hierarchy above the provided element, and merge the attribute of the annotation with the matching attribute of the annotation in the lower level of the annotation hierarchy.
An attribute at a lower level in the annotation hierarchy overrides an attribute with the same name in a higher level, and @AliasFor semantics are fully supported within a single annotation and in the annotation hierarchy.
Compared to getAllAnnotationAttributes, using this method will stop searching the annotation hierarchy once the specified first annotation search algorithm annotationName has been found. As a result, other annotations with the specified annotationName will be ignored.

AnnotationConfigUtils.processCommonDefinitionAnnotations(abd)

static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
		AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
		if (lazy != null) {
			abd.setLazyInit(lazy.getBoolean("value"));
		}
		else if (abd.getMetadata() != metadata) {
			lazy = attributesFor(abd.getMetadata(), Lazy.class);
			if (lazy != null) {
				abd.setLazyInit(lazy.getBoolean("value"));
			}
		}

		if (metadata.isAnnotated(Primary.class.getName())) {
			abd.setPrimary(true);
		}
		AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
		if (dependsOn != null) {
			abd.setDependsOn(dependsOn.getStringArray("value"));
		}

		AnnotationAttributes role = attributesFor(metadata, Role.class);
		if (role != null) {
			abd.setRole(role.getNumber("value").intValue());
		}
		AnnotationAttributes description = attributesFor(metadata, Description.class);
		if (description != null) {
			abd.setDescription(description.getString("value"));
		}
	}

Find out whether the bean definition has a certain annotation or AnnotationAttributes corresponding to a certain annotation. Then set the bean definition to the corresponding property value. It can be seen that there are @Lazt, @Primary, etc.

BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName)

Wrapping bean definition and bean name


definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry)

Determine whether to create an agent according to the situation


BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry)

Register the bean definition to the registry.

  • listeners.contextLoaded(context): Broadcast the ApplicationPreparedEvent event and trigger the corresponding event.

ConfigFileApplicationListener: A BeanFactoryPostProcessor: PropertySourceOrderingPostProcessor instance is registered with the context; this instance will reorder our property sources later.

LoggingApplicationListener: registered a singleton bean named springBootLoggingSystem in beanFactory

The other listeners did nothing.

refreshContext(context)

Detailed follow-up

afterRefresh(context, applicationArguments)

Nothing has been done so far, this method can be overridden

listeners.started(context)

Broadcast the ApplicationStartedEvent event and trigger the corresponding event. Here we mainly focus on the Listener of TomcatMetricsBinder

callRunners(context, applicationArguments)

Call the run method of all beans that implement the ApplicationRunner and CommandLineRunner interfaces

listeners.running(context)

Broadcast the ApplicationReadyEvent event and trigger the corresponding event.

Guess you like

Origin blog.csdn.net/sinat_33472737/article/details/112800567