Spring: continue to create singleton beans, expose them in advance, inject attributes and initialize beans

Keywords: Java Spring Back-end

review

In the previous article, we analyzed how to create a regular bean. We returned to the doCreateBean method in our AbstractAutowireCapableBeanFactory. Now we have generated the beanWrapper

//First, it depends on whether a singleton is allowed, whether circular dependency is allowed, and whether the bean is being created
//To determine whether exposure is required	
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
            //If exposure is required, turn on the log
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
            //Put beanName and its ObjectFactory into SingleFactory, that is, cache
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

addSingletonFactory method

The source code is as follows

	protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
        //Lock
		synchronized (this.singletonObjects) {
            //Determine whether the cache exists
            //If it doesn't exist
			if (!this.singletonObjects.containsKey(beanName)) {
                //If not in the L1 cache, add beanName and its beanfactory to the L3 cache
				this.singletonFactories.put(beanName, singletonFactory);
                //Remove beanName from L2 cache
				this.earlySingletonObjects.remove(beanName);
                //The beanName is stored in the registered singleton
				this.registeredSingletons.add(beanName);
			}
		}
	}

As you can see, the function of this method is to add the generated singleton bean to the L3 cache for exposure, and identify that the bean has been registered

The reason why the bean is exposed in advance here is that the bean has not been created yet. The beanWrapper is only generated through the constructor. The dependent bean has not been created yet, so the bean here has not been created yet (lack of creation of dependent bean). Therefore, it is necessary to expose in advance to detect whether circular dependency occurs to the dependent bean

populateBean method

At this point, we have exposed the beanWrapper. Next, we need to use the populateBean method to inject attributes

The source code is as follows

	protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
		//If beanWrapper is null
        //If the instance at this time is null, property injection cannot be performed
        if (bw == null) {
            //Judge whether there are prototype values waiting for injection in RootBeanDefinition
            //If yes, throw it wrong because it cannot be injected
			if (mbd.hasPropertyValues()) {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
			}
			else {
				// Skip property population phase for null instance.
                //If not, return directly 
				return;
			}
		}

		// 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.
        // In the instance aware bean postprocessors, give one last chance to change the beanwrapper
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
				if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
					return;
				}
			}
		}
		//Start attribute injection
        //Gets the property to inject
		PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
		//Gets the pattern of RootBeanDefinition injection
		int resolvedAutowireMode = mbd.getResolvedAutowireMode();
        //You can see the familiar byName and byType
		if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
			// Add property values based on autowire by name if applicable.
            //Property injection based on byName
			if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}
			// Add property values based on autowire by type if applicable.
            //Attribute injection according to byType
			if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}
            //Store the extracted dependent bean s in newPvs
			pvs = newPvs;
		}
		//Determine whether the subsequent processor has been initialized
		boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
        //Determine whether dependency check is required
		boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

		PropertyDescriptor[] filteredPds = null;
		if (hasInstAwareBpps) {
            //If the subsequent processor has been initialized
			if (pvs == null) {
				pvs = mbd.getPropertyValues();
			}
            //
			for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
				PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
				if (pvsToUse == null) {
					if (filteredPds == null) {
						filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
					}
                    //Post process all attributes requiring dependency check, but there is no dependency check at this time
					pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
					if (pvsToUse == null) {
						return;
					}
				}
				pvs = pvsToUse;
			}
		}
        //Type check if required
		if (needsDepCheck) {
			if (filteredPds == null) {
				filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
			}
            //Dependency check corresponds to the dependencies on attribute, which has been deprecated in 3.0
			checkDependencies(beanName, mbd, filteredPds, pvs);
		}

		if (pvs != null) {
            //Apply properties to bean s
			applyPropertyValues(beanName, mbd, bw, pvs);
		}
	}

Summarize the function of the populateBan method

  • Call the postprocessafterinstance function of the instantiaawarebeanpostprocessor processor
  • Extract the dependent bean s according to the injected type, byType or byName, and store them in PropertyValues
  • Apply the postProcessPropertyValues method processed by InstantiatinAwareBeanPostProcessor to process the injected properties again
  • Fill the found properties into BeanWrapper, that is, PropertyValues

The focus here is on the injection of the second and fourth steps. Other feelings are too difficult to see...

Extract dependent bean s according to the injected type

There are two types of injection

  • byType
  • byName

We can set the injection type for the bean

<bean id = "" class = "" autowire="byType|byName|constructor|default"/>

The automatic injection here is to automatically inject the dependent bean, not itself...

The corresponding code is as follows

AUTOWIRE_BY_NAME

This is an automatic injection method based on the name, that is, the dependent name attribute

The source code is as follows

	protected void autowireByName(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

        //Get the dependency attributes that need to be injected. Note that this is not a simple type, that is, the basic data type
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
        //Traverse these dependent properties
		for (String propertyName : propertyNames) {
            //Determine whether this bean exists in the IOC container of Spring
            //You can see that this is the ByName form
			if (containsBean(propertyName)) {
                //Call the getBean method to create
                //You can see how dependency is created recursively in singleton
                //And how to detect cyclic dependencies
				Object bean = getBean(propertyName);
                //Add the generated bean to the new PropertyValues
				pvs.add(propertyName, bean);
                //Register the bean
				registerDependentBean(propertyName, beanName);
				if (logger.isTraceEnabled()) {
					logger.trace("Added autowiring by name from bean name '" + beanName +
							"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
				}
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
							"' by name: no matching bean found");
				}
			}
		}
	}

