Reading tips: TL;DR. The article contains a large number of source code. It takes a long time to read. It may take more than 20 minutes to read carefully.
Recall that the core function of spring is, after all, a container for providing so-called "beans" ", and is responsible for the connection between beans. As we know, beans have different scopes, i.e. scope, singleton, prototype, Session, or custom. Beans can also be lazy loaded. Therefore, the time to create a Bean may be any time at runtime. Spring uses BeanDefinition to describe metadata such as name, class, scope, etc. of a Bean and create it when necessary. The creation process naturally includes the process of automatic injection.
In this article, we focus on Bean management in Spring.
How to describe a Bean
Spring uses BeanDefinition to describe beans. As shown above, we only select a few representative ones to see. Based on the definition of BeanDefinition interface, we can know that beans in spring have the following characteristics:
- essential information
- name
- describe
- The Class object used to create the Bean
- Scope with scope
- singleton: globally unique instance
- prototype: create a new instance every time you get it
- You can have a parent-child relationship
- There can be dependencies, that is, the creation of one Bean depends on the existence of another Bean
- Delay loading
- Automatic assembly
- Set whether to participate in automatic assembly (based on type)
- If more than one type meets the requirements, the main type can be set
- You can customize how Bean instances are created
- Set the factory Bean and create it with another Bean
- Set the factory method and create an instance with it
- Life cycle management
- The initialization method initMethod can be specified
- destroyMethod can be specified
- There are roles. Currently, three roles are defined, but we haven't seen how to use them yet
- ROLE_APPLICATION: This Bean is the main body of the application. The default value is
- ROLE_SUPPORT: identifies that the Bean is the most other supported part
- Role_infrastruture: the Bean is supported as a pure infrastructure and is inaccessible to users
- Gets the value of the parameter of the constructor
- Get MutablePropertyValues, which corresponds to the property key value pair of the Bean, which will be used during automatic injection.
- BeanDefinition can wrap another BeanDefinition. Get the wrapped definition through BeanDefinition getoriginingbeandefinition().
As for other classes in the inheritance tree, they are different. Let's look at them in turn
AbstractBeanDefinition
The direct implementation of BeanDefinition, from which you can also see the default values of some features
- scope defaults to single instance
- Delay loading is off by default
- Auto injection is off by default
- The role defaults to ROLE_APPLICATION
RootBeanDefinition
To tell you the truth, I'm a little confused when I look at this class directly. That's what the original annotation said.
A root bean definition represents the merged bean definition that backs a specific bean in a Spring BeanFactory at runtime. It might have been created from multiple original bean definitions that inherit from each other, typically registered as GenericBeanDefinitions. A root bean definition is essentially the 'unified' bean definition view at runtime.
Root bean definitions may also be used for registering individual bean definitions in the configuration phase. However, since Spring 2.5, the preferred way to register bean definitions programmatically is the GenericBeanDefinition class. GenericBeanDefinition has the advantage that it allows to dynamically define parent dependencies, not 'hard-coding' the role as a root bean definition.
I understand the literal meaning, but I don't understand the meaning behind this string of English. What is MergedBean? You can refer to it for reference This article , the so-called merging means merging a parent-child BeanDefinition into a BeanDefinition. The specific process of "merging" will be analyzed in the following "how to create a Bean".
GenericBeanDefinition
Compared with AbstractBeanDefinition, the standard implementation of BeanDefinition has more implementation of parent-child relationship. We can directly use this class to create our own BeanDefinition. If we want to create a BeanDefinition injection container out of thin air, we can use it.
AnnotatedBeanDefinition
The direct extension interface of BeanDefinition exposes AnnotationMetadata to the caller. The problem is, what is AnnotationMetadata? It is the abstraction defined by Spring for the annotation of the specified class. Through it, annotation information can be obtained without loading the target class. Like ClassMetadata, it seems that Spring abstracts all aspects of Bean definition.
AnnotatedGenericBeanDefinition
At the same time, GenericBeanDefinition and AnnotatedBeanDefinition are implemented, which shows that it not only has the ability of a complete BeanDefinition, but also holds the annotation information on the original class of the Bean. What's the use of holding the annotation information? Of course, it is useful. The runtime can directly obtain the annotation solution of the Bean definition without loading the original class, so as to improve the performance.
ScannedGenericBeanDefinition
Like AnnotatedGenericBeanDefinition, as like as two peas.
Summary
Spring's description of beans is actually more than these categories, but I think the others are interference, so they are removed. Our focus is to see how spring abstracts and the role of abstractions at all levels through these definitions. It will not be used when writing code, but it can ensure that it will not be confused when looking at the source code.
Where does Bean definition come from
It is also the example of the previous article, the simplest application method.
fun main() { val context = AnnotationConfigApplicationContext("com.gitee.floyd.springme.core") println(context.getBean(Bean1::class.java)) }
We take the org.springframework.context.annotation.AnnotationConfigApplicationContext construction method as the entry to analyze the loading process of Bean definitions.
public AnnotationConfigApplicationContext(String... basePackages) { this(); scan(basePackages); ... ... } public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } public void scan(String... basePackages) { ... ... this.scanner.scan(basePackages); ... ... }
Here we introduce two new classes: AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner. Let's look at their capabilities first, and then look at the scanning process
AnnotatedBeanDefinitionReader
public class AnnotatedBeanDefinitionReader { // Bean definition registrar is a registration interface. Its implementation class generally holds all these registered bean definitions. The typical implementation class is a specific ApplicationContext private final BeanDefinitionRegistry registry; // The Bean name generator, AnnotationBeanNameGenerator, generates the name according to the @ Component annotation or its sub annotation private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE; // Scope meta information parser, AnnotationScopeMetadataResolver parses @ scope annotations private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver(); // Condition calculator for parsing @ Conditional annotation private ConditionEvaluator conditionEvaluator; ... ... // Register Bean public void registerBean(Class<?> beanClass) { doRegisterBean(beanClass, null, null, null, null); } ... ... // Actually register the bean, and the type and name of the incoming bean private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier, @Nullable BeanDefinitionCustomizer[] customizers) { // Create AnnotatedGenericBeanDefinition from class object AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass); // Determine whether the Bean definition needs to be ignored if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { return; } // Set a callback function when creating an instance as a replacement for a factory method abd.setInstanceSupplier(supplier); ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); // Add Scope to Bean definition abd.setScope(scopeMetadata.getScopeName()); // Generate bean name String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); // Handling general annotations: @ Lazy, @ Primary, @ DependsOn, @ Role, @ Description AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); // Determined according to the passed in modifier annotation 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)); } } } // Apply the incoming BeanDefinition customizer if (customizers != null) { for (BeanDefinitionCustomizer customizer : customizers) { customizer.customize(abd); } } // Packaged as BeanDefinitionHolder BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); // The scope proxy mode of Bean is encapsulated in BeanDefinitionHolder, which is set here definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); // Inject the processed BeanDefinitionHolder into the registry BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); } } // AnnotationConfigUtils.processCommonDefinitionAnnotations method static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { // Processing of delayed loading 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")); } } // primary processing if (metadata.isAnnotated(Primary.class.getName())) { abd.setPrimary(true); } // Dependent processing AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class); if (dependsOn != null) { abd.setDependsOn(dependsOn.getStringArray("value")); } // Role processing AnnotationAttributes role = attributesFor(metadata, Role.class); if (role != null) { abd.setRole(role.getNumber("value").intValue()); } // Described processing AnnotationAttributes description = attributesFor(metadata, Description.class); if (description != null) { abd.setDescription(description.getString("value")); } }
Summary of key points
- AnnotatedBeanDefinitionReader encapsulates the process of building and injecting BeanDefinition into BeanDefinitionRegistry, where BeanDefinitionRegistry is our container
- BeanDefinitionRegistry, which was introduced earlier in the introduction of ApplicationContext, is used to receive and hold Bean definitions
- bean name generator: AnnotationBeanNameGenerator, which is generated according to @ Component and its sub annotations, @ ManagedBean, @ Named. See the following for detailed analysis
- Scope meta information, including scope scope and proxy mode, is identified by @ scope annotation. AnnotationScopeMetadataResolver is used to parse @ scope annotation. If the @ scope annotation is not specified, the result is: Singleton + no proxy.
- ConditionEvaluator, condition parser. Parse @ Conditional. The detailed analysis is shown below.
bean name generation logic
The AnnotationBeanNameGenerator class is worth a look. It has some hidden functions
public class AnnotationBeanNameGenerator implements BeanNameGenerator { // @The compelent annotation is presented in literal form private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component"; // Core method @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { // If the Bean has an annotation, the name is generated based on the annotation if (definition instanceof AnnotatedBeanDefinition) { String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition); if (StringUtils.hasText(beanName)) { // Explicit bean name found. return beanName; } } // If the Bean has no annotation, the initial lowercase form of the class name is used directly return buildDefaultBeanName(definition, registry); } protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) { AnnotationMetadata amd = annotatedDef.getMetadata(); Set<String> types = amd.getAnnotationTypes(); String beanName = null; for (String type : types) { AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type); if (attributes != null) { // Get meta annotations, that is, annotations of annotations, so that annotations such as @ Service and @ Repository can be detected Set<String> metaTypes = this.metaAnnotationTypesCache.computeIfAbsent(type, key -> { Set<String> result = amd.getMetaAnnotationTypes(key); return (result.isEmpty() ? Collections.emptySet() : result); }); if (isStereotypeWithNameValue(type, metaTypes, attributes)) { // The value attribute of the annotation must have a value and is not an empty string Object value = attributes.get("value"); if (value instanceof String) { String strVal = (String) value; if (StringUtils.hasLength(strVal)) { if (beanName != null && !strVal.equals(beanName)) { throw new IllegalStateException("Stereotype annotations suggest inconsistent " + "component names: '" + beanName + "' versus '" + strVal + "'"); } beanName = strVal; } } } } } return beanName; } // protected boolean isStereotypeWithNameValue(String annotationType, Set<String> metaAnnotationTypes, @Nullable Map<String, Object> attributes) { // It can also be seen here that JSR specified annotations such as @ ManagedBean and Named are also supported boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) || metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) || annotationType.equals("javax.annotation.ManagedBean") || annotationType.equals("javax.inject.Named"); return (isStereotype && attributes != null && attributes.containsKey("value")); } protected String buildDefaultBeanName(BeanDefinition definition) { String beanClassName = definition.getBeanClassName(); Assert.state(beanClassName != null, "No bean class name set"); String shortClassName = ClassUtils.getShortName(beanClassName); return Introspector.decapitalize(shortClassName); } }
main points
- Annotations that support the generation of Bean names include: @ Component and its sub annotations, @ MangedBean, @ Named
- If these annotations do not explicitly specify the name, they fall back to the default rule: the class name is lowercase
Determine whether the BeanDefinition needs to be loaded
The ConditionEvaluator class is also worth a visit. It explains how the @ Conditional annotation works. The entire class exposes a method: shouldSkip()
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) { // If the target class is not conditionally annotated, it should not be skipped if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { return false; } if (phase == null) { // If the configuration process is not specified, judge whether it belongs to the configuration phase or the Bean creation phase according to other conditions if (metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); } return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); } List<Condition> conditions = new ArrayList<>(); // Extract the Condition condition from the @ Condition annotation for (String[] conditionClasses : getConditionClasses(metadata)) { for (String conditionClass : conditionClasses) { Condition condition = getCondition(conditionClass, this.context.getClassLoader()); conditions.add(condition); } } // sort AnnotationAwareOrderComparator.sort(conditions); for (Condition condition : conditions) { ConfigurationPhase requiredPhase = null; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } // When the phases match and the conditions do not match, the Bean registration is skipped if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) { return true; } } // In other cases, it should not be skipped return false; }
Analyze ConfigurationClassUtils.isConfigurationCandidate()
private static final Set<String> candidateIndicators = new HashSet<>(8); static { // The following four annotations illustrate the configuration of beans candidateIndicators.add(Component.class.getName()); candidateIndicators.add(ComponentScan.class.getName()); candidateIndicators.add(Import.class.getName()); candidateIndicators.add(ImportResource.class.getName()); } public static boolean isConfigurationCandidate(AnnotationMetadata metadata) { // Annotations on the interface are not considered if (metadata.isInterface()) { return false; } // As long as it is annotated by the above four annotations, it indicates that the Bean is configured for (String indicator : candidateIndicators) { if (metadata.isAnnotated(indicator)) { return true; } } // Otherwise, if there is a method annotated by @ Bean inside, the Bean is also configured return hasBeanMethods(metadata); } static boolean hasBeanMethods(AnnotationMetadata metadata) { try { return metadata.hasAnnotatedMethods(Bean.class.getName()); } catch (Throwable ex) { ... ... } }
Understanding key
- AnnotatedTypeMetadata should be understood. As seen in the AnnotatedBeanDefinition earlier, Spring abstracts the annotation information and class information on the Bean, making it easy to use without loading specific classes.
- Condition condition. It has only one matching method, boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata), which is used to match whether the specified Bean meets the conditions in the container. Our commonly used annotation such as @ ConditionalOnClassMissingBean is the combination of @ Conditional derived annotation + condition derived class.
Summary of key points
- ConditionEvaluator is used to judge whether a Bean definition meets the loading conditions, whether it is loaded or not, and should be ignored. Judgment logic
- If the @ Conditional annotation does not exist, it needs to be loaded
- If there is a @ Conditional annotation, you need to look at the action stage of the annotation and the behavior of its internal Condition
- If the phase specified by Condition is inconsistent with the phase actually used by @ Conditional, it needs to be loaded
- Otherwise, determine whether to load according to the result of Condition.matches()
- When a bean definition is annotated with @ Component, @ ComponentScan, @ Import, @ ImportResource, or contains @ bean annotation methods, it indicates that it is a configuration bean. That is, the action phase is ConfigurationPhase.PARSE_CONFIGURATION, otherwise, the active phase is ConfigurationPhase.REGISTER_BEAN, the action stage, can also be used to judge whether the bean definition needs to be loaded: when the action stage of the declaration is inconsistent with that of the Condition class, the matching process will be ignored and loaded directly
ClassPathBeanDefinitionScanner
The inheritance tree is as above
- ClassPathScanningCandidateComponentProvider is used to scan and provide Bean definitions (so-called CandidateComponent) under the specified package. The core logic is in the public set < beandefinition > findcandidatecomponents (string basepackage) method.
- ClassPathBeanDefinitionScanner is used to define beans under batch scanning packages. The core logic is in the protected set < beandefinitionholder > doscan (string... Basepackages) method.
According to the calling relationship, starting from the scan method
public int scan(String... basePackages) { // Scan: scan the BeanDefinition from the package and inject it into the registry. See the following for details doScan(basePackages); if (this.includeAnnotationConfig) { // Register a batch of postprocessors, which we ignore. It registers a batch of internal postprocessors AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } // return what doesn't matter return ... } protected Set<BeanDefinitionHolder> doScan(String... basePackages) { Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>(); for (String basePackage : basePackages) { // Call findCandidateComponents of the parent class to find the qualified BeanDefinition under the specified package Set<BeanDefinition> candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { // Parsing Scope meta information ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); // Generate bean name String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { // A wave of additional logic, see below for details postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { // Handle a wave of general annotations: @ Lazy, @ Primary, @ DependsOn, @ Role, @ Description. This method has been analyzed earlier AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } // Check whether the newly created BeanDefinition is legal, mainly to check whether there is a Bean with the same name in the container if (checkCandidate(beanName, candidate)) { // Build BeanDefinitionHolder BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); // Proxy mode to apply scope definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); // Inject registry registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; } protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) { // Application default settings: delayed loading, automatic injection mode, dependency check, initialization method, destruction method, etc beanDefinition.applyDefaults(this.beanDefinitionDefaults); if (this.autowireCandidatePatterns != null) { // Set whether to participate in automatic injection beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName)); } } public void applyDefaults(BeanDefinitionDefaults defaults) { Boolean lazyInit = defaults.getLazyInit(); if (lazyInit != null) { setLazyInit(lazyInit); } setAutowireMode(defaults.getAutowireMode()); setDependencyCheck(defaults.getDependencyCheck()); setInitMethodName(defaults.getInitMethodName()); setEnforceInitMethod(false); setDestroyMethodName(defaults.getDestroyMethodName()); setEnforceDestroyMethod(false); }
There is the findCandidateComponents method calling ClassPathScanningCandidateComponentProvider above. Let's look again
public Set<BeanDefinition> findCandidateComponents(String basePackage) { if (this.componentsIndex != null && indexSupportsIncludeFilters()) { // Component scanning via Index return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { // Direct scanning return scanCandidateComponents(basePackage); } } // Direct scanning process: private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { // Path: classpath *: {path name after package conversion} / * * / *. Class, that is, all class files at all levels under the top package name under all jar packages String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); for (Resource resource : resources) if (resource.isReadable()) { try { MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); // It must meet the filter requirements in includeFilters before it can be scanned if (isCandidateComponent(metadataReader)) { // The BeanDefinition just scanned has only created an instance of the Bean definition and has not started parsing. The parsing is left to subclasses. ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); if (isCandidateComponent(sbd)) { candidates.add(sbd); } } } catch (Throwable ex) { throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex); } } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } return candidates; } protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { // In the discharge filter, it does not meet the requirements directly for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } // Match in the include filter to meet the requirements for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false; }
Only the code scanned directly from the class is posted above, but the other addcandidatecomponents fromindex does not say. We need to know what Index is in Spring first. Can refer to This article:
After @ Indexed is used in the project, the META-INT/spring.components file will be automatically generated in the project when compiling and packaging. When the Spring application context performs a ComponentScan, META-INT/spring.components will be read and loaded by the CandidateComponentsIndexLoader and converted into a CandidateComponentsIndex object. In this case, @ ComponentScan will not scan the specified package, but read the CandidateComponentsIndex object, so as to improve performance.
At this point, as like as two peas, we will follow the addCandidateComponentsFromIndex() method and find that it is exactly the same as what we said above.
Summary of key points
-
You can see that the ClassPathBeanDefinitionScanner does the same thing as the AnnotatedBeanDefinitionReader. It builds BeanDefinition and registers it with the container; The only difference is that the Bean definition of the former is scanned by itself, while the Bean definition of the latter is registered by external calling methods
-
Through ClassPathScanningCandidateComponentProvider.findCandidateComponents(), we know that it only provides the most original logic for scanning and generating BeanDefinition, and the resolution and setting of various properties of BeanDefinition are placed in its subclass ClassPathBeanDefinitionScanner.
Summary
This Reader and Scanner are just tools, and the BeanDefinition in the container is finally created.
How to create a Bean
Bean creation logic
There are several opportunities for Bean creation
- For the non delayed loaded singleton Bean, it will be created when the container refresh es, as we saw in the ApplicationContext source code analysis
- For deferred loaded singleton beans, they are created the first time they are retrieved
- For the prototype Bean, it will be created every time it is obtained
Let's take a look at the first case. Go directly to org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons. I deleted the unimportant code.
public void preInstantiateSingletons() throws BeansException { // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... for (String beanName : beanNames) { // Get the merged BeanDefinition RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // Singleton and non deferred loading will be created if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { if (isFactoryBean(beanName)) { // The name of the factory Bean has a special prefix:& Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { FactoryBean<?> factory = (FactoryBean<?>) bean; // Special handling for SmartFactoryBean boolean isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); if (isEagerInit) { // Really create beans getBean(beanName); } } } else { // Really create beans getBean(beanName); } } } for (String beanName : beanNames) { // Special handling for SmartInitializingSingleton Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance; smartSingleton.afterSingletonsInstantiated(); } } } public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } /** * Really create beans * name: bean Name of * requiedType: The type of Bean you want to resolve * args: Parameters used to construct the method when creating the bean. If you just get the existing bean, you don't need to * typeCheckOnly: Only check whether the resulting bean matches the target type, not really mark creation **/ protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) { // Name conversion: it mainly deals with aliases and obtains real names according to aliases String beanName = transformedBeanName(name); Object beanInstance; // Eagerly check singleton cache for manually registered singletons. // Get the registered singleton Bean instance from the singleton cache Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { // Create a Bean instance based on the shared instance. If the shared instance is a factoryBean, use it to create a Bean beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Check if bean definition exists in this factory. BeanFactory parentBeanFactory = getParentBeanFactory(); // If the Bean does not exist in the parent container, try to obtain the Bean from the parent container. The logic is consistent with this class. if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } // Tag created if (!typeCheckOnly) { markBeanAsCreated(beanName); } // If it is not obtained from the cache or parent container, it will be recreated. Here, obtain the BeanDefinition for creating RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // Guarantee initialization of beans that the current bean depends on. // Get dependency String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { // If the current Bean is dependent on its dependency, it will generate a circular dependency and report an error if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } try { // The logic of creating the dependent Bean is still the logic of this method getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // Create bean instance. // If it is a singleton, get the shared instance from the cache. If there is no shared instance, create it if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { // Create Bean return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); // Create a Bean instance based on the shared instance. If the shared instance is a factoryBean, use it to create a Bean beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { // If it is a prototype, a new one is created each time // It's a prototype -> create a new instance. Object prototypeInstance = null; try { // Callback before creation: mark that the bean is being created beforePrototypeCreation(beanName); // Create Bean prototypeInstance = createBean(beanName, mbd, args); } finally { // Callback after creation: mark that the bean has not been created afterPrototypeCreation(beanName); } // Create a Bean instance based on the shared instance. If the shared instance is a factoryBean, use it to create a Bean beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { // scope other than prototype and singleton String scopeName = mbd.getScope(); // Get the specified Scope from the scopes cache, and the object is a Bean holder Scope scope = this.scopes.get(scopeName); try { // There is only one instance of the whole scope. If there is no instance, it will be created. Object scopedInstance = scope.get(beanName, () -> { // Callback before creation: mark that the bean is being created beforePrototypeCreation(beanName); try { // Create Bean return createBean(beanName, mbd, args); } finally { // Callback after creation: mark that the bean has not been created afterPrototypeCreation(beanName); } }); // Create a Bean instance based on the shared instance. If the shared instance is a factoryBean, use it to create a Bean beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new ScopeNotActiveException(beanName, scopeName, ex); } } } // Make the final adjustment. If the instantiated type is inconsistent with the desired type, a type conversion is also required return adaptBeanInstance(name, beanInstance, requiredType); } <T> T adaptBeanInstance(String name, Object bean, @Nullable Class<?> requiredType) { // Check if required type matches the type of the actual bean instance. if (requiredType != null && !requiredType.isInstance(bean)) { try { Object convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); if (convertedBean == null) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } return (T) convertedBean; } catch (TypeMismatchException ex) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean; }
Although the above is only the code for preloading the singleton Bean, the method doGetBean() is a general method for obtaining the Bean. You can summarize this:
- To create a singleton Bean, first get it from the cache; If not, it is obtained from the parent container. If not, the creation logic is executed
- If a Bean has a dependent Bean, the dependent Bean will be created first; If the dependent Bean depends on the original Bean, it constitutes a circular dependency, and Spring will throw an exception
- scope is implemented in only three cases
- Singleton: when the scope of the BeanDefinition has no value (that is, the default singleton) or is a singleton, it is a singleton, and the whole container maintains only one instance
- Prototype: when the scope of the BeanDefinition is prototype, it is the prototype, and the logic of creating a new Bean will be executed every time
- Others: when the scope of BeanDefinition is not any of the above, the implementation method is that the container maintains a Map named scopes, and the value of each entry is a scope object, which is a container, and the new Bean will be put into the container. Only one Bean exists for the same scope.
- The created Bean instance cannot be used directly: if the expected type is specified, call the type converter maintained in the container for type conversion to get the final value
For Bean acquisition methods under normal conditions, such as org.springframework.context.support.AbstractApplicationContext#getBean(java.lang.String), the following are available
public Object getBean(String name) throws BeansException { assertBeanFactoryActive(); return getBeanFactory().getBean(name); } public Object getBean(String name) throws BeansException { // You can see whether it calls doGetBean() or the general method return doGetBean(name, null, null, false); }
You can see whether it calls doGetBean() or the general method, which is omitted here.
MergedBeanDefinition
First of all, there is no class or interface definition, but "how to describe Bean" mentioned the concept of merging Bean definitions, and the creation logic of Bean saw this concept again. What does it mean? Let's look at it from the org.springframework.beans.factory.support.AbstractBeanFactory#getMergedLocalBeanDefinition method
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException { // Get the results from the cache first RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName); if (mbd != null && !mbd.stale) { return mbd; } return getMergedBeanDefinition(beanName, getBeanDefinition(beanName)); } protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd) throws BeanDefinitionStoreException { return getMergedBeanDefinition(beanName, bd, null); } /** * Real method to get mergedBeanDefinition * Parameter description * beanName: bean Name of * bd: bean Definition of * contaningBd: Contains the bean definition of the bean (the bean is an internal bean) **/ protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd) throws BeanDefinitionStoreException { synchronized (this.mergedBeanDefinitions) { RootBeanDefinition mbd = null; RootBeanDefinition previous = null; // Check with full lock now in order to enforce the same merged instance. if (containingBd == null) { mbd = this.mergedBeanDefinitions.get(beanName); } if (mbd == null || mbd.stale) { previous = mbd; // If the current Bean definition does not have a parent Bean definition, a RootBeanDefinition is created if (bd.getParentName() == null) { // Use copy of given root bean definition. if (bd instanceof RootBeanDefinition) { mbd = ((RootBeanDefinition) bd).cloneBeanDefinition(); } else { mbd = new RootBeanDefinition(bd); } } else { // Otherwise, if the Bean definition is a child Bean definition, it needs to be merged with the parent Bean definition // Child bean definition: needs to be merged with parent. BeanDefinition pbd; try { String parentBeanName = transformedBeanName(bd.getParentName()); // Recursive logic obtains the parent Bean definition from the current container or parent container if (!beanName.equals(parentBeanName)) { pbd = getMergedBeanDefinition(parentBeanName); } else { BeanFactory parent = getParentBeanFactory(); if (parent instanceof ConfigurableBeanFactory) { pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName); } } } catch (NoSuchBeanDefinitionException ex) { throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName, "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex); } // Deep copy with overridden values. mbd = new RootBeanDefinition(pbd); // Overwrite the parent Bean definition with the child Bean definition to get a "merged" Bean definition mbd.overrideFrom(bd); } // Set default singleton scope, if not configured before. // The default setting is single instance if (!StringUtils.hasLength(mbd.getScope())) { mbd.setScope(SCOPE_SINGLETON); } // A bean contained in a non-singleton bean cannot be a singleton itself. // Let's correct this on the fly here, since this might be the result of // parent-child merging for the outer bean, in which case the original inner bean // definition will not have inherited the merged outer bean's singleton status. // The bean needs to be consistent with the scope of the bean containing it if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) { mbd.setScope(containingBd.getScope()); } // Cache the merged bean definition for the time being // (it might still get re-merged later on in order to pick up metadata changes) if (containingBd == null && isCacheBeanMetadata()) { this.mergedBeanDefinitions.put(beanName, mbd); } } // The merged Bean definition can be marked as stale, indicating that it needs to be merged again. The merge logic is executed here if (previous != null) { copyRelevantMergedBeanDefinitionCaches(previous, mbd); } return mbd; } } private void copyRelevantMergedBeanDefinitionCaches(RootBeanDefinition previous, RootBeanDefinition mbd) { if (ObjectUtils.nullSafeEquals(mbd.getBeanClassName(), previous.getBeanClassName()) && ObjectUtils.nullSafeEquals(mbd.getFactoryBeanName(), previous.getFactoryBeanName()) && ObjectUtils.nullSafeEquals(mbd.getFactoryMethodName(), previous.getFactoryMethodName())) { ResolvableType targetType = mbd.targetType; ResolvableType previousTargetType = previous.targetType; if (targetType == null || targetType.equals(previousTargetType)) { mbd.targetType = previousTargetType; mbd.isFactoryBean = previous.isFactoryBean; mbd.resolvedTargetType = previous.resolvedTargetType; mbd.factoryMethodReturnType = previous.factoryMethodReturnType; mbd.factoryMethodToIntrospect = previous.factoryMethodToIntrospect; } } }
The key is that the parent Bean definition is overwritten by the child Bean definition. See org.springframework.beans.factory.support.AbstractBeanDefinition#overrideFrom
// But you see, it doesn't seem to be any different. It's just the properties defined by a normal Bean public void overrideFrom(BeanDefinition other) { if (StringUtils.hasLength(other.getBeanClassName())) { setBeanClassName(other.getBeanClassName()); } if (StringUtils.hasLength(other.getScope())) { setScope(other.getScope()); } setAbstract(other.isAbstract()); if (StringUtils.hasLength(other.getFactoryBeanName())) { setFactoryBeanName(other.getFactoryBeanName()); } if (StringUtils.hasLength(other.getFactoryMethodName())) { setFactoryMethodName(other.getFactoryMethodName()); } setRole(other.getRole()); setSource(other.getSource()); copyAttributesFrom(other); if (other instanceof AbstractBeanDefinition) { AbstractBeanDefinition otherAbd = (AbstractBeanDefinition) other; if (otherAbd.hasBeanClass()) { setBeanClass(otherAbd.getBeanClass()); } if (otherAbd.hasConstructorArgumentValues()) { getConstructorArgumentValues().addArgumentValues(other.getConstructorArgumentValues()); } if (otherAbd.hasPropertyValues()) { getPropertyValues().addPropertyValues(other.getPropertyValues()); } if (otherAbd.hasMethodOverrides()) { getMethodOverrides().addOverrides(otherAbd.getMethodOverrides()); } Boolean lazyInit = otherAbd.getLazyInit(); if (lazyInit != null) { setLazyInit(lazyInit); } setAutowireMode(otherAbd.getAutowireMode()); setDependencyCheck(otherAbd.getDependencyCheck()); setDependsOn(otherAbd.getDependsOn()); setAutowireCandidate(otherAbd.isAutowireCandidate()); setPrimary(otherAbd.isPrimary()); copyQualifiersFrom(otherAbd); setInstanceSupplier(otherAbd.getInstanceSupplier()); setNonPublicAccessAllowed(otherAbd.isNonPublicAccessAllowed()); setLenientConstructorResolution(otherAbd.isLenientConstructorResolution()); if (otherAbd.getInitMethodName() != null) { setInitMethodName(otherAbd.getInitMethodName()); setEnforceInitMethod(otherAbd.isEnforceInitMethod()); } if (otherAbd.getDestroyMethodName() != null) { setDestroyMethodName(otherAbd.getDestroyMethodName()); setEnforceDestroyMethod(otherAbd.isEnforceDestroyMethod()); } setSynthetic(otherAbd.isSynthetic()); setResource(otherAbd.getResource()); } else { getConstructorArgumentValues().addArgumentValues(other.getConstructorArgumentValues()); getPropertyValues().addPropertyValues(other.getPropertyValues()); setLazyInit(other.isLazyInit()); setResourceDescription(other.getResourceDescription()); } }
Summary of key points: the so-called merging is the merging of beandefinitions with parent-child relationship. They are merged into one BeanDefinition. The merging logic is to overwrite the parent BeanDefinition with the child BeanDefinition, and the final result is represented by RootBeanDefinition.
Automatic injection
There is too much code for automatic injection, which is divided into several parts
So far, we haven't seen the execution logic of automatic injection, BeanPostProcessor, etc. they are all in org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object []), which was called by the source code of the Bean created earlier
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { RootBeanDefinition mbdToUse = mbd; // Make sure bean class is actually resolved at this point, and // clone the bean definition in case of a dynamically resolved Class // which cannot be stored in the shared merged bean definition. Class<?> resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); } // Prepare method overrides. // Never mind mbdToUse.prepareMethodOverrides(); // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. // In some cases, the obtained Bean may be a proxy. At this time, it is not necessary to really execute the Bean creation logic. The instantiaawarebeanpostprocessor is used to do this Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } // If there are no additional settings, you can really create beans Object beanInstance = doCreateBean(beanName, mbdToUse, args); return beanInstance; }
- The only thing to note here is that you can provide a proxy instance through the instantiaawarebeanpostprocessor, which provides convenience for test functions such as Spy, so that they can forge beans and build a test environment.
Actions before creating an instance
Yes, resolvebeforeinstance
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // Make sure bean class is actually resolved at this point. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { // Determine the type of Bean Class<?> targetType = determineTargetType(beanName, mbd); if (targetType != null) { // When the InstantiationAwareBeanPostProcessor is applied, agents are generally generated bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); if (bean != null) { // If an agent is generated, it is regarded as instantiated and all post processors are applied. Note why the post processor is called here, because the proxy generated above will return directly and will not follow the bean creation logic bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } mbd.beforeInstantiationResolved = (bean != null); } return bean; } // Application InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation, which is specially used before instantiation, so that we can return the agent of a target object. protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { Object result = bp.postProcessBeforeInstantiation(beanClass, beanName); if (result != null) { return result; } } return null; } // After instantiation, all post processors are called. public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }
- Note the same as above
Create instance operation overview
For the actual operation of creation, there are
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. // The created Bean does not exist directly in the form of an object, but in the form of a BeanWrapper BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { // If it is a singleton, remove the BeanWrapper from the cache instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // Real creation steps instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { // Backfill final information mbd.resolvedTargetType = beanType; } // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { // Apply all mergedbeandefinitionpostprocessors applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); mbd.postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. // Exposing singleton Bean references in advance is mainly to solve the problem of circular references boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; // Perform Bean injection populateBean(beanName, mbd, instanceWrapper); // Initialize Bean exposedObject = initializeBean(beanName, exposedObject, mbd); if (earlySingletonExposure) { // Circular dependency check: normally, the object exposed in advance and the initialized bean object should be one, otherwise it indicates that there is a problem Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw ... ... } } } } // Register bean as disposable. // Register the bean with the destroy callback of the corresponding Scope registerDisposableBeanIfNecessary(beanName, bean, mbd); return exposedObject; }
- Note 1: the steps of creating a Bean include creating, executing injection, and executing initialization
- Note 2: the solution of circular reference
Solution of circular reference
It should be noted that there are two ways of circular reference
-
When Scope is a singleton, it should be sorted out exactly as the code in "overview of creating instance operations"
First, Spring maintains three related singleton caches
- singletonObjects: used to maintain singleton objects that have been created
- earlySingletonObjects: used to maintain singleton objects that have been created but have not yet been automatically injected and initialized
- singletonFactories: used to maintain factory instances generated by singleton objects
Then let's look at the specific methods of injection. For example, injection according to name still calls the getBean method to obtain the Bean, in which the getSingleton method will be called to obtain the instance object
- First, get the created singleton object from singletonObjects
- Then try to get the singleton object created to general from earlySingletonObjects
- Then try to create a singleton object with the corresponding singletonFactories. Note that the object will be added with earlySingletonObjects, so you can ignore singletonFactories for convenience
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // It has been simplified to this singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } return singletonObject; }
Then look at what you did when you created the bean (intercepting the previous code): you added a singletonFactory. Combined with the previous analysis, you actually put the newly created bean that has not been injected with properties and initialized in earlySingletonObjects, so that you can directly obtain the bean if there is a circular dependency. Avoid dead loop.
if (earlySingletonExposure) { // The getEarlyBeanReference method actually passes the incoming bean back directly addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
To sum up, the focus here is to separate the creation and initialization (including injection) of beans, and maintain a semi initialized state, so that beans in the semi initialized state can be injected.
**However, this can only be done when it is setter injection, and what about constructor injection? * * in fact, when constructor injection, the current object has not been created and cannot have a semi initialized state, so constructor injection does not allow circular dependency.
-
When Scope is a prototype, refer to line AbstractBeanFactory.doCreate.273, where you can check whether a prototype Bean with the same name under the current thread is being created. Spring is a synchronization framework. When creating a prototype Bean, it is impossible for the same thread to create two prototype beans at the same time. Obviously, as stated in the note, circular reference of prototype beans is not allowed here.
// Fail if we're already creating this bean instance: // We're assumably within a circular reference. if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }
Why doesn't the prototype support circular dependency? Because Spring doesn't cache any state related to the prototype. Although it can also have intermediate states, Spring doesn't cache it. This is a scenario that can be implemented technically, but Spring's design idea does not allow it to exist: the so-called prototype creates a new Bean every time it gets a Bean. What should be obtained when circular dependency is used A Bean completely different from me.
What is BeanWrapper
This class is not for us to use directly, but flows inside the container. It is used to provide the analysis and operation of actual Bean instances, such as operation and query on attributes. It is only commonly used to implement the class BeanWrapperImpl. Understanding it is helpful to view the source code. Let's take a brief look at the interface definition here
public interface BeanWrapper extends ConfigurablePropertyAccessor { // Get the real Bean instance Object getWrappedInstance(); // Get the real Bean class Class<?> getWrappedClass(); // Gets the property descriptor for all instances PropertyDescriptor[] getPropertyDescriptors(); // Gets the property descriptor of the specified property PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException; }
Exact operation of creating an instance
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // Make sure bean class is actually resolved at this point. // Parsing real bean class objects Class<?> beanClass = resolveBeanClass(mbd, beanName); // If BeanDefinitino provides a Supplier for instance creation, it can be directly used to create it. The application scenario of Supplier needs to be explored Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } // If a factory method is specified, it is created directly through the factory method if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } // Shortcut when re-creating the same bean... boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } // If the constructors of BeanDefinition have been resolved, they can be directly used to create them if (resolved) { if (autowireNecessary) { // It is created by the constructor and injected with constructor parameters return autowireConstructor(beanName, mbd, null, null); } else { // Create directly using a parameterless constructor return instantiateBean(beanName, mbd); } } // If a SmartInstantiationAwareBeanPostProcessor provides a constructor, it is used to create Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // Create with default constructor ctors = mbd.getPreferredConstructors(); if (ctors != null) { return autowireConstructor(beanName, mbd, ctors, null); } // Create directly using a parameterless constructor return instantiateBean(beanName, mbd); }
- Order in which alternatives are created
- Supplier set in advance
- Specified factory method
- Use the constructor parsed in advance by BeanDefinition to create
- Created using the constructor provided by SmartInstantiationAwareBeanPostProcessor
- It is created using the class's own parameterized constructor. The parameters come from the container and are automatically injected
- Create using the class's own parameterless constructor
Inject and create by a parametric constructor
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) { BeanWrapperImpl bw = new BeanWrapperImpl(); // Initialization here only injects the content related to type conversion this.beanFactory.initBeanWrapper(bw); Constructor<?> constructorToUse = null; ArgumentsHolder argsHolderToUse = null; Object[] argsToUse = null; if (explicitArgs != null) { // Give args and use it directly argsToUse = explicitArgs; } else { // Without giving args, find args in BeanDefinition and use it Object[] argsToResolve = null; synchronized (mbd.constructorArgumentLock) { constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod; if (constructorToUse != null && mbd.constructorArgumentsResolved) { // Found a cached constructor... argsToUse = mbd.resolvedConstructorArguments; if (argsToUse == null) { argsToResolve = mbd.preparedConstructorArguments; } } } if (argsToResolve != null) { argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve); } } if (constructorToUse == null || argsToUse == null) { // Take specified constructors, if any. Constructor<?>[] candidates = chosenCtors; // Gets all constructor methods of the class as candidates if (candidates == null) { Class<?> beanClass = mbd.getBeanClass(); candidates = (mbd.isNonPublicAccessAllowed() ? beanClass.getDeclaredConstructors() : beanClass.getConstructors()); } // If the object has only one parameterless constructor, you can create the object directly without automatic injection if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) { Constructor<?> uniqueCandidate = candidates[0]; if (uniqueCandidate.getParameterCount() == 0) { synchronized (mbd.constructorArgumentLock) { mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate; mbd.constructorArgumentsResolved = true; mbd.resolvedConstructorArguments = EMPTY_ARGS; } bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS)); return bw; } } // Need to resolve the constructor. // Criteria for automatic injection: there is a constructor, or the injection mode of BeanDefinition is constructor injection boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR); ConstructorArgumentValues resolvedValues = null; int minNrOfArgs; if (explicitArgs != null) { minNrOfArgs = explicitArgs.length; } else { ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues(); resolvedValues = new ConstructorArgumentValues(); minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } AutowireUtils.sortConstructors(candidates); int minTypeDiffWeight = Integer.MAX_VALUE; Set<Constructor<?>> ambiguousConstructors = null; Deque<UnsatisfiedDependencyException> causes = null; // Find out which constructor to use to create the Bean for (Constructor<?> candidate : candidates) { int parameterCount = candidate.getParameterCount(); ArgumentsHolder argsHolder; Class<?>[] paramTypes = candidate.getParameterTypes(); if (resolvedValues != null) { String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount); if (paramNames == null) { ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer(); if (pnd != null) { paramNames = pnd.getParameterNames(candidate); } } // Here, find the bean s that meet the requirements in the container and build the parameters that meet the requirements for later use argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1); } else { // Explicit arguments given -> arguments length must match exactly. if (parameterCount != explicitArgs.length) { continue; } argsHolder = new ArgumentsHolder(explicitArgs); } // Weight calculation rule: compare the given parameter type with the parameter at the corresponding position of the actual constructor. The more consistent it is, the higher the score will be. int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); // Choose this constructor if it represents the closest match. // Bubble method to find the one that best meets the requirements. If the two weights are consistent, add the "indistinguishable" list if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; argsHolderToUse = argsHolder; argsToUse = argsHolder.arguments; minTypeDiffWeight = typeDiffWeight; ambiguousConstructors = null; } else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) { if (ambiguousConstructors == null) { ambiguousConstructors = new LinkedHashSet<>(); ambiguousConstructors.add(constructorToUse); } ambiguousConstructors.add(candidate); } } // Finally, no constructor is selected and an error is reported if (constructorToUse == null) { throw new BeanCreationException(......); } else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) { // Finally, multiple constructors meet the conditions, which cannot be distinguished, and an error is also reported throw new BeanCreationException(......); } } Assert.state(argsToUse != null, "Unresolved constructor arguments"); // Create Bean instances with reflection bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse)); return bw; }
- Spring will first find out the parameter combinations that meet the type requirements from the container, and then find the constructor that best matches these parameter types to create an instance
- It is conceivable that as long as a bean of exact type is created, it will be instantiated successfully, because there can be no constructor with exactly the same parameter type
Create using a parameterless constructor
protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) { // There's no need to catch up here. It just gets the parameterless constructor of the class and instantiates it through reflection Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this); // Wrap as BeanWrapper BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; }
Execution of automatic injection logic
For the logic of automatic injection, i.e. populateBean(), there are
// Perform Bean injection protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the // state of the bean before properties are set. This can be used, for example, // to support styles of field injection. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { // The instantiaawarebeanpostprocessor is applied and executed just after the instance is created and before the property is set for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); // Self defined injection mode: automatic detection, by name and by type; int resolvedAutowireMode = mbd.getResolvedAutowireMode(); if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // Add property values based on autowire by name if applicable. if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { // Inject by name autowireByName(beanName, mbd, bw, newPvs); } // Add property values based on autowire by type if applicable. if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { // Injection by type autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); PropertyDescriptor[] filteredPds = null; if (hasInstAwareBpps) { if (pvs == null) { pvs = mbd.getPropertyValues(); } // Apply the instantiawarebeanpostprocessor.postprocessproperties and postProcessPropertyValues methods for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } pvs = pvsToUse; } } if (needsDepCheck) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } // Dependency check: check whether all required properties have been set checkDependencies(beanName, mbd, filteredPds, pvs); } if (pvs != null) { // Really apply these attributes applyPropertyValues(beanName, mbd, bw, pvs); } } // Name injection is to find the bean with the specified name from the container and add it to the temporary property set for later use protected void autowireByName(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { if (containsBean(propertyName)) { // Gets the Bean with the specified name Object bean = getBean(propertyName); pvs.add(propertyName, bean); // Register Bean dependency cache registerDependentBean(propertyName, beanName); } } } // Injection by type is a little more complicated. It is roughly to find all bean s that match the type from the container, and then get the exact one according to certain rules protected void autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } Set<String> autowiredBeanNames = new LinkedHashSet<>(4); String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); // Don't try autowiring by type for type Object: never makes sense, // even if it technically is a unsatisfied, non-simple property. if (Object.class != pd.getPropertyType()) { // Get the set method of the property MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); // Do not allow eager init for type matching in case of a prioritized post-processor. boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered); DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter); if (autowiredArgument != null) { pvs.add(propertyName, autowiredArgument); } for (String autowiredBeanName : autowiredBeanNames) { // Register Bean dependency cache registerDependentBean(autowiredBeanName, beanName); } autowiredBeanNames.clear(); } } }
Operation of instance initialization
The logic of initialization is
// Initialize Bean protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // Call xxxAware and inject the corresponding content. There are three managed here: BeanNameAware, BeanClassLoaderAware and beanfactory aware invokeAwareMethods(beanName, bean); Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { // Apply all BeanPostProcessor.postProcessBeforeInitialization methods wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } // Call the initialization method, which includes initializingbean.afterpropertieset and the user-defined init method invokeInitMethods(beanName, wrappedBean, mbd); if (mbd == null || !mbd.isSynthetic()) { // Apply all BeanPostProcessor.postProcessAfterInitialization methods wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
- Instantiation contains content
- Call of beanxaware interface
- Call to BeanPostProcessor.postProcessBeforeInitialization
- Call to initializingbean.afterpropertieset
- Call of custom initialization method
- Call to BeanPostProcessor.postProcessAfterInitialization
Get the operation of the dependent instance according to the dependency type
When executing automatic injection logic, the following methods are useful. Let's see what it does: org.springframework.beans.factory.config.autowirecapablebeanfactory#resolvedependency (org.springframework.beans.factory.config.dependencydescriptor, java.lang.string, java.util.set < Java. Lang.string >, org.springframework.beans.typeconverter)
javaxInjectProviderClass = ClassUtils.forName("javax.inject.Provider", DefaultListableBeanFactory.class.getClassLoader()); /** * In the current container, resolve the Bean corresponding to the specified dependency * desciptor: Dependency description class, which can be field, method and constructor dependency. The description class contains these parameter information * requestingBeanName: The name of the bean that declares the dependency * autowiredBeanNames: A place to put the results, which is used to store the name of the found dependent bean * typeConverter: Type converter required * Return value: returns the dependent Bean **/ public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // Initialize parameter name searcher descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); if (Optional.class == descriptor.getDependencyType()) { // If the dependent type is Optional, it is created according to the corresponding logic return createOptionalDependency(descriptor, requestingBeanName); } else if (ObjectFactory.class == descriptor.getDependencyType() || ObjectProvider.class == descriptor.getDependencyType()) { // If the dependent type is factory or Provider (which is also a kind of factory), DependencyObjectProvider is returned return new DependencyObjectProvider(descriptor, requestingBeanName); } else if (javaxInjectProviderClass == descriptor.getDependencyType()) { // If the dependent type is javax.inject.Provider, it will be resolved according to JSR330 return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName); } else { // If deferred creation is supported, the resolution result is a proxy Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName); if (result == null) { // This is the real creation result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } } public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { // Gets the type of the dependency Class<?> type = descriptor.getDependencyType(); // Get the value explicitly specified during user setting injection, such as the value in @ Qualifier. The suggestion here means user suggestion, not calculated automatically Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value != null) { // If the recommended value is a string, parse the string and obtain the container of the corresponding name after parsing from the container if (value instanceof String) { // Parse the string, which means that the string can be a SPEL expression String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); // Get the final value. The BeanDefinition here is only passed in to get the scope value value = evaluateBeanDefinitionString(strVal, bd); } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); // Type conversion return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor()); } // Try to parse the Bean as a collection type Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); if (multipleBeans != null) { return multipleBeans; } // Get qualified bean s from the entire container Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { if (isRequired(descriptor)) { raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } // Still can't find, it's null return null; } String autowiredBeanName; Object instanceCandidate; // If the number of matched bean s is greater than 1, you need to make a selection if (matchingBeans.size() > 1) { // First, select according to the Primary, then sort according to the Priority, and finally select according to whether the found name is consistent with the dependent name autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); // When there is no Primary, Priority is not implemented, and the name does not match, it may not be found if (autowiredBeanName == null) { if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { // It is possible that descriptors have their own defined response methods return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans); } else { // In case of an optional Collection/Map, silently ignore a non-unique case: // possibly it was meant to be an empty collection of multiple regular beans // (before 4.3 in particular when we didn't even look for collection beans). // No descriptors, then null return null; } } // This is the match instanceCandidate = matchingBeans.get(autowiredBeanName); } else { // We have exactly one match. // When there happens to be a match, everything is beautiful Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } if (autowiredBeanNames != null) { // Put the found bean name into the receive collection autowiredBeanNames.add(autowiredBeanName); } if (instanceCandidate instanceof Class) { // The final instance is obtained by parsing instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this); } Object result = instanceCandidate; return result; } protected Map<String, Object> findAutowireCandidates(@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) { // Get all bean names of the specified type in the current container and all parent containers as candidates String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager()); Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length); for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) { Class<?> autowiringType = classObjectEntry.getKey(); if (autowiringType.isAssignableFrom(requiredType)) { Object autowiringValue = classObjectEntry.getValue(); autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType); if (requiredType.isInstance(autowiringValue)) { result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue); break; } } } for (String candidate : candidateNames) { // If these candidate bean s turn on the auto injection switch if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { // Join candidate addCandidateEntry(result, candidate, descriptor, requiredType); } } ... ... return result; } private void addCandidateEntry(Map<String, Object> candidates, String candidateName, DependencyDescriptor descriptor, Class<?> requiredType) { // The following are the different parsing methods of different types of descriptors, which are not discussed here. if (descriptor instanceof MultiElementDescriptor) { Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this); if (!(beanInstance instanceof NullBean)) { candidates.put(candidateName, beanInstance); } } else if (containsSingleton(candidateName) || (descriptor instanceof StreamDependencyDescriptor && ((StreamDependencyDescriptor) descriptor).isOrdered())) { Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this); candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance)); } else { candidates.put(candidateName, getType(candidateName)); } }
Summary
The creation of Bean is the most important thing, because it is related to the life cycle of Spring, which is a question asked very frequently in the interview. With the above analysis, we can summarize the key processes in the creation process. In fact, there are two case s
case1
- First, execute instantiawarebeanpostprocessor. Postprocessbeforeinstance() to provide an opportunity to create an agent. If the agent is created successfully
- Execute all BeanPostProcessor.postProcessAfterInitialization() methods
- For preloaded singleton beans, SmartInitializingSingleton.afterSingletonsInstantiated() is also called
- end
case2
- Instantiate Bean
- Attempt to create using the specified factory method
- Try to create with a construction method. If a parametric construction method is used, the parameters of the construction method will be automatically injected
- Apply instantiaawarebeanpostprocessor. Postprocessafterinstance()
- Automatically inject the required attributes, find qualified beans from the container by name or type, and inject
- Call BeanNameAware, BeanClassLoaderAware, beanfactory aware
- Apply BeanPostProcessor.postProcessBeforeInitialization()
- Apply initializingbean. Afterpropertieset()
- Apply custom init method
- Apply BeanPostProcessor.postProcessAfterInitialization
- For preloaded singleton beans, SmartInitializingSingleton.afterSingletonsInstantiated() is also called
- end
Note that the life cycle of beans in Spring is discussed here, not the life cycle of Spring. If it is the latter, please refer to the first article for analysis.
How to destroy beans
Objects in the JVM are recycled through reachability analysis and garbage collection mechanism; Bean objects in Spring are always held by containers. Can't they never be garbage collected? The idea is correct and the design is reasonable. However, it should be noted that there is a premise: Scope. Because of its existence, bean life cycle management becomes convenient.
- For beans whose Scope is a singleton, the container is globally unique and referenced by the container. Of course, it will not and cannot be destroyed
- For beans with Scope as the prototype, there is no reference inside the container after creation and they are handed over to the application, which is consistent with the objects from ordinary new and can be recycled
- For other beans with a Scope, it depends on the Scope. The Scope is destroyed and the Bean objects are recycled together. This situation has not been seen before; Otherwise, it will be left to the Scope to deal with it. For example, the Scope of the Request or Session Scope in the web saves the Bean object in HttpServletRequest or HttpSession, that is, the life cycle ends with the destruction of the Request or Session.
We focus on two points: how to destroy beans destroyed with containers; How to destroy beans whose life cycle does not follow the container
Destroyed by container
Destruction method of container
protected void doClose() { // Check whether an actual close attempt is necessary... if (this.active.get() && this.closed.compareAndSet(false, true)) { // Publish container close event publishEvent(new ContextClosedEvent(this)); // Call the shutdown method of the lifecycle processor if (this.lifecycleProcessor != null) { this.lifecycleProcessor.onClose(); } // Play: destroy Bean destroyBeans(); // Close the state of this context itself. // Actually, I didn't do anything closeBeanFactory(); // Let subclasses do some final clean-up if they wish... // Container lifecycle approach onClose(); // Switch to inactive. this.active.set(false); } } protected void destroyBeans() { getBeanFactory().destroySingletons(); } public void destroySingletons() { String[] disposableBeanNames; synchronized (this.disposableBeans) { disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet()); } for (int i = disposableBeanNames.length - 1; i >= 0; i--) { destroySingleton(disposableBeanNames[i]); } } public void destroySingleton(String beanName) { ... ... destroyBean(beanName, disposableBean); } protected void destroyBean(String beanName, @Nullable DisposableBean bean) { ... ... bean.destroy(); ... ... }
You can see that when the container is closed, only the singleton Bean is destroyed and two life cycle related methods are called
- LifyCycle.close()
- DisposableBean.destroy()
But there is another kind of Bean destruction callback that we haven't seen: the call of custom destruction method
Destroyed by JVM
Just like the annotation on the annotation method org.springframework.context.annotation.Bean#destroyMethod, only beans whose life cycle is completely controlled by the container can normally be called by the container to various destruction methods, that is, single cases, which can not be guaranteed by other scopes. Therefore, life cycle methods such as prototypes and sessions mentioned above cannot be called normally, because the container can't control them.
Note: Only invoked on beans whose lifecycle is under the full control of the factory, which is always the case for singletons but not guaranteed for any other scope.
summary
This article has been written for three days. Can you believe it???
To sum up, from the perspective of source code, this paper introduces how Spring describes beans, how to scan beans when creating containers, how to create beans at different times, the solution of Bean circular dependency, the logic of automatic injection, and the destruction scenarios of beans with different scopes. But it's just a casual observation, but when you encounter problems in the future, you should be able to locate the problems soon 🤔.
Naming rules
- Xxxprovider: Provider is a combination of policy pattern and abstract factory pattern. The so-called abstract factory pattern means that it encapsulates the process of creating instances; The so-called policy pattern means that it can be passed as a policy into other other other based classes. For example, classpathscanningandidatecomponentprovider is associated with AnnotationConfigApplicationContext.
What's next
Spring source code analysis - powerful BeanPostProcessor