Prototype Bean Loading Process
Previous articles analyzed the whole loading process of non-lazy loading single beans. In addition to non-lazy loading single beans, there is another kind of Bean in Spring, which is Prototype beans. Look at the definition:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> 6 7 <bean id="prototypeBean" class="org.xrq.action.PrototypeBean" scope="prototype" /> 8 9 </beans>
The prototype Bean loading process is generally similar to that of a single Bean, so take a look at the differences in this step of AbstractBeanFactory's doGetBean approach:
1 else if (mbd.isPrototype()) { 2 // It's a prototype -> create a new instance. 3 Object prototypeInstance = null; 4 try { 5 beforePrototypeCreation(beanName); 6 prototypeInstance = createBean(beanName, mbd, args); 7 } 8 finally { 9 afterPrototypeCreation(beanName); 10 } 11 bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); 12 }
Line 6 createBean is the same. The main difference between prototype beans instantiation is line 6, which creates beans directly, while single beans are compared again:
1 if (mbd.isSingleton()) { 2 sharedInstance = getSingleton(beanName, new ObjectFactory() { 3 public Object getObject() throws BeansException { 4 try { 5 return createBean(beanName, mbd, args); 6 } 7 catch (BeansException ex) { 8 // Explicitly remove instance from singleton cache: It might have been put there 9 // eagerly by the creation process, to allow for circular reference resolution. 10 // Also remove any beans that received a temporary reference to the bean. 11 destroySingleton(beanName); 12 throw ex; 13 } 14 } 15 }); 16 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); 17 }
It prefers to try getSington, that is, to try to get the existence of beans from singleton Objects, if there is a bean object that returns directly to singleton Objects.
Next, we see that the difference between prototype bean creation and single bean creation lies in lines 5 and 9. Look at the code in line 5 first.
1 protected void beforePrototypeCreation(String beanName) { 2 Object curVal = this.prototypesCurrentlyInCreation.get(); 3 if (curVal == null) { 4 this.prototypesCurrentlyInCreation.set(beanName); 5 } 6 else if (curVal instanceof String) { 7 Set<String> beanNameSet = new HashSet<String>(2); 8 beanNameSet.add((String) curVal); 9 beanNameSet.add(beanName); 10 this.prototypesCurrentlyInCreation.set(beanNameSet); 11 } 12 else { 13 Set<String> beanNameSet = (Set<String>) curVal; 14 beanNameSet.add(beanName); 15 } 16 }
The main purpose of this section is to set the current bean Name to ThreadLocal before the bean is created. The purpose is to ensure that multithreads do not create the same bean at the same time. Then look at the code implementation of line 9, which is what the bean did after it was created:
1 protected void afterPrototypeCreation(String beanName) { 2 Object curVal = this.prototypesCurrentlyInCreation.get(); 3 if (curVal instanceof String) { 4 this.prototypesCurrentlyInCreation.remove(); 5 } 6 else if (curVal instanceof Set) { 7 Set<String> beanNameSet = (Set<String>) curVal; 8 beanNameSet.remove(beanName); 9 if (beanNameSet.isEmpty()) { 10 this.prototypesCurrentlyInCreation.remove(); 11 } 12 } 13 }
It's a good idea to remove the current bean so that other threads can create beans. Line 11 ignores the code, meaning that if the bean is an implementation class of FactoryBean, call the getObject() method to get the real object.
byName source code implementation
Spring has the function of providing developers with Autowire (automatic assembly). The two most commonly used attributes of automatic assembly are byName and byType. Because automatic assembly is to solve the problem of < property > excessive caused by object injection, it is easy to find that byName and byType Spring source implementations should be in the property injection block, and locate the populateBean method of AbstractAutowire Capable BeanFactory, which is the code of property injection, to intercept the key points directly:
1 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME || 2 mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { 3 MutablePropertyValues newPvs = new MutablePropertyValues(pvs); 4 5 // Add property values based on autowire by name if applicable. 6 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) { 7 autowireByName(beanName, mbd, bw, newPvs); 8 } 9 10 // Add property values based on autowire by type if applicable. 11 if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) { 12 autowireByType(beanName, mbd, bw, newPvs); 13 } 14 15 pvs = newPvs; 16 }
When you see lines 6 to 8 judging byName form, you execute byName auto-assembly code; lines 11 to 13 judging byType form, you execute byType auto-assembly code. Let's first look at the byName code implementation in line 7:
1 protected void autowireByName( 2 String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { 3 4 String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); 5 for (String propertyName : propertyNames) { 6 if (containsBean(propertyName)) { 7 Object bean = getBean(propertyName); 8 pvs.add(propertyName, bean); 9 registerDependentBean(propertyName, beanName); 10 if (logger.isDebugEnabled()) { 11 logger.debug("Added autowiring by name from bean name '" + beanName + 12 "' via property '" + propertyName + "' to bean named '" + propertyName + "'"); 13 } 14 } 15 else { 16 if (logger.isTraceEnabled()) { 17 logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName + 18 "' by name: no matching bean found"); 19 } 20 } 21 } 22 }
As for length, the code does not follow each other layer by layer. Let's sort it out logically.
- Line 4: Finding attributes that are not simple attributes in beans is somewhat ambiguous, meaning that finding attributes is attributes of object types, but not all object types can be found, such as CharSequence type, Number type, Date type, URL type, URI type, Locale type and Class type, which can be seen in Bean Utils'ispleProperty method.
- Lines 5 to 7 traverse all the properties found. If the bean definition contains the attribute name, instantiate the bean corresponding to the attribute name first.
- Line 9 registerDependentBean, register the dependent beans of the current bean for destroying the dependent beans before a bean is destroyed
The rest of the code is logged, there's nothing to say.
byType source code implementation
The source implementation of byName is described above. Next, take a look at the byType source implementation:
1 protected void autowireByType( 2 String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { 3 4 TypeConverter converter = getCustomTypeConverter(); 5 if (converter == null) { 6 converter = bw; 7 } 8 9 Set<String> autowiredBeanNames = new LinkedHashSet<String>(4); 10 String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); 11 for (String propertyName : propertyNames) { 12 try { 13 PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); 14 // Don't try autowiring by type for type Object: never makes sense, 15 // even if it technically is a unsatisfied, non-simple property. 16 if (!Object.class.equals(pd.getPropertyType())) { 17 MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); 18 // Do not allow eager init for type matching in case of a prioritized post-processor. 19 boolean eager = !PriorityOrdered.class.isAssignableFrom(bw.getWrappedClass()); 20 DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); 21 Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter); 22 if (autowiredArgument != null) { 23 pvs.add(propertyName, autowiredArgument); 24 } 25 for (String autowiredBeanName : autowiredBeanNames) { 26 registerDependentBean(autowiredBeanName, beanName); 27 if (logger.isDebugEnabled()) { 28 logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" + 29 propertyName + "' to bean named '" + autowiredBeanName + "'"); 30 } 31 } 32 autowiredBeanNames.clear(); 33 } 34 } 35 catch (BeansException ex) { 36 throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex); 37 } 38 } 39 }
As before, line 10 finds that the attribute in the Bean is an attribute of the object type.
Next, go through PropertyName to get the property description corresponding to PropertyName. Note the 16-line judgment and the corresponding comment: Don't try to assemble the Object type automatically. It doesn't make any sense, even if it's a non-simple object attribute from a technical point of view.
Skipping lines 18 to 20 (not quite clear what to do), the source code for byType implementation is mainly in the method resolveDependency of line 21, which is the method in the implementation class DefaultListableBeanFactory of the AbstractAutowireCapableBeanFactory class:
1 public Object resolveDependency(DependencyDescriptor descriptor, String beanName, 2 Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException { 3 4 descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); 5 if (descriptor.getDependencyType().equals(ObjectFactory.class)) { 6 return new DependencyObjectFactory(descriptor, beanName); 7 } 8 else if (descriptor.getDependencyType().equals(javaxInjectProviderClass)) { 9 return new DependencyProviderFactory().createDependencyProvider(descriptor, beanName); 10 } 11 else { 12 return doResolveDependency(descriptor, descriptor.getDependencyType(), beanName, autowiredBeanNames, typeConverter); 13 } 14 }
Let's decide whether the property to be assembled automatically is ObjectFactory.class or javax InjectProvider Class or something else. We assemble something else. Look at the 12-line code implementation.
1 protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName, 2 Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException { 3 4 Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); 5 if (value != null) { 6 if (value instanceof String) { 7 String strVal = resolveEmbeddedValue((String) value); 8 BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); 9 value = evaluateBeanDefinitionString(strVal, bd); 10 } 11 TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); 12 return converter.convertIfNecessary(value, type); 13 } 14 15 if (type.isArray()) { 16 ... 17 } 18 else if (Collection.class.isAssignableFrom(type) && type.isInterface()) { 19 ... 20 } 21 else if (Map.class.isAssignableFrom(type) && type.isInterface()) { 22 ... 23 } 24 else { 25 Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); 26 if (matchingBeans.isEmpty()) { 27 if (descriptor.isRequired()) { 28 raiseNoSuchBeanDefinitionException(type, "", descriptor); 29 } 30 return null; 31 } 32 if (matchingBeans.size() > 1) { 33 String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor); 34 if (primaryBeanName == null) { 35 throw new NoSuchBeanDefinitionException(type, "expected single matching bean but found " + 36 matchingBeans.size() + ": " + matchingBeans.keySet()); 37 } 38 if (autowiredBeanNames != null) { 39 autowiredBeanNames.add(primaryBeanName); 40 } 41 return matchingBeans.get(primaryBeanName); 42 } 43 // We have exactly one match. 44 Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); 45 if (autowiredBeanNames != null) { 46 autowiredBeanNames.add(entry.getKey()); 47 } 48 return entry.getValue(); 49 } 50 }
The fourth line turns out to be null. In order to simplify the Array assembly, Collection assembly, Map assembly, the code is omitted, focusing on the assembly of common attributes. Firstly, line 25 gets the candidates for automatic assembly:
1 protected Map<String, Object> findAutowireCandidates( 2 String beanName, Class requiredType, DependencyDescriptor descriptor) { 3 4 String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( 5 this, requiredType, true, descriptor.isEager()); 6 Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length); 7 for (Class autowiringType : this.resolvableDependencies.keySet()) { 8 if (autowiringType.isAssignableFrom(requiredType)) { 9 Object autowiringValue = this.resolvableDependencies.get(autowiringType); 10 autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType); 11 if (requiredType.isInstance(autowiringValue)) { 12 result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue); 13 break; 14 } 15 } 16 } 17 for (String candidateName : candidateNames) { 18 if (!candidateName.equals(beanName) && isAutowireCandidate(candidateName, descriptor)) { 19 result.put(candidateName, getBean(candidateName)); 20 } 21 } 22 return result; 23 }
Sort out the code logic:
- First, the candidate bean name is obtained, and then the getBeanNamesForType method of DefaultListableBeanFactory is used to find the implementation class or subclass of the specified Type in all bean definitions.
- Then line 7 to line 16 determines whether the type to be automatically assembled is the correct type to be automatically assembled. This is in? [Spring Source Analysis] Some operations before and after initialization of non-lazy-loaded singleton beans As mentioned in the PrepareBeanFactory method, if the type to be assembled automatically is a corrected type, such as a ResourceLoader, a proxy instance will be generated for that type. See the implementation of the AutowireUtils.resolveAutowiringValue method in line 10.
- Normally, it is executed in line 17 to line 21. Look up the Bean Definition corresponding to the bean Name one by one to determine whether the candidate is automatically assembled. By default, it is all. If the autowire-candidate attribute of < bean> is set to false, it is not?
In this way, we get all the candidates of the implementation class or subclass of the object to be assembled and form a Map with Key as Bean Name and Value as concrete beans. Then look back at the logic after getting the Bean:
1 Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); 2 if (matchingBeans.isEmpty()) { 3 if (descriptor.isRequired()) { 4 raiseNoSuchBeanDefinitionException(type, "", descriptor); 5 } 6 return null; 7 } 8 if (matchingBeans.size() > 1) { 9 String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor); 10 if (primaryBeanName == null) { 11 throw new NoSuchBeanDefinitionException(type, "expected single matching bean but found " + 12 matchingBeans.size() + ": " + matchingBeans.keySet()); 13 } 14 if (autowiredBeanNames != null) { 15 autowiredBeanNames.add(primaryBeanName); 16 } 17 return matchingBeans.get(primaryBeanName); 18 } 19 // We have exactly one match. 20 Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); 21 if (autowiredBeanNames != null) { 22 autowiredBeanNames.add(entry.getKey()); 23 } 24 ... 25 }
Tidy up the logic:
- If the resulting Map is empty and the attribute must be injected, throw an exception
- If there are multiple candidates in the Map, judge whether there are < beans > attributes configured as "primary=true". If there are, execute the code from line 13 to line 15 without returning null and throwing exceptions on line 8. I believe Spring should be familiar with the description of exceptions.
- If there is only one candidate in the Map, get that directly.
Through this whole process, byType automatic assembly is realized. The byType automatic assembly process is longer, and there are more details in the middle. We need to see more before we can understand it.
Finally, note that all the Property Name - > Property Value mappings to be injected are only placed in Mutable Property Values after they are obtained. Finally, they are traversed by the setProperty Values method of the AbstractProperty Accessor class and injected one by one.
Acquisition of Bean Instance Source Code by FactoryBean
We know that personalized customization of beans can be achieved by implementing the FactoryBean interface and rewriting the getObject() method. In this section, we will look at how Spring source code achieves obtaining Bean instances through FactoryBean. The code directly locates the doGetBean method of AbstractBeanFactory to create the singleton Bean section:
1 // Create bean instance. 2 if (mbd.isSingleton()) { 3 sharedInstance = getSingleton(beanName, new ObjectFactory() { 4 public Object getObject() throws BeansException { 5 try { 6 return createBean(beanName, mbd, args); 7 } 8 catch (BeansException ex) { 9 // Explicitly remove instance from singleton cache: It might have been put there 10 // eagerly by the creation process, to allow for circular reference resolution. 11 // Also remove any beans that received a temporary reference to the bean. 12 destroySingleton(beanName); 13 throw ex; 14 } 15 } 16 }); 17 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); 18 }
FactoryBean is first a Bean and is instantiated into an object before calling the getObject() method, so it still executes lines 3 to 16, which has been analyzed before. Then the method of line 17 is executed:
1 protected Object getObjectForBeanInstance( 2 Object beanInstance, String name, String beanName, RootBeanDefinition mbd) { 3 4 // Don't let calling code try to dereference the factory if the bean isn't a factory. 5 if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) { 6 throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass()); 7 } 8 9 // Now we have the bean instance, which may be a normal bean or a FactoryBean. 10 // If it's a FactoryBean, we use it to create a bean instance, unless the 11 // caller actually wants a reference to the factory. 12 if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { 13 return beanInstance; 14 } 15 16 Object object = null; 17 if (mbd == null) { 18 object = getCachedObjectForFactoryBean(beanName); 19 } 20 if (object == null) { 21 // Return bean instance from factory. 22 FactoryBean factory = (FactoryBean) beanInstance; 23 // Caches object obtained from FactoryBean if it is a singleton. 24 if (mbd == null && containsBeanDefinition(beanName)) { 25 mbd = getMergedLocalBeanDefinition(beanName); 26 } 27 boolean synthetic = (mbd != null && mbd.isSynthetic()); 28 object = getObjectFromFactoryBean(factory, beanName, !synthetic); 29 } 30 return object; 31 }
Lines 5 to 7 first determine whether the bean Name starts with "&" and is not an implementation class of FactoryBean, and throws an exception if it is not satisfied, because the bean Name starts with "&" is a feature of the implementation class bean definition of FactoryBean.
Then judge lines 12 to 14 if:
- Beans are not implementations of FactoryBean s
- beanName begins with "&"
In both cases, the generated bean object is returned directly without executing the remaining process.
Finally, the process goes to line 16 to line 30, and finally calls the getObject() method to implement personalized customized bean s. First, the method of line 28 is executed:
1 protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) { 2 if (factory.isSingleton() && containsSingleton(beanName)) { 3 synchronized (getSingletonMutex()) { 4 Object object = this.factoryBeanObjectCache.get(beanName); 5 if (object == null) { 6 object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess); 7 this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT)); 8 } 9 return (object != NULL_OBJECT ? object : null); 10 } 11 } 12 else { 13 return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess); 14 } 15 }
Lines 1 to 11 end up being the same as lines 12 to 13, calling the following paragraph:
1 private Object doGetObjectFromFactoryBean( 2 final FactoryBean factory, final String beanName, final boolean shouldPostProcess) 3 throws BeanCreationException { 4 5 Object object; 6 try { 7 if (System.getSecurityManager() != null) { 8 AccessControlContext acc = getAccessControlContext(); 9 try { 10 object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { 11 public Object run() throws Exception { 12 return factory.getObject(); 13 } 14 }, acc); 15 } 16 catch (PrivilegedActionException pae) { 17 throw pae.getException(); 18 } 19 } 20 else { 21 object = factory.getObject(); 22 } 23 } 24 catch (FactoryBeanNotInitializedException ex) { 25 throw new BeanCurrentlyInCreationException(beanName, ex.toString()); 26 } 27 catch (Throwable ex) { 28 throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex); 29 } 30 31 // Do not accept a null value for a FactoryBean that's not fully 32 // initialized yet: Many FactoryBeans just return null then. 33 if (object == null && isSingletonCurrentlyInCreation(beanName)) { 34 throw new BeanCurrentlyInCreationException( 35 beanName, "FactoryBean which is currently in creation returned null from getObject"); 36 } 37 38 if (object != null && shouldPostProcess) { 39 try { 40 object = postProcessObjectFromFactoryBean(object, beanName); 41 } 42 catch (Throwable ex) { 43 throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex); 44 } 45 } 46 47 return object; 48 }
Lines 12 and 21, the same code, ultimately calls the getObject() method to get the object. Looking back at the previous getObjectFromFactoryBean method, although if...else... logic ultimately calls the above method, the difference is that:
- If the isSington method of the FactoryBean interface implements the class returns true, then every time the getObject method is invoked, it will first try to retrieve the target object from the FactoryBean object cache, take it directly, and create and put it into the FactoryBean object cache without it. This ensures that the final target object is a single object after every single FactoryBean invokes the getObject() method. For example, it is the same copy in memory.
- If the isSington method of the FactoryBean interface implementation class returns false, a new target object is created every time the getObject method is called.