The steps are as follows

  • Get the properties that you want to depend on
  • Traverse the properties that you need to depend on
  • Check whether the bean exists in the IOC container according to the bean name of the dependent attribute, reflecting ByName
    • If the bean does not exist, an error is thrown, indicating that no matching bean is found
    • If the bean exists, call the getBean method to create the dependent bean (go through that process again, so in the final analysis, creating a bean is a recursive process)
      • Put the created bean into PropertyValues
      • Register the bean

The overall logic is nothing. Let's see how to obtain the dependent attributes

unsatisfiedNonSimpleProperties

Corresponding to the specific method of obtaining dependent attributes, this method will actively exclude simple attributes, namely reference types and basic data types

The source code is as follows

protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
    //Create a treeSet for de duplication
   Set<String> result = new TreeSet<>();
    //Get all dependencies
   PropertyValues pvs = mbd.getPropertyValues();
   PropertyDescriptor[] pds = bw.getPropertyDescriptors();
    //
   for (PropertyDescriptor pd : pds) {
       //Only the set method is written
       //Is not in a collection that does not check dependencies
       //Not a simple type
      if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
            !BeanUtils.isSimpleProperty(pd.getPropertyType())) {
          //Add it after meeting the conditions
         result.add(pd.getName());
      }
   }
   return StringUtils.toStringArray(result);
}

The code is more abstract, so I decided to take chestnuts as an example

A configuration file is simply an injection type

Class is also very simple. It simply provides a set method injection

Let's look directly at what those two sets are

PropertyDescriptor[] pds = bw.getPropertyDescriptors();

give the result as follows


There are two objects in it

One is the bean's own class without writeMethod


Another one is about twoPojo, and there is a formal set method called writeMethod!!!!

Therefore, it can be inferred that this collection obtains the attribute details that need to be relied on

PropertyValues pvs = mbd.getPropertyValues();

The contents are as follows


As you can see, it is completely the information in the configuration file....

Therefore, this collection stores the dependent attribute information in the configuration file

Now, it's easy to see what this for loop eliminates

Back to our for loop

   for (PropertyDescriptor pd : pds) {
       //Only the set method is written
       //Is not in a collection that does not check dependencies
       //Not a simple type
       //And there is no beanName in the configuration file,
       //If there is in the configuration file, just go to the IOC container and get it. There is no need for automatic injection
       //Instead of injection
      if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
            !BeanUtils.isSimpleProperty(pd.getPropertyType())) {
          //Add it after meeting the conditions
         result.add(pd.getName());
      }
   }


You can see the result. The result is 0

Suppose we remove the property of the configuration file

You can see the result with twoPOJO

Let's take a look at what BeanUtils filters out



You can see some types of filtering

  • void type

  • isPrimitiveOrWrapper: judge whether it is a wrapper class, that is, a filter wrapper class

  • Enum type

  • Date type

  • URI type

  • URL type

  • Locale type

  • Class type

  • CharSequence type

To summarize, which dependent bean s will be used

  • Dependencies that do not write the Set method are filtered
  • Dependencies in collections that are not checked are filtered
  • The type of injection that has been specified in the configuration file will be filtered
  • Simple types are also filtered

AUTOWIRE_BY_TYPE

There is also an automatic injection type, which is to find according to the type

The corresponding codes are as follows

The corresponding method is autowireByType

The source code is as follows

protected void autowireByType(
			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
		//Get type processor
		TypeConverter converter = getCustomTypeConverter();
		if (converter == null) {
			converter = bw;
		}
		//Use a LinkedHashSet to store the bean name of the bean that needs to be automatically injected
    	//Previously, beanName was stored using a set for de duplication, and then converted into a string array
		Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
    	//You can see that filtering is also required here, so I won't repeat it
		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    	//Traversal of dependencies to be injected
		for (String propertyName : propertyNames) {
			try {
                
				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.
				//Judge whether the dependency is of Object type (Note: Object type is not filtered in filtering)
                if (Object.class != pd.getPropertyType()) {
                    //Get the parameters of the bean's set method..
                    //It is equivalent to getting the type.. Reflects ByType
					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);
					//Resolve the to that matches the property of the specified beanName, and store the resolved property name in autowiredBeanNames
                    Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
					if (autowiredArgument != null) {
						pvs.add(propertyName, autowiredArgument);
					}
                    //Traverse the bean to be injected
					for (String autowiredBeanName : autowiredBeanNames) {
						//Registration dependency
                        registerDependentBean(autowiredBeanName, beanName);
						if (logger.isTraceEnabled()) {
							logger.trace("Autowiring by type from bean name '" + beanName + "' via property '" +
									propertyName + "' to bean named '" + autowiredBeanName + "'");
						}
					}
					autowiredBeanNames.clear();
				}
			}
			catch (BeansException ex) {
				throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
			}
		}
	}

