Spring source code analysis 4: Bean rest of the way to get the process analysis

Prototype Bean loading process

Previous article analyzes the Singleton Bean non-lazy loading of the entire loading process, in addition to the single case of non-Bean lazy loading, Spring There is also a Bean is the prototype (Prototype) of Bean, look at the definition of the way:

1 
 
 
2 
 
 
  
  
6 
7     
  
  
   
8     
9 
 
  
 

Generally speaking prototype Bean loading process and almost single cases Bean, look at the differences, in this step of the method AbstractBeanFactory of doGetBean:

 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 as the prototype Bean instantiation main difference lies in the line 6, which is created directly bean, and singleton bean Let us compare:

 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 priority will try getSington, which is to try to get it from singletonObjects the bean exists, if there is a direct return to the bean object singletonObjects.

Next, we see that the difference between the prototype and create a single bean bean embodiment is further characterized by creating and row 5, line 9, look at the code line 5:

 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
 
 
  
   beanNameSet = new HashSet
  
  
   
   (2);
 8         beanNameSet.add((String) curVal);
 9 beanNameSet.add(beanName); 10         this.prototypesCurrentlyInCreation.set(beanNameSet); 11 } 12     else { 13         Set
   
   
    
     beanNameSet = (Set
    
    
      ) CurVal; 14 beanNameSet.add (beanName); 15} 16}
    
    
   
   
  
  
 
 

This main point is that bean put before the creation of the current ThreadLocal beanName set to go, the aim is to ensure that multiple threads at the same time does not create the same bean. Then look at the code line 9 of the realization that the bean After creating what has been done:

 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
 
 
  
   beanNameSet = (Set
  
  
   
   ) curVal;
 8         beanNameSet.remove(beanName);
 9         if (beanNameSet.isEmpty()) { 10             this.prototypesCurrentlyInCreation.remove(); 11 } 12 } 13 }
  
  
 
 

Well understood, it is to look at the current bean removed, so that other threads can create a bean. The first 11 lines of code do not read, meaning that if the bean implementation class is FactoryBean then call getObject () method to get real object.

byName source code implementation

Spring promising developers with Autowire (auto assembly) function, automatic assembly and most common is byName byType these two attributes. Since the object is to solve the automatic assembling due to injection Too many problems, so it is easy to find Spring byName and byType source code to achieve this one should be injected in the property, navigate to populateBean method attributes inject code AbstractAutowireCapableBeanFactory direct interception of focus:

 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 }

See line 6 to line 8 determines whether byName form, is executed byName autowiring codes; ~ line 11, line 13 determines whether byType form, is automatically executed byType assembly code. So first of all look at the code on line 7 byName achieve:

 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 }

Space problem, with the layers of different codes, logical comb:

  • Line 4, find the Bean is not a simple attribute properties, these words around a bit, meaning that the object is to find the property type of property, but not all types of objects will be found, such as the type CharSequence, Number Type, Date type, URL type, URI type, Locale type, Class type will be ignored, visible isSimpleProperty specific method of BeanUtils
  • 5, lines - 7 lines, through all attributes are found, if the bean definition contains an attribute name, then the first instance of the name attribute corresponding bean
  • Line 9 registerDependentBean, registered at the current bean depends bean, a bean for the destruction of the first to be destroyed before it depends on bean

Rest of the code are some of the play log, nothing to say.

byType source code implementation

Above that byName source code to achieve, let's look at byType source code to achieve:

 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
 
 
  
   autowiredBeanNames = new LinkedHashSet
  
  
   
   (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 }
  
  
 
 

Like before, the line 10 is found in the Bean property is the object type attributes.

Then it is to walk PropertyName, get property descriptors corresponding PropertyName, take note of Judgment and the corresponding Note 16 line: Do not attempt to automatically assemble Object type, it does not make sense, even from a technical point of view it is a non-simple object properties .

Line 18 through line 20 is skipped (not too clear is doing), the main source of the byType implemented method resolveDependency line 21, the method is a method to achieve this type of class DefaultListableBeanFactory AbstractAutowireCapableBeanFactory:

 1 public Object resolveDependency(DependencyDescriptor descriptor, String beanName, 2     Set
 
 
  
   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 }
 
 

Here I want to automatically determine what property is ObjectFactory.class assembly or javaxInjectProviderClass or the other, we are assembling the other, look at the code line to achieve 12:

 1 protected Object doResolveDependency(DependencyDescriptor descriptor, Class
 
  type, String beanName, 2     Set
 
 
  
   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
  
  
   
    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
   
   
    
     entry = matchingBeans.entrySet().iterator().next(); 45         if (autowiredBeanNames != null) { 46 autowiredBeanNames.add(entry.getKey()); 47 } 48         return entry.getValue(); 49 } 50 }
   
   
  
  
 
 

