FactoryBean for Spring instantiation source code analysis (11)

Originally, this chapter needed to explain the instantiation process of singleton beans, but suddenly I discovered that the custom FactoryBean was actually instantiated during registerBeanPostProcessors, and I planned to find out.

The function and usage of FactoryBean have been explained in the article Manually Implementing the Mybatis Proxy Interface Object. This chapter mainly analyzes the instantiation process of FactoryBean.

Preparation conditions

Create a FactoryBean for verification. Due to the manual implementation of the mybatis proxy interface object in this article, I have two custom FactoryBeans, but ValidateFactoryBean is just a singleton Bean.

package com.qhyu.cloud.mybatis.config;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

/**
 * All rights Reserved, Designed By https://umd.edu/<br>
 * Title: ValidateFactoryBean<br>
 * Package: com.qhyu.cloud.mybatis.config<br>
 * Copyright ? 2023 umd.edu. All rights reserved.<br>
 * Company: The University of Maryland<br>
 *
 * @author candidate<br>
 * @date October 12, 2023 15:49<br>
 */
@Component
public class ValidateFactoryBean<T> implements FactoryBean<T> {<!-- -->
@Override
public T getObject() throws Exception {<!-- -->
return null;
}

@Override
public Class<?> getObjectType() {<!-- -->
return null;
}
}

Here I also show the MapperFactoryBean

package com.qhyu.cloud.mybatis.config;

import com.qhyu.cloud.mybatis.handler.MethodArgsHandler;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

import java.lang.reflect.Proxy;


@Component
public class MapperFactoryBean<T> implements FactoryBean<T> {<!-- -->

public MapperFactoryBean() {<!-- -->
}

private Class<T> target;

public MapperFactoryBean(Class<T> target) {<!-- -->
this.target = target;
}

@Override
@SuppressWarnings({<!-- -->"unchecked","rawtypes"})
public T getObject() throws Exception {<!-- -->
// Generate a proxy object here. For specific implementation, see the article Manually Implementing the MyBatis Proxy Interface Object.
return (T) Proxy.newProxyInstance(target.getClassLoader(), new Class[]{<!-- -->getObjectType()}, new MethodArgsHandler());
}

/** Just return a singleton by default */
@Override
public boolean isSingleton() {<!-- -->
return true;
}

@Override
public Class<T> getObjectType() {<!-- -->
// Directly return the type of target
return target;
}
\t
}

Instance analysis

Confirm FactoryBean loading location

In order to confirm that FactoryBean is not instantiated before registerBeanPostProcessors(beanFactory) is called in the refresh method in AbstractApplicationContext, I added a preRegisterBeanPostProcessors method in the source code.

/**
* Title: What beans were in the container before registering BeanPostProcessor
* @author candidate
* @date 2023/10/12 15:12
* @since 2023/10/12
* @param beanFactory ConfigurableListableBeanFactory
*/
private void preRegisterBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {<!-- -->
for (String singletonName : beanFactory.getSingletonNames()) {<!-- -->
System.out.println("preRegisterBeanPostProcessors:" + singletonName);
}
}

The printed content after starting the project is as follows, which is the bean injected into the container before registerBeanPostProcessors(beanFactory) in my project.

Then after registerBeanPostProcessors(beanFactory) is executed, the same logic is followed to see whether FactoryBean will be instantiated. Add a new afterRegisterBeanPostProcessors method.

/**
* Title: To verify whether my guess is correct, I need to know the instantiation time of FactoryBean
* @author candidate
* @date 2023/10/11 15:08
* @since 2023/10/11
* @param beanFactory ConfigurableListableBeanFactory
*/
private void afterRegisterBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {<!-- -->
String[] singletonNames = beanFactory.getSingletonNames();
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {<!-- -->
if(beanFactory.isFactoryBean(beanDefinitionName)){<!-- -->
System.out.println("The current Bean is FactoryBean:" + beanDefinitionName);
 for (String singletonName : singletonNames) {<!-- -->
System.out.println("afterRegisterBeanPostProcessors:" + singletonName);
if (beanDefinitionName.contains(singletonName)){<!-- -->
System.out.println("FactoryBean has been initialized" + singletonName);
}
}
}
}

}