Two details can be seen here

  • Spring does not inject ByType for Object type
  • The Type is obtained through the parameters of the Set method in the past. The parameter is actually Type, which can be seen as the embodiment of ByType
  • Create dependencies through resolveDependency
resolveDependency


It can be seen that the creation of ByType dependency is implemented by DefaultListableBeanFactory, while the creation of ByName dependency is implemented by AbstractAutowireCapableFactory

The source code is as follows

	public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
		
        //
		descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
        //Extended support for the Optional class
		if (Optional.class == descriptor.getDependencyType()) {
            //The Optional class is created using methods alone
			return createOptionalDependency(descriptor, requestingBeanName);
		}
        //ObjectFactory and ObjectProvider are also created by separate methods
		else if (ObjectFactory.class == descriptor.getDependencyType() ||
				ObjectProvider.class == descriptor.getDependencyType()) {
			return new DependencyObjectProvider(descriptor, requestingBeanName);
		}
        //Handle JavaInjectProviderClass separately
		else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
			return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
		}
		else {
            //For other general types
            //Use autowirecandidate resolver for injection
			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);
			if (result == null) {
                //If autpwirecandideresolver injection fails, use doResolveDependency for injection
				result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
			}
			return result;
		}
	}

It can be seen that for some specific types, other types will take the same steps (most of them)

getLazyResollutionProxyIfNecessary

For general types, this method will be used first

From the annotation of this method, it feels like adding a proxy to the pointcut..

However, its default implementation directly returns Null, so don't look at it first.

doResolveDependency

Next, execute this method for processing

The source code is as follows. You can see that it is very scary...

	public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

		InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
		try {
			Object shortcut = descriptor.resolveShortcut(this);
			if (shortcut != null) {
				return shortcut;
			}
			//Get the class object, that is, the parameters obtained through the set method
			Class<?> type = descriptor.getDependencyType();
            //New @ Value annotation is supported
            //@The Value annotation can be used directly on the class, so the annotation is obtained according to the class
            //Then go to the corresponding configuration for injection
			Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
            //Judge whether @ Value is injected
            //So ByName will not be injected by @ Value????
			if (value != null) {
                //If yes, the following actions will be performed
				if (value instanceof String) {
					String strVal = resolveEmbeddedValue((String) value);
					BeanDefinition bd = (beanName != null && containsBean(beanName) ?
							getMergedBeanDefinition(beanName) : null);
					value = evaluateBeanDefinitionString(strVal, bd);
				}
				TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
				try {
					return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
				}
				catch (UnsupportedOperationException ex) {
					// A custom TypeConverter which does not support TypeDescriptor resolution...
					return (descriptor.getField() != null ?
							converter.convertIfNecessary(value, type, descriptor.getField()) :
							converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
				}
			}
			//Consider whether this attribute is of multiple beans, that is, to handle collection types
			Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
			if (multipleBeans != null) {
				return multipleBeans;
			}
			//Find all candidates
			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
			if (matchingBeans.isEmpty()) {
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				return null;
			}

			String autowiredBeanName;
			Object instanceCandidate;
			//Judge the number of candidates
			if (matchingBeans.size() > 1) {
                //If there are two or more meanings, do the following
                //Decide which candidate to use
				autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
                //Judge whether it can be decided
				if (autowiredBeanName == null) {
                    //
					if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
						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).
						return null;
					}
				}
				instanceCandidate = matchingBeans.get(autowiredBeanName);
			}
			else {
                //If there is only one candidate value, proceed to the following processing
				// We have exactly one match.
                //To get the only candidate
				Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
				//Use candidate's properties
                //beanName is key
                //The dependent object is the candidate's value
                autowiredBeanName = entry.getKey();
				instanceCandidate = entry.getValue();
			}
			//Add to collection
			if (autowiredBeanNames != null) {
				autowiredBeanNames.add(autowiredBeanName);
			}
			if (instanceCandidate instanceof Class) {
				instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
			}
            //At this time, let result be the candidate
			Object result = instanceCandidate;
            //Then judge whether it is a NullBean type 
			if (result instanceof NullBean) {
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				result = null;
			}
			if (!ClassUtils.isAssignableValue(type, result)) {
				throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
			}
            //Return results
			return result;
		}
		finally {
			ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
		}
	}

It's so complicated. I'm going to vomit......

Summarize the general process

  • Get the Class type by parsing the parameters obtained from the set method

  • Use this Class type to determine whether it is identified by the @ Value annotation

    • If so, take another logic
    • If not, the representative still needs to inject
      • Judge whether it is a collection type. If it is a collection type, go through the processing of collection type
      • If it is not a collection type, get all candidates
      • Judge the number of candidates
        • If there are more than two candidates
          • Judging which candidate to use is another way if you can't decide
          • If the decision is made, the candidate depends on the Bean instance
        • If there is only one candidate, use the candidate directly
      • Then do some space judgment on the generated candidates
  • Return results

