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
@Configuration
Annotated Configuration classes, and one that doesn’t have @configuration, but does@Component
.@Import
.@ImportResouce
.@Service
.@ComponentScan
In 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
- Register the built-in BeanPostProcessor
- 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
- Whether the container has been around for ConfigurationClassPostProcessor Bean
- If there is no (the first run will not exist), can achieve through the constructor RootBeanDefinition ConfigurationClassPostProcessor BeanDefinition, RootBeanDefinition is a subclass of BeanDefinition
- 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.
- Packaged into BeanDefinition
- Determine if the condition is satisfied @condition
- Set the bean scope
- Parse the generic annotations (@lazy, @Primary, @dependson, @Role, @Description) and set the attributes to the beanDefinition
- 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
- Executed first realized PriorityOrdered BeanDefinitionRegistryPostProcessors interface. Register the handler after the bean is defined
- Then execute implement BeanDefinitionRegistryPostProcessors Ordered interface.
- 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:
- Scan for the @configuration class in the registered bean and get its scan path
- Sort the configuration classes
- 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.
- 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
- Set all BeanDefinitions to immutable to prevent bean definitions from changing during instantiation
- 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()
- Obtain all beanName values previously held in beanDefinitionNames
- Loop through beans to get the beans scoped as singletons
- FactoryBean = factoryBean = factoryBean = factoryBean = factoryBean
- The factoryBean, through the getBean instantiation, and to save the bean instance in the container (DefaultSingletonBeanRegistry. SingletonObjects)
- 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
- Clear the cache of context resources (such as ASM metadata in scans)
- 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).
- 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:
- through
BeanDefinitionReader
Read the bean configuration class from javA_config - Again by
ConfigurationClassPostProcessor
Parse configuration class - through
BeanDefinitionScanner
Scan path configured in the scan configuration class - by
ConfigurationClassPostProcessor
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 optionally. - At last,
beanFactory
thegetBean
Method to instantiate the bean.- It’s called during this process
registerBeanPostProcessors
Method 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
- It’s called during this process