The result is the fourth line null do not read, to simplify the code Array assembly, Collection assembly code Map assembly are omitted, emphasis ordinary look at the assembling property. First Line 25 candidates obtain at automatic assembly:

 1 protected Map
 
 
  
   findAutowireCandidates( 2     String beanName, Class requiredType, DependencyDescriptor descriptor) {
 3 
 4     String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( 5             this, requiredType, true, descriptor.isEager());
 6     Map
  
  
   
    result = new LinkedHashMap
   
   
    
    (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 }
   
   
  
  
 
 

Code logic to sort out:

  • First acquisition candidates bean name, by getBeanNamesForType DefaultListableBeanFactory method, that is looking at all of the definitions specified Type Bean implementation class or subclass
  • Next type judgment line 7 to line 16 to automatic assembling is not to be automatically assembled correct type, the in [Analysis Spring source] before and after a single embodiment Bean initialize non lazy loading of some of the operations a Texts PrepareBeanFactory method when there is I said before, if the type to be automatically assembled is correct type, such as a a ResourceLoader, will generate a proxy instance for the type, specifically look at the implementation of the method AutowireUtils.resolveAutowiringValue line 10
  • Normally, the code is 17, line 21 - line executed one by one to find what is determined corresponding to the BeanDefinition beanName, a judgment is not a candidate for automatic assembly, is the default, if The autowire-candidate attribute is not set to false

Thus, to get the candidate class or subclass to achieve all the objects to be assembled to form a Map, Key is beanName, Value concrete Bean. Then look back at the logic after the acquisition Bean:

 1 Map
 
 
  
   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
  
  
   
    entry = matchingBeans.entrySet().iterator().next(); 21     if (autowiredBeanNames != null) { 22 autowiredBeanNames.add(entry.getKey()); 23 } 24     ... 25 }
  
  
 
 

Sort out the logic:

  • If you get the Map is empty and the property must be injected, Throws
  • If get a plurality of candidates in the Map, it is determined whether there Configuration attribute is "primary = true", there are 13 lines of code execution took ~ on line 15, the first method is not the line 8 returns null, throw exception that description believed Spring be used more more familiar
  • If you get the Map is only one candidate, take it directly to the

By this way the whole process to achieve byType automatic assembly, automatic assembly process byType longer, more middle details, also need to look at to figure it out.

Finally note that all that is to be injected PropertyName -> PropertyValue after mapping are simply placed MutablePropertyValues ​​get the last traversal by the method AbstractPropertyAccessor setPropertyValues ​​class and one by one injection.

Get Bean examples of source code to achieve through FactoryBean

We know that can be achieved by FactoryBean interface rewrite getObject () method to achieve Bean customization process, this part we will look at how Spring source is achieved by obtaining Bean instance of FactoryBean. DoGetBean code is located directly create a single embodiment of the method of AbstractBeanFactory Bean this 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 The first is to call the getObject () method and after a Bean is instantiated out to be an object, so it will execute the code 3, line 16 - line, analyzed before this code will not say. After the execution of line 17 methods:

 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 }

First line to the fifth line 7 beanName to determine what the "&" at the beginning of the implementation class and not FactoryBean, throw exception is not satisfied, because the "&" is the beginning of the implementation class of a bean definition FactoryBean beanName feature.

Then determines 12 line 14 - line, if:

  • bean implementation class is not FactoryBean
  • beanName with "&" at the beginning

In both cases, directly to the return generated bean object out, the rest of the process will not be executed.

Finally, the process went 16 th to the 30 line, the final call to getObject () method to achieve customization bean, performed first on line 28 methods:

 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 }

Code ~ code, line 13, line 12, line 11 of the first line to the final is the same, the following paragraph call:

 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() { 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 }

第12行和第21行的代码,都一样,最终调用getObject()方法获取对象。回过头去看之前的getObjectFromFactoryBean方法,虽然if...else...逻辑最终都是调用了以上的方法,但是区别在于:

  • 如果FactoryBean接口实现类的isSington方法返回的是true,那么每次调用getObject方法的时候会优先尝试从FactoryBean对象缓存中取目标对象,有就直接拿,没有就创建并放入FactoryBean对象缓存,这样保证了每次单例的FactoryBean调用getObject()方法后最终拿到的目标对象一定是单例的,即在内存中都是同一份
  • 如果FactoryBean接口实现类的isSington方法返回的是false,那么每次调用getObject方法的时候都会新创建一个目标对象

图片描述

微信公众号【黄小斜】作者是蚂蚁金服 JAVA 工程师,专注于 JAVA
后端技术栈:SpringBoot、SSM全家桶、MySQL、分布式、中间件、微服务,同时也懂点投资理财,坚持学习和写作,相信终身学习的力量!关注公众号后回复”架构师“即可领取
Java基础、进阶、项目和架构师等免费学习资料,更有数据库、分布式、微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送作者原创的Java学习指南、Java程序员面试指南等干货资源

posted @ 2019-08-25 14:02  黄小斜 阅读( ...) 评论( ...) 编辑 收藏

Guess you like

Origin www.cnblogs.com/xll1025/p/11407749.html