The general idea is like this.... It's too difficult to study it carefully next time

After that, we have completed the acquisition of all injected properties, but the obtained properties exist in the form of PropertyValue and have not been applied to the instantiated bean

Injection properties

Back to the populate method, now all dependencies have been created and waiting to be injected into the bean. The corresponding execution method is the applyPropertyValues method, which is also the last execution method of the populateBean

The source code is as follows

protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
    //If the dependency is empty, return directly
   if (pvs.isEmpty()) {
      return;
   }

   if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
      ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
   }

   MutablePropertyValues mpvs = null;
   List<PropertyValue> original;


   if (pvs instanceof MutablePropertyValues) {
      mpvs = (MutablePropertyValues) pvs;
           //Judge whether the dependency has been converted to the corresponding type
      if (mpvs.isConverted()) {
         // Shortcut: use the pre-converted values as-is.
         try {
             //If it is converted to the corresponding type, it is directly loaded into BeanWrapper
            bw.setPropertyValues(mpvs);
             //End return
            return;
         }
         catch (BeansException ex) {
            throw new BeanCreationException(
                  mbd.getResourceDescription(), beanName, "Error setting property values", ex);
         }
      }
      original = mpvs.getPropertyValueList();
   }
   else {
       //Put dependencies in original
      original = Arrays.asList(pvs.getPropertyValues());
   }

    //
   TypeConverter converter = getCustomTypeConverter();
   if (converter == null) {
      converter = bw;
   }
    //Get the corresponding parser
   BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);

   // Create a deep copy, resolving any references for values.
    //Use an ArrayList to store dependent deep copy objects
   List<PropertyValue> deepCopy = new ArrayList<>(original.size());
   boolean resolveNecessary = false;
    //Traverse all dependencies
   for (PropertyValue pv : original) {
       //Judge whether the dependency has been converted
      if (pv.isConverted()) {
          //If it has been converted, it will be directly added to the collection
         deepCopy.add(pv);
      }
      else {
          //Get dependent name, variable name
         String propertyName = pv.getName();
          //Get the dependent value, which is already an object
         Object originalValue = pv.getValue();
          //If the value is AutowiredPropertyMarker
          //I'm not sure what's going on
         if (originalValue == AutowiredPropertyMarker.INSTANCE) {
            Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod();
            if (writeMethod == null) {
               throw new IllegalArgumentException("Autowire marker for property without write method: " + pv);
            }
            originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true);
         }
          //Parsing using a parser
         Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
          //Put the parsing result in the convertedValue variable
         Object convertedValue = resolvedValue;
         boolean convertible = bw.isWritableProperty(propertyName) &&
               !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
         if (convertible) {
             //Transform
            convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
         }
         // Possibly store converted value in merged bean definition,
         // in order to avoid re-conversion for every created bean instance.
         if (resolvedValue == originalValue) {
            if (convertible) {
               pv.setConvertedValue(convertedValue);
            }
             //Add to deep copy collection
            deepCopy.add(pv);
         }
         else if (convertible && originalValue instanceof TypedStringValue &&
               !((TypedStringValue) originalValue).isDynamic() &&
               !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
            pv.setConvertedValue(convertedValue);
            deepCopy.add(pv);
         }
         else {
            resolveNecessary = true;
            deepCopy.add(new PropertyValue(pv, convertedValue));
         }
      }
   }
   if (mpvs != null && !resolveNecessary) {
      mpvs.setConverted();
   }

   // Set our (possibly massaged) deep copy.
   try {
       //Finally, save all the dependencies of the deep copy into the beanWrapper
      bw.setPropertyValues(new MutablePropertyValues(deepCopy));
   }
   catch (BeansException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Error setting property values", ex);
   }
}

The injection attribute is also difficult to understand. The general process is

  • Judge whether all dependencies have been transformed
    • If they are all converted, they can be directly added to beanWrapper
    • If the conversion is not good, it is necessary to make a deep copy of the dependency
      • Get the dependent variable name and dependent value. The value is usually a Spring customized RuntimeBeanReference
      • Transform using parser
      • Add the converted value to the deepCopy set, which is an ArrayList
  • Finally, save the deepCopy collection into beanWrapper

Therefore, the focus is on parsing and saving the parsed dependencies into beanWrapper. Let's see how to parse them

valueResolver.resolveValueIfNecessary(pv, originalValue);

This method is used for parsing

