preface

Spring is the most widely used open source framework, and the Spring Framework is the foundation of the Spring family bucket. The most important aspects of the Spring Framework are IOC and AOP. IOC is the foundation of the Spring Framework. Today we are going to parse IOC. In general IOC has two key points: 1. Create bean containers; 2. 2. Initialize the bean.

Source code analysis

This article uses JAVA_CONFIG (annotation) to interpret the Spring IOC source code.

Let’s start with a simple piece of code

The configuration class

@Configuration
@ComponentScan("com.zhouxh")
public class Main {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext applicationContext = 
        		new AnnotationConfigApplicationContext(Main.class);
		TestService testService = (TestService)applicationContext.getBean("testService");
		testService.sayHi();
		System.out.println("The end"); }}Copy the code

The service class

package com.zhouxh.service;
import org.springframework.stereotype.Service;

@Service("testService")
public class TestService {
	public void sayHi(a){
		System.out.println("hello spring"); }}Copy the code

Mark the Main class as a Configuration class with the @Configuration annotation, and specify the bean scan path with @ComponentScan(“com.zhouxh”).

The Spring IoC container loading process

Mind maps

1. AnnotationConfigApplicationContext instantiate the container

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Main.class);
Copy the code

A simple description of the argument construct:

  • You can receive multiple configuration classes, but typically only one configuration class is passed in
  • There are two cases of this configuration class. One is a belt in the traditional sense@ConfigurationAnnotated Configuration classes, and one that doesn’t have @configuration, but does@Component.@Import.@ImportResouce.@Service.@ComponentScanIn Spring, the former is called the Full configuration class and the latter is called the Lite configuration class. Lite configuration classes are also referred to as plain beans in some parts of this source code analysis.
public AnnotationConfigApplicationContext(Class
       ... componentClasses) {
	// Call the constructor with no arguments
	this(a);// Register the configuration class
	register(componentClasses);
	// The IOC container refreshes the interface
	refresh();
}
Copy the code

this()

When the no-argument constructor is called through this(), the superclass constructor is implicitly called