The startup print is as follows. Our FactoryBean is indeed instantiated when registerBeanPostProcessors(beanFactory) is executed.

Find the location of the instantiation

Since it needs to be instantiated, it must be obtained from the singleton pool, and it will be created if there is no cache for this bean (this sentence involves the instantiation of singleton beans, which will be explained in the next chapter). Therefore, add a conditional breakpoint in the doGetBean method of AbstractBeanFactory to reversely push the instantiation of FactoryBean when it is triggered.

Using IDEA to view Threads & Variables, you can see that the getTypeForFactoryBean method of AbstractBeanFactory is executing doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true); which is where our custom FactoryBean is instantiated.

I show the source code below. Since we have a singleton by default, mbd.isSingleton() returns true, and then allowInit becomes the key. We need to move forward based on the above breakpoints to see where this value comes from. .

protected ResolvableType getTypeForFactoryBean(String beanName, RootBeanDefinition mbd, boolean allowInit) {<!-- -->
ResolvableType result = getTypeForFactoryBeanFromAttributes(mbd);
if (result != ResolvableType.NONE) {<!-- -->
return result;
}

if (allowInit & amp; & amp; mbd.isSingleton()) {<!-- -->
try {<!-- -->
System.out.println("AbstractBeanFactory-CG1719-Get FactoryBean:" + FACTORY_BEAN_PREFIX + beanName);
FactoryBean<?> factoryBean = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true);
Class<?> objectType = getTypeForFactoryBean(factoryBean);
return (objectType != null ? ResolvableType.forClass(objectType) : ResolvableType.NONE);
}
catch (BeanCreationException ex) {<!-- -->
if (ex.contains(BeanCurrentlyInCreationException.class)) {<!-- -->
logger.trace(LogMessage.format("Bean currently in creation on FactoryBean type check: %s", ex));
}
else if (mbd.isLazyInit()) {<!-- -->
logger.trace(LogMessage.format("Bean creation exception on lazy FactoryBean type check: %s", ex));
}
else {<!-- -->
logger.debug(LogMessage.format("Bean creation exception on eager FactoryBean type check: %s", ex));
}
onSuppressedException(ex);
}
}
return ResolvableType.NONE;
}

Then I found that descriptor.isEager() was passed in the findAutowireCandidates method of DefaultListableBeanFactory.

So the value passed is related to the DependencyDescriptor object.

protected Map<String, Object> findAutowireCandidates(
@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {<!-- -->
System.out.println("Current BeanName=" + beanName + " isEager(whether eager)=" + descriptor.isEager() + " required type=" + requiredType.getName());
// True is passed in here, which is what is passed in the descriptor information.
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this, requiredType, true, descriptor.isEager());
Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);
for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {<!-- -->
Class<?> autowiringType = classObjectEntry.getKey();
if (autowiringType.isAssignableFrom(requiredType)) {<!-- -->
Object autowiringValue = classObjectEntry.getValue();
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
if (requiredType.isInstance(autowiringValue)) {<!-- -->
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
}
}
}
//.......omit part of the code

Go back and see the return this.beanFactory.resolveDependency(new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter) in the resolveAutowiredArgument method of ConstructorResolver, the DependencyDescriptor object created here.

DependencyDescriptor

Eager defaults to true when the object is created.

public DependencyDescriptor(MethodParameter methodParameter, boolean required) {<!-- -->
this(methodParameter, required, true);
}

Cleaning

When the implementation class of BeanPostProcessor is instantiated, all BeanDefinitions in the current factory will be traversed. If the eager attribute of a BeanPostProcessor’s DependencyDescriptor object is true, it means that the Bean’s dependencies are eagerly loaded. In this case, Spring will instantiate the corresponding FactoryBean in advance. So even if ValidateFactoryBean does nothing, it is equivalent to an ordinary singleton Bean. As long as it is of FactoryBean type, it will be instantiated in advance.