The source code is as follows

	@Nullable
	public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
		// We must check each value to see whether it requires a runtime reference
		// to another bean to be resolved.
        //Determine whether it is a RuntimeBeanReference
		if (value instanceof RuntimeBeanReference) {
			RuntimeBeanReference ref = (RuntimeBeanReference) value;
			return resolveReference(argName, ref);
		}
        //Determine whether it is a RuntimeBeanNameReference
		else if (value instanceof RuntimeBeanNameReference) {
			String refName = ((RuntimeBeanNameReference) value).getBeanName();
			refName = String.valueOf(doEvaluate(refName));
			if (!this.beanFactory.containsBean(refName)) {
				throw new BeanDefinitionStoreException(
						"Invalid bean name '" + refName + "' in bean reference for " + argName);
			}
			return refName;
		}
        //Judge whether it is BeanDefinitinHolder
		else if (value instanceof BeanDefinitionHolder) {
			// Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.
			BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
			return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
		}
        //Judge whether it is BeanDefinition
		else if (value instanceof BeanDefinition) {
			// Resolve plain BeanDefinition, without contained name: use dummy name.
			BeanDefinition bd = (BeanDefinition) value;
			String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR +
					ObjectUtils.getIdentityHexString(bd);
			return resolveInnerBean(argName, innerBeanName, bd);
		}
        //Determine whether it is a DependencyDescriptor
		else if (value instanceof DependencyDescriptor) {
			Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
			Object result = this.beanFactory.resolveDependency(
					(DependencyDescriptor) value, this.beanName, autowiredBeanNames, this.typeConverter);
			for (String autowiredBeanName : autowiredBeanNames) {
				if (this.beanFactory.containsBean(autowiredBeanName)) {
					this.beanFactory.registerDependentBean(autowiredBeanName, this.beanName);
				}
			}
			return result;
		}
        //Determine whether it is ManagedArray
		else if (value instanceof ManagedArray) {
			// May need to resolve contained runtime references.
			ManagedArray array = (ManagedArray) value;
			Class<?> elementType = array.resolvedElementType;
			if (elementType == null) {
				String elementTypeName = array.getElementTypeName();
				if (StringUtils.hasText(elementTypeName)) {
					try {
						elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());
						array.resolvedElementType = elementType;
					}
					catch (Throwable ex) {
						// Improve the message by showing the context.
						throw new BeanCreationException(
								this.beanDefinition.getResourceDescription(), this.beanName,
								"Error resolving array type for " + argName, ex);
					}
				}
				else {
					elementType = Object.class;
				}
			}
			return resolveManagedArray(argName, (List<?>) value, elementType);
		}
        //Determine whether it is ManagedList
		else if (value instanceof ManagedList) {
			// May need to resolve contained runtime references.
			return resolveManagedList(argName, (List<?>) value);
		}
        //Judge whether it is ManagedSet
		else if (value instanceof ManagedSet) {
			// May need to resolve contained runtime references.
			return resolveManagedSet(argName, (Set<?>) value);
		}
        //Judge whether it is ManagedMap
		else if (value instanceof ManagedMap) {
			// May need to resolve contained runtime references.
			return resolveManagedMap(argName, (Map<?, ?>) value);
		}
        //Determine whether it is ManagedProperties
		else if (value instanceof ManagedProperties) {
			Properties original = (Properties) value;
			Properties copy = new Properties();
			original.forEach((propKey, propValue) -> {
				if (propKey instanceof TypedStringValue) {
					propKey = evaluate((TypedStringValue) propKey);
				}
				if (propValue instanceof TypedStringValue) {
					propValue = evaluate((TypedStringValue) propValue);
				}
				if (propKey == null || propValue == null) {
					throw new BeanCreationException(
							this.beanDefinition.getResourceDescription(), this.beanName,
							"Error converting Properties key/value pair for " + argName + ": resolved to null");
				}
				copy.put(propKey, propValue);
			});
			return copy;
		}
        //Determine whether it is TypeStringValue
		else if (value instanceof TypedStringValue) {
			// Convert value to target type here.
			TypedStringValue typedStringValue = (TypedStringValue) value;
			Object valueObject = evaluate(typedStringValue);
			try {
				Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
				if (resolvedTargetType != null) {
					return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
				}
				else {
					return valueObject;
				}
			}
			catch (Throwable ex) {
				// Improve the message by showing the context.
				throw new BeanCreationException(
						this.beanDefinition.getResourceDescription(), this.beanName,
						"Error converting typed String value for " + argName, ex);
			}
		}
        //Determine whether it is a NullBean
		else if (value instanceof NullBean) {
			return null;
		}
        //If none of the above types
		else {
			return evaluate(value);
		}
	}

As you can see, the injected attribute is distinguished according to the type of the value passed in

  • Of type RuntimeBeanReference
  • Of type RuntimeBeanNameReference
  • Of type BeanDefinitionHolder
  • Of type BeanDefiniton
  • Of type DependencyDescriptor
  • Of type ManagedArray
  • Amount of ManagedList type
  • Of type ManagedSet
  • ManagedMap type
  • Of type ManagedProperty
  • Of NullBean type
  • Other types. Other types here are already the corresponding class types

Generally, an already corresponding type is passed in

That will directly correspond to the final evaluate method

