1.getBean
This is triggered when we get the Bean using the BeanFactory#getBean(String name) method
loading
Bean stage. The following code
:
//AbstractBeanFactory.java
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null.null.false);
}Copy the code
AbstractBeanFactory#doGetBean(String name, Class<T> requiredType,Object[] args, Boolean typeCheckOnly) The method accepts four parameters:
- Name: To get the Bean name
- RequiredType: The Bean type to get
- Args: Parameter passed when the Bean is created. This parameter is valid only when the Bean is created
- TypeCheckOnly: Indicates whether type detection is performed
2.doGetBean
//AbstractBeanFactory.java
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//<1> Returns the bean name, stripping out the factory reference prefix.
// If name is alias, get the corresponding beanName.
final String beanName = transformedBeanName(name);
Object bean;
// Get the Bean object from the cache or the instance factory
Object sharedInstance = getSingleton(beanName);
if(sharedInstance ! =null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
} else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); }}// <2> Complete the FactoryBean processing and use it to get the FactoryBean processing result
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// <3> Because Spring only addresses cyclic dependencies in singleton mode, in prototype mode an exception is thrown if a cyclic dependency exists.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// <4> If it is not found in the current container, it is loaded from the parent container
BeanFactory parentBeanFactory = getParentBeanFactory();
if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
String nameToLookup = originalBeanName(name);
AbstractBeanFactory (AbstractBeanFactory)
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
// Get the Bean object from parentBeanFactory with explicit args
} else if(args ! =null) {
return (T)parentBeanFactory.getBean(nameToLookup, args);
// Get the Bean object from parentBeanFactory with an explicit requiredType
} else if(requiredType ! =null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
// Use nameToLookup directly to get the Bean object from parentBeanFactory
} else {
return(T)parentBeanFactory.getBean(nameToLookup); }}// <5> Create bean instead of just type checking, where record is required
if(! typeCheckOnly) { markBeanAsCreated(beanName); }try {
// <6> Get beanName's corresponding GenericBeanDefinition object from the container and convert it to RootBeanDefinition object
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Check the given merged BeanDefinition
checkMergedBeanDefinition(mbd, beanName, args);
// <7> process the dependent bean
String[] dependsOn = mbd.getDependsOn();
if(dependsOn ! =null) {
for (String dep : dependsOn) {
// If the given dependent bean is already registered to depend on the given bean
// In the case of loop dependencies, throw a BeanCreationException
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// The cache dependency calls TODO Taro
registerDependentBean(dep, beanName);
try {
// Recursive processing of dependent beans
getBean(dep);
} catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex); }}}// <8> bean instantiation
if (mbd.isSingleton()) { // Singleton mode
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
// Explicitly remove the Bean instance from the singleton cache
// Destroy it because it may already exist in singleton mode to solve the cyclic dependency. TODO taro
destroySingleton(beanName);
throwex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }else if (mbd.isPrototype()) { // Prototype mode
Object prototypeInstance;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
} finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {
// Get the Scope object corresponding to scopeName
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
// Create a bean from the specified scope
Object scopedInstance = scope.get(beanName, () -> {
// Load preprocessing
beforePrototypeCreation(beanName);
try {
// Create a Bean object
return createBean(beanName, mbd, args);
} finally {
// load the suffix processingafterPrototypeCreation(beanName); }});// Get the object from the Bean instance
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
} catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); }}}catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throwex; }}// <9> Check that the required type matches the actual type of the bean
if(requiredType ! =null && !requiredType.isInstance(bean)) {
try {
// Perform the conversion
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
/ / conversion fails, throwing BeanNotOfRequiredTypeException anomalies
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
} catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T)bean;
}Copy the code
This code is very long, and the logic is very complex, we will break this code down for analysis
- <1> : for detailed analysis, see “2.1 Obtaining beanName”
- <2> : Detailed parsing, see “2.2 Obtaining beans from singleton Bean Cache”
- <3> : detailed analysis, see “2.3 Prototype Pattern dependency Check”
- <4>, see “2.4 Obtaining Bean from parentBeanFactory”
- <5>, detailed parsing, see “2.5 specified Bean marked as created or to be created”
- <6> for detailed analysis, see “2.6 Obtaining BeanDefinition”.
- <7>, detailed analysis, see “2.7 Dependent Bean processing”
- <8>, see “2.8 Bean instantiation with different scopes”
- <9>, detailed analysis, see “2.9 Type Conversion”
2.1 get beanName
The corresponding code segment is as follows:
//AbstractBeanFactory.java
final String beanName = transformedBeanName(name);Copy the code
So the name doesn’t have to be beanName, it could be aliasName, it could be FactoryBean, so we need to call AbstractBeanFactory#transformedBeanName(String name), Convert name. The code snippet is as follows:
//AbstractBeanFactory.java
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}Copy the code
- Call the BeanFactoryUtils#transformedBeanName(String name) method to strip the FactoryBean modifier. The code snippet is as follows:
//BeanFactoryUtils.java
// Cache the converted name to beanName
private static final Map<String, String> transformedBeanNameCache = new ConcurrentHashMap<>();
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
if(! name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {return name;
}
// name = "&userService"
// beanName = "userService"
return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
do {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
});
}Copy the code
- Call the BeanFactoryUtils#canonicalName(String name) method to get the final beanName of alias. This is a circular process
//SimpleAliasRegistry.java
// Cache alias to beanName
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);
public String canonicalName(String name) {
String canonicalName = name;
String resolvedName;
// Loop to get the final beanName from aliasMap
do {
resolvedName = this.aliasMap.get(canonicalName);
if(resolvedName ! =null) { canonicalName = resolvedName; }}while(resolvedName ! =null);
return canonicalName;
}
Copy the code
2.2 Obtaining beans from the singleton Bean cache
The corresponding code segment is as follows:
//AbstractBeanFactory.java
// Get the Bean object from the cache or instance factory
Object sharedInstance = getSingleton(beanName);
if(sharedInstance ! =null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
} else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); }}//
Completes the FactoryBean processing and gets the result of the FactoryBean processing
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}Copy the code
- Single-column beans are created only once in their lifetime, and the beans that have been created are stored in the cache so that the next time they are fetched, they are fetched directly from the single-column cache
-
, if the Bean object is retrieved from the cache, AbstractBeanFactory#getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition MBD), because the Bean in the cache may be the original Bean, not necessarily the instantiated Bean
For full analysis, see “[Spring source] — IoC load Bean: from the singleton cache.”
2.3 Prototype pattern dependency check
The corresponding code segment is as follows:
//AbstractBeanFactory.java
Spring only handles loop dependencies in single-column mode
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}Copy the code
Spring only handles singleton loop dependencies and throws exceptions for prototype loop dependencies. The main reason, again, has to do with Spring’s strategy for dealing with loop dependencies.
- Singleton pattern: Spring does not add the Bean to the cache until the Bean is fully created. Instead, it puts the ObjectFactory of the Bean in the cache before the Bean is created. This allows you to use the ObjectFactory directly whenever you need to rely on the Bean when the next Bean is created
- Prototype pattern: Can’t use cache, loop dependencies can’t be handled
For details, see “[Spring source] — IoC loading Bean: parentBeanFactory and dependency processing”
2.4 Get beans from parentBeanFactory
The corresponding code segment is as follows:
// AbstractBeanFactory.java
// If it is not found in the current container, it is loaded from the parent container
BeanFactory parentBeanFactory = getParentBeanFactory();
if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
String nameToLookup = originalBeanName(name);
AbstractBeanFactory (AbstractBeanFactory)
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory)parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
// Get the Bean object from parentBeanFactory with explicit args
} else if(args ! =null) {
return (T)parentBeanFactory.getBean(nameToLookup, args);
// Get the Bean object from parentBeanFactory with an explicit requiredType
} else if(requiredType ! =null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
// Use nameToLookup directly to get the Bean object from parentBeanFactory
} else {
return(T)parentBeanFactory.getBean(nameToLookup); }}Copy the code
If there is no corresponding BeanDefinition object in the current container, it tries to load it from the parent container and recursively calls getBean(…). Methods.
For details, see “[Spring source] — IoC loading Bean: parentBeanFactory and dependency processing”
2.5 The specified Bean is marked as already created or about to be created
The corresponding code segment is as follows:
// AbstractBeanFactory.java
// Create a bean instead of just doing type checking, where records are required
if(! typeCheckOnly) { markBeanAsCreated(beanName); }Copy the code
For details, see “[Spring source] — IoC loading Bean: parentBeanFactory and dependency processing”
2.6 get BeanDefinition
The corresponding code segment is as follows:
// AbstractBeanFactory.java
// Get the corresponding GenericBeanDefinition object from the container and convert it to a RootBeanDefinition object
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Check the given merged BeanDefinition
checkMergedBeanDefinition(mbd, beanName, args);Copy the code
..todo