Read this article and you will learn
- in
getBean
Method,Spring
Handle aliases as well asfactoryBean
的name
Spring
How do I get data from a multilevel cachebeanName
To obtainbean
Spring
How to deal with user acquisition commonbean
和factoryBean
The introduction
From the initialization of the Spring container, we learned how Spring converts XML files to BeanDefinition and registers them with BeanDefinitionRegstry.
Today we will continue our study of Spring bean loading
public static void main(String[] args) {
Resource resource = new ClassPathResource("coderLi.xml");
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
xmlBeanDefinitionReader.loadBeanDefinitions(resource);
} Copy the code
<beans>
<bean class="com.demo.data.Person">
<description>
Wechat search :CoderLi </description> </bean> </beans> Copy the code
Familiar flavors, familiar recipes
Source code analysis
We can add the following code to the Java code above
System.out.println(defaultListableBeanFactory.getBean("com.demo.data.Person#0"));
Copy the code
We get the Person bean object from the Spring container according to the default beanName, though we can use the default alias
System.out.println(defaultListableBeanFactory.getBean("com.demo.data.Person"));
Copy the code
For those unfamiliar with Spring aliases, take a look at my article spring-AliasRegistry
We directly into the AbstractBeanFactory# getBean (String) method, the AbstractBeanFactory for DefaultListableBeanFactory parent class
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null.null.false);
}
Copy the code
AbstractBeanFactory#doGetBean
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// Find the bean name for this parameter
final String beanName = transformedBeanName(name);
Object bean; // Eagerly check singleton cache for manually registered singletons. // Check to see if the bean is in the buffer. Spring only holds the bean as a singleton Object sharedInstance = getSingleton(beanName); if(sharedInstance ! =null && args == null) { // I deleted some spring logs here // Handle the factory bean case, including fetching from the Factory Beans cache, or re-calling the Factory bean's get bean method, including some callbacks bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } ..Copy the code
Because this method is too long, we cut it out and we go through it step by step
TransformedBeanName (name) converts our name to the real beanName, because the argument we pass in May bean alias or may be the beanName of a factoryBean (prefixed with &), The beanName of the factoryBean we store in Spring does not have an & prefix, so we need to get rid of that prefix
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
// Whether it is a factory bean, if not, return it directly if(! name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) { return name; } // If so, remove the & prefix return transformedBeanNameCache.computeIfAbsent(name, beanName -> { do { beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length()); } while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)); return beanName; }); } / * ** Find the final bean Name for this alias, if none (* The name of the bean is the name of the bean. * * / public String canonicalName(String name) { String canonicalName = name; // Handle aliasing... String resolvedName; do { resolvedName = this.aliasMap.get(canonicalName); if(resolvedName ! =null) { canonicalName = resolvedName; } } while(resolvedName ! =null); return canonicalName; } Copy the code
Let’s look again at the next method DefaultSingletonBeanRegistry# getSingleton (String)
public Object getSingleton(String beanName) {
// allowEarlyReference allows early dependencies
return getSingleton(beanName, true);
}
Copy the code
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
// This bean is in the creation phase
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // Concurrency control synchronized (this.singletonObjects) { // Whether the singleton cache exists singletonObject = this.earlySingletonObjects.get(beanName); // Whether to run the bean created by the GetBean Factory if (singletonObject == null && allowEarlyReference) { // Get the ObjectFactory in the cache ObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName); if(singletonFactory ! =null) { singletonObject = singletonFactory.getObject(); // Cache the object in earlySingletonObject this.earlySingletonObjects.put(beanName, singletonObject); // Remove from the factory buffer this.singletonFactories.remove(beanName); } } } } return singletonObject; } Copy the code
The above code is Spring’s attempt to load a singleton from the cache. The singleton is created only once in the same Spring container, and then the bean is fetched directly from the cache.
Under the introduce this method, we first DefaultSingletonBeanRegistry the member variables in a class
Map<String, Object> singletonObjects
The key is beanName and the value is the bean instanceMap<String, ObjectFactory<? >> singletonFactories
Key is beanName and value is the factory where the bean was createdMap<String, Object> earlySingletonObjects
Key is beanName and value is bean. But andsingletonObjects
The difference is that beans are added toearlySingletonObjects
While the bean is still in a state of creation, the purpose is simple, and Spring is used to solve the loop dependency in some scenarios
Let’s go back to the code and examine its logic
- Try to get beans from singletonObjects, where they are already created, and it would be nice to know that from there
- If not, we need to determine whether the beanName bean is being created
- If so, let’s see if the bean we are creating has been exposed. If not, let’s see if our parameters are allowed to rely on the earlier bean.
- If early dependencies are allowed, we try to get the corresponding bean from ObjectFactory, put it into earlySingletonObjects, and remove it from singletonFactories
A design similar to multi-level caching
We see in the above methods isSingletonCurrentlyInCreation (beanName) this method,
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
Copy the code
SingletonsCurrentlyInCreation this Set, when creating a beans before its corresponding beanName placed in the Set, the analysis in the later involves, here first a mouth
It is normal for us to get the bean the first time and return null
So let’s say we getBean twice in our code
defaultListableBeanFactory.getBean("com.demo.data.Person#0")
defaultListableBeanFactory.getBean("com.demo.data.Person#0")
Copy the code
The value returned for the second call is not null
Object sharedInstance = getSingleton(beanName);
if(sharedInstance ! =null && args == null) {
// Handle the factory bean case, including fetching from the Factory Beans cache, or re-calling the Factory bean's get bean method, including some callbacks
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
Copy the code
So let’s just assume that sharedInstance is not null which is the second time we call getBean, and we’re going into the getObjectForBeanInstance method
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// We want a factory bean
if (BeanFactoryUtils.isFactoryDereference(name)) {
// If this is of type NullBean, this is a NULL instance if (beanInstance instanceof NullBean) { return beanInstance; } // Get beanInstance is not a factory, but your tm name has this & is confused if(! (beanInstanceinstanceof FactoryBean)) { throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass()); } if(mbd ! =null) { mbd.isFactoryBean = true; } return beanInstance; } // Wang doesn't want the factory bean, and Spring finds a normal bean for him, and returns it directly if(! (beanInstanceinstanceof FactoryBean)) { return beanInstance; } Spring needs to do some extra processing to return a normal bean to Wang Object object = null; if(mbd ! =null) { mbd.isFactoryBean = true; } else { // See if there is any in the cache object = getCachedObjectForFactoryBean(beanName); } // If the bean factory does not exist if (object == null) { // Return bean instance from factory. FactoryBean<? > factory = (FactoryBean<? >) beanInstance; // Fetch the bean Definition from the cache if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } // Whether the bean is user-defined or created by the application itself booleansynthetic = (mbd ! =null && mbd.isSynthetic()); object = getObjectFromFactoryBean(factory, beanName, ! synthetic); } return object; } Copy the code
Let’s take a step-by-step look at the code above
- We call
getBean(name)
If the name is prefixed with &, we want to get a FactoryBean from Spring, Then we need to determine if the beanInstance we get from the cache is a FactoryBean and return it if it is and throw an exception if it is not - We want a non-FactoryBean and find a non-FactoryBean bean in the Spring container, so return it
- We want a non-FactoryBean, but if we find a Bean of factoryBean in the Spring container, we need to enter
getObjectFromFactoryBean
The method of
protected Object getObjectFromFactoryBean(FactoryBean<? > factory, String beanName,boolean shouldPostProcess) {
// It is a singleton and exists in the cache
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
// Get the specified bean from the cache (this bean is created from the factory bean) Object object = this.factoryBeanObjectCache.get(beanName); if (object == null) { // Empty gets the object from the Factory bean object = doGetObjectFromFactoryBean(factory, beanName); // Fetch from the cache Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if(alreadyThere ! =null) { // It is already stored in the cache, and subsequent operations are not needed object = alreadyThere; } else { // Need to do some post-processing if (shouldPostProcess) { // If the bean is being created, if (isSingletonCurrentlyInCreation(beanName)) { return object; } / / preposition Mainly to the bean to join singletonsCurrentlyInCreation is creating in the queue beforeSingletonCreation(beanName); try { // Postprocess the objects obtained from factoryBean // The generated object will be exposed to the bean reference and called back to beanPostProcessor object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's singleton object failed", ex); } finally { / / post processing From the singletonsCurrentlyInCreation removed afterSingletonCreation(beanName); } } // His factory bean already exists in the cache, so the beans generated by this factory bean should also be cached if (containsSingleton(beanName)) { this.factoryBeanObjectCache.put(beanName, object); } } } return object; } } else { / / the singleton Object object = doGetObjectFromFactoryBean(factory, beanName); if (shouldPostProcess) { try { // object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex); } } return object; } } Copy the code
Ahh ahh ahh ahh, the code is very long long………. Let’s break it down a little bit
-
The first step is to determine whether the factoryBean singleton, if not, and is the user’s own definition of beans, you need to call postProcessObjectFromFactoryBean method to do a follow-up processing
- The final callback is an interface we often use
BeanPostProcessor
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; } Copy the code
- The final callback is an interface we often use
-
If this beanFactory is a singleton, let’s look at factoryBeanObjectCache. BeanName beanName beanName beanName beanName beanName beanName beanName beanName beanName
-
If they exist, and he returned directly, if it doesn’t exist, then doGetObjectFromFactoryBean, from this method used in FactoryBean# getObject produce beans
-
In fact, the following code is really confusing
Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if(alreadyThere ! =null) { // It is already stored in the cache, and subsequent operations are not needed object = alreadyThere; } Copy the code
-
Then this method, we see beforeSingletonCreation is getSingleton above isSingletonCurrentlyInCreation judge whether a bean in is creating
protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } } public boolean isSingletonCurrentlyInCreation(String beanName) { return this.singletonsCurrentlyInCreation.contains(beanName); } Copy the code
-
Then call to postProcessObjectFromFactoryBean method, finally the callback is our commonly used a BeanPostProcessor interface
-
It is best to call the afterSingletonCreation(beanName) method, remove it from the collection of beans being created, and last but not least, add it to the factoryBeanObjectCache collection
Today we first analysis here, the subsequent words we continue to discuss in the later articles. Today we looked at the three methods in getBean
conclusion
- Find the corresponding beanName based on the name in the parameter, whether the name is an alias or the beanName of a factoryBean
- Check to see if the beanName object is in the cache
- Start with level 1 caching
singletonObjects
Let’s see if there are any - And then from level two cache
earlySingletonObjects
- If not, then cache from level 3
singletonFactories
Let’s see if there are any
- Start with level 1 caching
- If there is a bean in the cache, we still need to deal with that bean
- If the bean returned from the Spring cache is a factoryBean, and the user also wants a beanFactory (the prefix in the name argument is
&
), so we go straight back - If the bean returned from the Spring cache is a normal bean, and the user wants a normal bean, it will be returned
- If the bean returned from the Spring cache is a factoryBean, and the user wants a plain bean, then we get the bean from the factoryBean
- The process of getting this bean from a factoryBean calls pre-processing, post-processing, and the usual interface callbacks
BeanPostProcessor
- If the bean returned from the Spring cache is a factoryBean, and the user also wants a beanFactory (the prefix in the name argument is
The above three methods are generally such a process, I hope to help you
Interested in group chat, communication and paddling together