The source code is as follows

    @Nullable
    protected Object evaluate(@Nullable Object value) {
        //Processing strings
        //The main processing is to parse the string into a Bean object
        if (value instanceof String) {
            return this.doEvaluate((String)value);
        }
        //Processing string arrays
        else if (value instanceof String[]) {
            String[] values = (String[])((String[])value);
            boolean actuallyResolved = false;
            Object[] resolvedValues = new Object[values.length];
			//In fact, it is to traverse the string array and process each string
            for(int i = 0; i < values.length; ++i) {
                String originalValue = values[i];
                Object resolvedValue = this.doEvaluate(originalValue);
                if (resolvedValue != originalValue) {
                    actuallyResolved = true;
                }

                resolvedValues[i] = resolvedValue;
            }

            return actuallyResolved ? resolvedValues : values;
        }
        //It is neither a string nor a string array. It directly returns value, that is, the created object
        //It usually returns directly here
        else {
            return value;
        }
    }

You can see that this method does special processing for String and String array, but String type and String array type are impossible here... Because the front has been filtered out

bw.setPropertyValues

This method is to save the dependency into beanWrapper

You can see that all the dependencies to be injected are stored in deepCopy

After entering this method, you can see that this method is implemented by AbstractPropertyAccessor, and the overloaded method is called

The source code is as follows

@Override
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
      throws BeansException {
	//Storage exception
   List<PropertyAccessException> propertyAccessExceptions = null;
    //Turn the passed dependency into a List
   List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
         ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
	//This parameter is false, regardless of the following steps
   if (ignoreUnknown) {
      this.suppressNotWritablePropertyException = true;
   }
   try {
       //Traverse all dependencies passed in
      for (PropertyValue pv : propertyValues) {
         // setPropertyValue may throw any BeansException, which won't be caught
         // here, if there is a critical failure such as no matching field.
         // We can attempt to deal only with less serious exceptions.
         try {
             //Inject
            setPropertyValue(pv);
         }
          //The following are all exception handling. I don't want to see it
         catch (NotWritablePropertyException ex) {
            if (!ignoreUnknown) {
               throw ex;
            }
            // Otherwise, just ignore it and continue...
         }
         catch (NullValueInNestedPathException ex) {
            if (!ignoreInvalid) {
               throw ex;
            }
            // Otherwise, just ignore it and continue...
         }
          //Statistics PropertyAccessException exception
         catch (PropertyAccessException ex) {
            if (propertyAccessExceptions == null) {
               propertyAccessExceptions = new ArrayList<>();
            }
            propertyAccessExceptions.add(ex);
         }
      }
   }
   finally {
      if (ignoreUnknown) {
         this.suppressNotWritablePropertyException = false;
      }
   }

    //Here is the statistics of PropertyAccessException
    //Finally, all are thrown
   // If we encountered individual exceptions, throw the composite exception.
   if (propertyAccessExceptions != null) {
      PropertyAccessException[] paeArray = propertyAccessExceptions.toArray(new PropertyAccessException[0]);
      throw new PropertyBatchUpdateException(paeArray);
   }
}

It can be seen that this method is to traverse all dependencies, and then execute setProperty method for each dependency. The key is setProperty method

After clicking in, you will find that IDEA will jump here


But in fact, this method is rewritten by AbstractNestablePropertyAccessor!!! So it's not the logic here. It won't come in here

So we should click here to continue breakpoint debugging

The source code of this method is as follows

	@Override
	public void setPropertyValue(PropertyValue pv) throws BeansException {
		PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
        //Judge whether there is a token. Ordinary injections will not come here
		if (tokens == null) {
			String propertyName = pv.getName();
			AbstractNestablePropertyAccessor nestedPa;
			try {
				nestedPa = getPropertyAccessorForPropertyPath(propertyName);
			}
			catch (NotReadablePropertyException ex) {
				throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
						"Nested property in path '" + propertyName + "' does not exist", ex);
			}
			tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
			if (nestedPa == this) {
				pv.getOriginalPropertyValue().resolvedTokens = tokens;
			}
			nestedPa.setPropertyValue(tokens, pv);
		}
		else {
            //Usually I come in here
			setPropertyValue(tokens, pv);
		}
	}

After coming in, there is another overloaded method

    protected void setPropertyValue(AbstractNestablePropertyAccessor.PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
        //The tokens.key is usually 0
        if (tokens.keys != null) {
            this.processKeyedProperty(tokens, pv);
        } else {
            //So it's usually this way
            this.processLocalProperty(tokens, pv);
        }

    }
processLocalProperty

The source code of the method is as follows