public AnnotationConfigApplicationContext(a) {
	// Execute the parent class no-argument construct and set the bean factory class
	/** * public GenericApplicationContext() { * this.beanFactory = new DefaultListableBeanFactory(); *} * /
	// Set the read configuration class
	this.reader = new AnnotatedBeanDefinitionReader(this);
	// Set the scan class, which is not used by default unless the externally displayed call scan() method specifies the scan path
	this.scanner = new ClassPathBeanDefinitionScanner(this);
}
Copy the code

AnnotatedBeanDefinitionReader 【 beanDefinition reader 】 mainly do two things

  1. Register the built-in BeanPostProcessor
  2. Register the relevant BeanDefinition

Based on the new AnnotatedBeanDefinitionReader (this) code tracking found really do things AnnotationConfigUtils. RegisterAnnotationConfigProcessors (enclosing registry); Since this method has a lot of code, only the core code is posted here.

if(! registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def =new RootBeanDefinition(ConfigurationClassPostProcessor.class);
	def.setSource(source);
	beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
Copy the code

There is a lot of similar code in this method. Its main purpose is to add built-in components to the container

  1. Whether the container has been around for ConfigurationClassPostProcessor Bean
  2. If there is no (the first run will not exist), can achieve through the constructor RootBeanDefinition ConfigurationClassPostProcessor BeanDefinition, RootBeanDefinition is a subclass of BeanDefinition
  3. We execute the registerPostProcessor method, which internally registers the Bean, and we do the same for other beans.

This method will eventually ContextAnnotationAutowireCandidateResolver, ConfigurationClassPostProcessor, bean scanning and registered 】 【 CommonAnnotationBeanPostPr Ocessor, AutowiredAnnotationBeanPostProcessor automatic injection 】 【 @autowired properties, PersistenceAnnotationBeanPostProcessor These five components are added to the Spring container.

register(componentClasses);

Register is a method for parsing the passed configuration class into beanDefinition and storing it in the beanDefinitionMap for instantiation.

  1. Packaged into BeanDefinition
  2. Determine if the condition is satisfied @condition
  3. Set the bean scope
  4. Parse the generic annotations (@lazy, @Primary, @dependson, @Role, @Description) and set the attributes to the beanDefinition
  5. Register beanDefinition with (put) beanDefinitionMap and wait for subsequent instantiation.
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
		@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
		@Nullable BeanDefinitionCustomizer[] customizers) {

	// Wrap beanClass with BeanDefinition
	AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
        Spring has an @condition annotation. If the Condition is not met, the bean will not be parsed
	if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
		return;
	}
	abd.setInstanceSupplier(supplier);
	// Set the bean scope spring default Singleton
	ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); abd.setScope(scopeMetadata.getScopeName()); String beanName = (name ! =null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
	// Parse the generic annotations (@lazy, @primary, @dependson, @Role, @Description) and set the attributes to the beanDefinition
	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(newAutowireCandidateQualifier(qualifier)); }}}if(customizers ! =null) {
		for (BeanDefinitionCustomizer customizer : customizers) {
			customizer.customize(abd);
		}
	}
	BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
	definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
	// Add beanDefinition to map cache
	BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
Copy the code

refresh();

So far, the testService class we want to inject has not been scanned. We simply wrapped the default built-in bean and configuration class as beanDefintion in the above method and registered it with (put) BeanDefintionMap.

The really important code is in this refresh() refresh method.

Mind maps

prepareRefresh();

To begin the preparation of the refresh method, there are only two lines of code that record the container start time, startup identity, and so on

// Initialize the property, which is overridden by the word class
initPropertySources();
// Verify that all necessary attributes have been resolved
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
Copy the code

InitPropertySources method, which we can override by inheriting the class and then adding some parameter to the Environment.

protected void initPropertySources(a) {
    System.out.println("Extended initPropertySource");
    // We added a username attribute to the Environment, so that we can use it later
    getEnvironment().getSystemProperties().put("username"."bobo");
    // The username attribute must be included in the Environment. If not, an exception will be thrown
    / / set here will be getEnvironment () validateRequiredProperties (); To the test.
    getEnvironment().setRequiredProperties("username");
}     
Copy the code

prepareBeanFactory(beanFactory)

Add the necessary components to the beanFactory code that require attention

// The BeanFactory interface is not registered as a resolvable type in a normal factory. MessageSource is registered as a Bean (and discovered for autoliring).
// Means that the following four types can be autoassembled in any component. The first parameter is the autoassembled type and the second field is the value of the autoassembled
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);

/ / add a rear processor: ApplicationListenerDetector, then place the processor implements the BeanPostProcessor interface
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
Copy the code

postProcessBeanFactory(beanFactory)

Spring provides extension points to personalize the beanFactory after subclasses inherit.

invokeBeanFactoryPostProcessors(beanFactory)

Pay attention to the following code:

public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List
       
         beanFactoryPostProcessors)
        
Copy the code

Parameters in beanFactoryPostProcessors not spring management spring beanFactoryPostProcessor but from the outside by adding the following code, if not by this method is to add a default empty

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.addBeanFactoryPostProcessor(xxx);
Copy the code

InvokeBeanFactoryPostProcessors methods exist in the three sections of similar code, here only paste a were analyzed

These three similar pieces of code mean that BeanFactoryPostProcessor executes in sequence

  1. Executed first realized PriorityOrdered BeanDefinitionRegistryPostProcessors interface. Register the handler after the bean is defined
  2. Then execute implement BeanDefinitionRegistryPostProcessors Ordered interface.
  3. Finally, execute the normal bean definition to register the postprocessor
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);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
Copy the code
invokeBeanDefinitionRegistryPostProcessors

Logic for the above code is roughly: choose the corresponding types of spring BeanFactoryPostProcessor, then by invokeBeanDefinitionRegistryPostProcessors method scanning bean out of the need to register, and added to the registry.

Call invokeBeanDefinitionRegistryPostProcessors method is ultimately call processConfigBeanDefinitions method

This method basically does the following:

  1. Scan for the @configuration class in the registered bean and get its scan path
  2. Sort the configuration classes
  3. Through the scan path, scan all classes and methods in the path that are modified by @Propertysource, @ComponentScan, @Import, @ImportResource, and @Bean and add them to the corresponding collection.
  4. Then, take beanDefinitions from the corresponding set and register them with (put) beanDefinitionMap.
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
	List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
	// This is the name of all beans that need to be registered
	String[] candidateNames = registry.getBeanDefinitionNames();
	// Loop through the @Configuration class in the bean to be registered
	for (String beanName : candidateNames) {
		BeanDefinition beanDef = registry.getBeanDefinition(beanName);
		if(beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) ! =null) {
			if (logger.isDebugEnabled()) {
				logger.debug("Bean definition has already been processed as a configuration class: "+ beanDef); }}else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
			configCandidates.add(newBeanDefinitionHolder(beanDef, beanName)); }}// Return immediately if no @Configuration classes were found
	if (configCandidates.isEmpty()) {
		return;
	}

	// Register first according to the @order annotation
	configCandidates.sort((bd1, bd2) -> {
		int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
		int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
		return Integer.compare(i1, i2);
	});

	// Detect any custom bean name generation strategy supplied through the enclosing application context
	SingletonBeanRegistry sbr = null;
	// If it is a singleton
	if (registry instanceof SingletonBeanRegistry) {
		sbr = (SingletonBeanRegistry) registry;
		if (!this.localBeanNameGeneratorSet) {
			BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
					AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
			if(generator ! =null) {
				this.componentScanBeanNameGenerator = generator;
				this.importBeanNameGenerator = generator; }}}if (this.environment == null) {
		this.environment = new StandardEnvironment();
	}
	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 the configuration class. Parse the @propertysource, @ComponentScan, @Import, @ImportResource, @Bean modified classes and methods in the scan path, and add them to the corresponding collection.
		parser.parse(candidates);
		parser.validate();

		Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
		configClasses.removeAll(alreadyParsed);

		// Read the model and create bean definitions based on its content
		if (this.reader == null) {
			this.reader = new ConfigurationClassBeanDefinitionReader(
					registry, this.sourceExtractor, this.resourceLoader, this.environment,
					this.importBeanNameGenerator, parser.getImportRegistry());
		}
                // Register beanDefinition from the corresponding set
		this.reader.loadBeanDefinitions(configClasses);
		alreadyParsed.addAll(configClasses);

		candidates.clear();
		// Determine if a new bean is registered and register it
		if (registry.getBeanDefinitionCount() > candidateNames.length) {
			String[] newCandidateNames = registry.getBeanDefinitionNames();
			Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
			Set<String> alreadyParsedClasses = new HashSet<>();
			for (ConfigurationClass configurationClass : alreadyParsed) {
				alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
			}
			for (String candidateName : newCandidateNames) {
				if(! oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName);if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && ! alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(newBeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; }}while(! candidates.isEmpty());// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
	if(sbr ! =null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
		sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
	}

	if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
		// Clear cache in externally provided MetadataReaderFactory; this is a no-op
		// for a shared cache since it'll be cleared by the ApplicationContext.
		((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); }}Copy the code

registerBeanPostProcessors(beanFactory)

Instantiate and register beanFactory beans that extend BeanPostProcessor. Such as: AutowiredAnnotationBeanPostProcessor (processing is decorated the @autowired annotation of bean and injection) RequiredAnnotationBeanPostProcessor (processing is @ the Required annotations modified method) CommonAnnotationBeanPostProcessor (processing @ PreDestroy, @ PostConstruct, @ the Resource such as the role of the more annotations), etc

InitMessageSource ()

Initialize the internationalization resource processor

initApplicationEventMulticaster()

Initializing the create event multicast.

registerListeners()

Register listeners

The above two methods and spring initApplicationEventMulticaster/registerListeners 】 【 mechanism of event listeners are closely related.

onRefresh()

Spring provides extension points that are used in SpringBoot

finishBeanFactoryInitialization()

Complete the initialization of the context’s bean factory, initializing all remaining singleton beans. Core code to focus on

  1. Set all BeanDefinitions to immutable to prevent bean definitions from changing during instantiation
  2. Instantiate all singleton beans
// Set all beanDefinitions to immutable to avoid changes to bean definitions during instantiation
beanFactory.freezeConfiguration();
// Instantiate all singleton beans
beanFactory.preInstantiateSingletons();
Copy the code
DefaultListableBeanFactory.preInstantiateSingletons()
  1. Obtain all beanName values previously held in beanDefinitionNames
  2. Loop through beans to get the beans scoped as singletons
  3. FactoryBean = factoryBean = factoryBean = factoryBean = factoryBean
  4. The factoryBean, through the getBean instantiation, and to save the bean instance in the container (DefaultSingletonBeanRegistry. SingletonObjects)
  5. Call the callback method for all instantiated beans
public void preInstantiateSingletons(a) throws BeansException {
	if (logger.isTraceEnabled()) {
		logger.trace("Pre-instantiating singletons in " + this);
	}

	// Obtain beanName previously stored in beanDefinitionNames
	List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

	// Loop the bean to get the singleton bean
	for (String beanName : beanNames) {
		RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
		if(! bd.isAbstract() && bd.isSingleton() && ! bd.isLazyInit()) {// Determine whether it is a factoryBean
			if (isFactoryBean(beanName)) {
				Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
				if (bean instanceofFactoryBean) { FactoryBean<? > factory = (FactoryBean<? >) bean;boolean isEagerInit;
					if(System.getSecurityManager() ! =null && factory instanceofSmartFactoryBean) { isEagerInit = AccessController.doPrivileged( (PrivilegedAction<Boolean>) ((SmartFactoryBean<? >) factory)::isEagerInit, getAccessControlContext()); }else {
						isEagerInit = (factory instanceofSmartFactoryBean && ((SmartFactoryBean<? >) factory).isEagerInit()); }if(isEagerInit) { getBean(beanName); }}}else {
				// A bean that does not inherit FacetoryBean is instantiated by getBeangetBean(beanName); }}}// Trigger callback methods for all instantiated beans
	for (String beanName : beanNames) {
		Object singletonInstance = getSingleton(beanName);
		if (singletonInstance instanceof SmartInitializingSingleton) {
			SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
			if(System.getSecurityManager() ! =null) {
				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
					smartSingleton.afterSingletonsInstantiated();
					return null;
				}, getAccessControlContext());
			}
			else{ smartSingleton.afterSingletonsInstantiated(); }}}}Copy the code

finishRefresh()

Publish the corresponding event

  1. Clear the cache of context resources (such as ASM metadata in scans)
  2. Initialize the Lifecycle handler for the context and refresh (find the beans in the Spring container that implement the Lifecycle interface and execute the start() method).
  3. The ContextRefreshedEvent event is published to inform the corresponding ApplicationListener of the action to take in response
protected void finishRefresh(a) {
	// Clear the resource cache of the context (such as ASM metadata from the scan).
	clearResourceCaches();

	// Initialize the lifecycle processor
	initLifecycleProcessor();

	// First propagate the refresh to the lifecycle processor.
	getLifecycleProcessor().onRefresh();

	// Push the context flush finished event to the appropriate listener
	publishEvent(new ContextRefreshedEvent(this));

	// Participate in LiveBeansView MBean, if active.
	LiveBeansView.registerApplicationContext(this);
}
Copy the code

conclusion

This completes the Spring IOC initialization and Spring Bean loading process.

Spring IOC initialization: Create the beanFactory instance objects, create BeanDefinitionReader, create BeanDefinitionScanner ContextAnnotationAutowireCandidateResolver, Configurati OnClassPostProcessor, bean scanning and registered 】 【 CommonAnnotationBeanPostProcessor, AutowiredAnnotationBeanPostProcessor 【 @autowired attribute automatically Into 】, PersistenceAnnotationBeanPostProcessor these five components are added to the spring container.

Spring Bean loading process:

  • throughBeanDefinitionReaderRead the bean configuration class from javA_config
  • Again byConfigurationClassPostProcessorParse configuration class
  • throughBeanDefinitionScannerScan path configured in the scan configuration class
  • byConfigurationClassPostProcessorOptionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally optionally.
  • At last,beanFactorythegetBeanMethod to instantiate the bean.
    • It’s called during this processregisterBeanPostProcessorsMethod to populate the bean with data
    • AutowiredAnnotationBeanPostProcessor (processing is decorated the @autowired annotation of bean and injection) RequiredAnnotationBeanPostProcessor (@ Required annotations modified method processing) CommonAnnotationBeanPostProcessor (processing @ PreDestroy, @ PostConstruct, @ the Resource such as the role of the more annotations), etc