private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
    	//Get the injection dependency information according to the actualName of tokens
    	//For example, the previous beanWrapper and some Class information
    	//It can be understood that ph is the bean to be injected into
		PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
		if (ph == null || !ph.isWritable()) {
			if (pv.isOptional()) {
				if (logger.isDebugEnabled()) {
					logger.debug("Ignoring optional value for property '" + tokens.actualName +
							"' - property not found on bean class [" + getRootClass().getName() + "]");
				}
				return;
			}
			if (this.suppressNotWritablePropertyException) {
				// Optimization for common ignoreUnknown=true scenario since the
				// exception would be caught and swallowed higher up anyway...
				return;
			}
			throw createNotWritablePropertyException(tokens.canonicalName);
		}
    

		Object oldValue = null;
		try {
            //Get the dependent value, which is an object at this time
			Object originalValue = pv.getValue();
			Object valueToApply = originalValue;
			if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
				if (pv.isConverted()) {
					valueToApply = pv.getConvertedValue();
				}
				else {
					if (isExtractOldValueForEditor() && ph.isReadable()) {
						try {
							oldValue = ph.getValue();
						}
						catch (Exception ex) {
							if (ex instanceof PrivilegedActionException) {
								ex = ((PrivilegedActionException) ex).getException();
							}
							if (logger.isDebugEnabled()) {
								logger.debug("Could not read previous value of property '" +
										this.nestedPath + tokens.canonicalName + "'", ex);
							}
						}
					}
					valueToApply = convertForProperty(
							tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
				}
				pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
			}
            //Inject dependencies into bean s
			ph.setValue(valueToApply);
		}
		catch (TypeMismatchException ex) {
			throw ex;
		}
		catch (InvocationTargetException ex) {
			PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(
					getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
			if (ex.getTargetException() instanceof ClassCastException) {
				throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException());
			}
			else {
				Throwable cause = ex.getTargetException();
				if (cause instanceof UndeclaredThrowableException) {
					// May happen e.g. with Groovy-generated methods
					cause = cause.getCause();
				}
				throw new MethodInvocationException(propertyChangeEvent, cause);
			}
		}
		catch (Exception ex) {
			PropertyChangeEvent pce = new PropertyChangeEvent(
					getRootInstance(), this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
			throw new MethodInvocationException(pce, ex);
		}
	}
ph.setValue

Finally, the injection step is completed by BeanWrapperImpl


The source code is as follows

		public void setValue(@Nullable Object value) throws Exception {
            //Gets the Method object of the Set Method
			Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
					((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
					this.pd.getWriteMethod());
            //System security manager
			if (System.getSecurityManager() != null) {
				AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
					ReflectionUtils.makeAccessible(writeMethod);
					return null;
				});
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>)
							() -> writeMethod.invoke(getWrappedInstance(), value), acc);
				}
				catch (PrivilegedActionException ex) {
					throw ex.getException();
				}
			}
			else {
                //Use the reflection tool to make the set method object accessible
				ReflectionUtils.makeAccessible(writeMethod);
                //getWrappedInstance obtains the bean to be dependency injected, and value is the dependency to be injected
                //Execute the set method of the bean to be dependency injected to complete dependency injection
				writeMethod.invoke(getWrappedInstance(), value);
			}
		}
	}


After executing the invoke method, you can see that twoPojo has successfully injected

Therefore, it can be understood that the essence of the injection attribute is that BeanWrapperImpl uses the created dependency and Set method reflection to inject

Attribute injection is now complete

However, at this time, only the attributes are injected into the beanWrapper. The Bean has basically been created, but the overall Bean creation process is still in the doCreateBean method of AbstractAutowireCapableBeanFactory. Only by completing this method can the whole Bean be created. There are still some things in the life cycle, such as the init method


The next step is to execute the initializeBean method

initializeBean method

Although the method is called initializeBean, in fact, the whole Bean has been basically completed. Before studying the Bean configuration file parsing, it encountered a init-method attribute. The function of this attribute is to call the init-method specified method after bean instantiation to complete the action before instantiation. The function of this attribute lies in the step of initializing the bean

The corresponding source code is as follows

	protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
        //Get the security manager of the system, its zu'yong'shi
        //When running an unknown Java program, the program may have malicious code (deleting system files, restarting the system, etc.),
        //In order to prevent the running of malicious code from affecting the system			
        //You need to control the permissions of the running code. At this time, you need to enable the Java security manager.
        //However, the key here is to execute the invokeAwareMethods method
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
            //Execute the invokeAwareMethods method
			invokeAwareMethods(beanName, bean);
		}
		//
		Object wrappedBean = bean;
		if (mbd == null || !mbd.isSynthetic()) {
            //Pre initialization of post application processor
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		try {
            //Execute init method
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		if (mbd == null || !mbd.isSynthetic()) {
            //Call the post initialization of the post processor
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

The overall process is clear

  • Execute the invokeAwareMethods method
  • Call the post processor's processing logic before initialization
  • Execute the invokeinitialmethods method
  • Call the post processor's processing logic after initialization

The logic of the post processor is set aside here

invokeAwareMethods method

People who have used Spring know that beans are not Aware of the IOC container and cannot obtain the services of the IOC container. If you want beans to obtain IOC services, you must make beans Aware of the existence of the IOC container. Therefore, there will be a series of Aware interfaces corresponding to different IOC services. As long as beans implement this interface, they can obtain the corresponding IOC services, I won't go into detail about Aware interface

The source code is as follows

	private void invokeAwareMethods(String beanName, Object bean) {
        //Judge whether it is Aware interface
		if (bean instanceof Aware) {
            //Determine whether it is the BeanNameAware interface inside
            //This interface allows the bean to get its name in the IOC container
			if (bean instanceof BeanNameAware) {
                //Inject your own BeanName into the bean
				((BeanNameAware) bean).setBeanName(beanName);
			}
            //Determine whether it is the BeanClassLoaderAware interface
            //This interface allows the bean to obtain the class loader service of IOC
			if (bean instanceof BeanClassLoaderAware) {
				ClassLoader bcl = getBeanClassLoader();
				if (bcl != null) {
                    //Inject BeanClassLoader into Bean
					((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
				}
			}
            //Determine whether it is BeanFactoryAware interface
            //This interface can obtain the BeanFactory service of IOC
			if (bean instanceof BeanFactoryAware) {
                //AbstractAutowireCapableBeanFactory.this in this step can be regarded as a this pointer
                //Inject Bean to create its own Factory
				((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
			}
		}
	}

It can be seen that this method simply enumerates all Aware interfaces, forcibly converts them into interface types, and then injects corresponding services. From here, we can see that there are actually three corresponding Aware interfaces

  • BeanNameAware
  • BeanClassLoaderAware
  • BeanFactoryAware

After waking up the IOC service for the Bean, the next step is to activate the method init method

invokeAwareMethods method

This method is used to activate the method init method

The source code is as follows

	protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {
		//Determine whether InitializingBean is of type
        //This is done only when the bean implements the InitializingBean interface
		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            //If so, you need to call the afterpropertieset method
            //The security processor of the system is also ignored here, except that the afterpropertieset method will be called here
			if (logger.isTraceEnabled()) {
				logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						((InitializingBean) bean).afterPropertiesSet();
						return null;
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
                //Call the afterPropertySet method
                //And you can see that it is called by the bean itself
				((InitializingBean) bean).afterPropertiesSet();
			}
		}
        
		//Determine whether it is NullBean.Class
		if (mbd != null && bean.getClass() != NullBean.class) {
            //Gets the method name specified by method init
			String initMethodName = mbd.getInitMethodName();
            //The afterPropertiesSet method is also filtered here
			if (StringUtils.hasLength(initMethodName) &&
					!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
					!mbd.isExternallyManagedInitMethod(initMethodName)) {
                //Execution method
				invokeCustomInitMethod(beanName, bean, mbd);
			}
		}
	}

I also saw a knowledge left by Spring, the InitializingBean interface

  • Determine whether the Bean implements the InitializingBean interface
    • If implemented, the afterpropertieset method in the finite execution interface
  • Go to RootBeanDefinitin to find the method init attribute
    • Find the corresponding method and filter out the afterpropertieset method. Of course, the premise of this filtering is that the Bean implements the InitializingBean interface
    • Call the invokeCustomInitMethod method to implement

invokeCustomInitMethod method

The source code is as follows

	protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition mbd)
			throws Throwable {
		
        //Get method init attribute
		String initMethodName = mbd.getInitMethodName();
        //Assert
		Assert.state(initMethodName != null, "No init method set");
        //Use reflection to get the Method object of the Method
		Method initMethod = (mbd.isNonPublicAccessAllowed() ?
				BeanUtils.findMethod(bean.getClass(), initMethodName) :
				ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));
		//If the obtained Method object is empty
		if (initMethod == null) {
            //Judge whether enforcement is set in RootBeanDefinition
			if (mbd.isEnforceInitMethod()) {
                //If it is empty and enforced, an error will be reported
				throw new BeanDefinitionValidationException("Could not find an init method named '" +
						initMethodName + "' on bean with name '" + beanName + "'");
			}
			else {
                //If not enforced, ignore and return directly
				if (logger.isTraceEnabled()) {
					logger.trace("No default init method named '" + initMethodName +
							"' found on bean with name '" + beanName + "'");
				}
				// Ignore non-existent default lifecycle methods.
				return;
			}
		}
		//If it is not empty, the following processing will be performed
		if (logger.isTraceEnabled()) {
			logger.trace("Invoking init method  '" + initMethodName + "' on bean with name '" + beanName + "'");
		}
        //I don't quite understand this method. The comment says that I should try to get the interface method
		Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod);

        //Security manager, don't think about it first
		if (System.getSecurityManager() != null) {
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				ReflectionUtils.makeAccessible(methodToInvoke);
				return null;
			});
			try {
				AccessController.doPrivileged((PrivilegedExceptionAction<Object>)
						() -> methodToInvoke.invoke(bean), getAccessControlContext());
			}
			catch (PrivilegedActionException pae) {
				InvocationTargetException ex = (InvocationTargetException) pae.getException();
				throw ex.getTargetException();
			}
		}
		else {
			try {
                //This method can be called through the reflection tool
				ReflectionUtils.makeAccessible(methodToInvoke);
                //Execute this method through reflection
				methodToInvoke.invoke(bean);
			}
			catch (InvocationTargetException ex) {
				throw ex.getTargetException();
			}
		}
	}

The general logic here is also very simple

  • Judge whether the method init attribute is empty
    • If it is empty, the assertion will throw an error
    • If it is not empty, get the Method object of the Method through reflection
      • If the Method object is empty, judge whether the configuration file is set to enforce. If enforced, an error will be thrown
      • If the Method object is empty and the configuration file does not set enforcement, it is ignored directly
  • Execute the Method by reflection


So far, the object has completed property injection and initialization

Posted by psr540 on Sun, 24 Oct 2021 01:14:36 -0700