An overview of the

In terms of bean types, Spring divides beans into normal beans and factoryBeans. There are also two ways to declare beans: @Component and @bean. So let’s take a look at the similarities and differences in how these beans are instantiated. Here we call Normal beans and @Component legitimate beans because they are instantiated in a public way, whereas factoryBeans and @Beans are not legitimate beans because they are instantiated in a somewhat niche way. I don’t think there is good or bad, just like people, what you like is good.

You gain

What can you get out of this article?

  • The Normal Bean and @Component generate an entry to the beanDefinition
  • How do the beanDefinition of normal Beans and @Component generate bean instances and put them into the Spring container
  • The @bean generates an entry to the beanDefinition
  • How does the @bean beanDefinition generate a Bean instance and put it into the Spring container, where instance might bea proxy class
  • The FactoryBean generates an entry to the beanDefinition
  • How does the beanDefinition of A FactoryBean generate a bean instance and put it into the Spring container, where instance might bea proxy class
  • The connections and differences between these different types of beans generate bean instance procedures

This article is focused and concise, and I hope you have some insights and ideas

The sample

Below, we list the classes that are commonly used in practice, one for each type of bean, so that we can align our thinking

import com.skyler.project // normal bean @Service public class UserServiceImpl implements IUserService { @Override Public int insert(User User) {}} // @bean declared class @configuration public class MapperAutoConfiguration {@bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) } }Copy the code

Different types of beans generate an entry to the BeanDefinition, as shown in the figure below, which we will call “entry diagrams” for later introduction.

The figure shows the entry for generating BeanDefinitions from.class files of different types of classes, as well as the big-box process and entry for general-purpose beanDefinition generating bean Instance or proxy proxy classes

Normal beans and beans of @Component type

The entrance

See entrance diagram above

Specific BeanDefinition

Normal beans and beans of type @Component generate BeanDefinitions as rootBeanDefinitions. The key attributes at this point are

resolvedTargetType=com.skyler.project.UserServiceImpl resolvedConstructorOrFactoryMethod=com.skyler.project.UserServiceImpl beanClass=com.skyler.project.UserServiceImpl AutowireMode = 0 attributes extenallyManagedConfigMembers = deposit is a member of the bean propertiesCopy the code

At the end of the article, the object attribute comparison diagram of BeanDefinition’s @Component declared bean, @Bean declared bean, and Bean of FactoryBean type is posted

How does beanDefinition generate bean instance

How does beanDefinition generate the bean instance trace

UserServiceImpl:
AbstractBeanFactory.getBean
AbstractBeanFactory.doGetBean
DefaultSingletonBeanRegistry.getSingleton
AbstractAutowireCapableBeanFactory.createBean
AbstractAutowireCapableBeanFactory.doCreateBean
--this.createBeanInstance
----resolveBeanClass
----instantiateBean
AbstractAutowireCapableBeanFactory.populateBean
--AutowiredAnnotationBeanPostProcessor.postProcessProperties forBeanPostProcessor.postProcessProperties ----InjectionMetadata.inject AbstractAutowireCapableBeanFactory.initializeBean // Use AbstractAdvisor to generate a proxy classCopy the code

Bean of type @bean

The entrance

See entrance diagram above

Specific BeanDefinition

At this time for ConfigurationClassBeanDefinition BeanDefinition type. Taking @bean SqlSessionFactory as an example, the key attributes are

factoryMethodMetadata
autowireMode=3
factoryBeanName=tk.mybatis.mapper.autoconfigure.MapperAutoConfiguration
factoryMethodName=sqlSessionFactory
factoryMethodReturnType
Copy the code

At the end of the article, the object attribute comparison diagram of BeanDefinition’s @Component declared bean, @Bean declared bean, and Bean of FactoryBean type is posted

How does beanDefinition generate bean instance

How does beanDefinition generate the bean instance trace

AbstractBeanFactory.getBean
AbstractBeanFactory.doGetBean
DefaultSingletonBeanRegistry.getSingleton
AbstractAutowireCapableBeanFactory.createBean
AbstractAutowireCapableBeanFactory.doCreateBean
--this.createBeanInstance
----this.instantiateUsingFactoryMethod
------ConstructorResolver.instantiateUsingFactoryMethod
--------ConfigurationClassEnhancer$BeanMethdoInterceptor
Copy the code

You can view the debug running trace more clearly

Bean of type @Configuration

The entrance

See entrance diagram above

Specific BeanDefinition

At this time for ConfigurationClassBeanDefinition BeanDefinition type. A bean of type @Configuration generates a BeanDefinition in a special way, as shown in the figure below

How does beanDefinition generate bean instance

AbstractBeanFactory.getBean AbstractBeanFactory.doGetBean DefaultSingletonBeanRegistry.getSingleton AbstractAutowireCapableBeanFactory.createBean AbstractAutowireCapableBeanFactory.doCreateBean -- AbstractAutowireCapableBeanFactory. AutowireConstructor if the constructorCopy the code

You can view the debug running trace more clearly. So let’s take MapperAutoConfiguration as an example

A bean of type FactoryBean

The entrance

There is no entry for FactoryBean’s.class file to generate a beanDefinition. Because a FactoryBean is a special bean. Take a look at its usage and situations. A FactoryBean is first a bean, and second, a factory bean: what does a factory bean do? Producing beans. How to produce it? What are the benefits of producing with the factoryBean.getobject () method? Or what does a FactoryBean mean?

FactoryBean produces products, not FactoryBeans themselves. That is, the object returned by a FactoryBean is not an instance of the specified class. It returns the object returned by the FactoryBean’s getObject method. A FactoryBean is special in that it can register two beans to the container, one itself and the Bean represented by the return value of the factoryBean.getobject () method. Within the Spring framework, there are many implementation classes for FactoryBeans, which are used in many applications (Spring AOP, ORM, transaction management)

So, the FactoryBean interface is an extension point for the instantiation logic of the Spring IoC container. If the initialization code is complex, you can customize a FactoryBean in this scenario, write complex initializers in the class, and add them to the container as plug-ins. Both Spring Cloud openFeign and Mybatis Mapper apply this technique to the actual working code

// Spring Cloud openFeign declares a FeignClient @feignClient (value =)"${provider.application:skyler-base}", contextId = "SkylerComboFeignService")
public interface SkylerComboFeignService {

    @PostMapping(value = "api/skyler/combo/create", produces = MediaType.APPLICATION_JSON_VALUE)
    ResultDTO create(@RequestBody ComboParam param);

    @GetMapping(value = "api/skyler/combo/list") ResultDTO<Page<ComboDTO>> listCombo(Long comboId, Integer currentPage,Integer pageSize); } @repository public interface ComboMapper {int insert(Combo record); List<Combo> selectByExample(ComboExample example); } @Service public class ComboService { @AutoWired ComboMapper comboMapper; }Copy the code

I’m sure you’re familiar with these two pieces of code, both of which are commonly used in development. As soon as we declare/define this, we can use Feign to call the database remotely, and all of this is done by FactoryBean.

So the openFeign entry is at FeignClientsRegistrar; The entry point to Mybatis Mapper is at MapperScannerRegistrar.

Specific BeanDefinition

The BeanDefinition type is RootBeanDefinition. Taking @FeignClient and Mybatis Mapper as an example, the key attributes are as follows

propertyValues=MutablePropertyValues 
resolvedTargetType=org.springframework.cloud.openfeign.FeignClientFactoryBean
resolvedConstructorOrFactoryMethod=org.springframework.cloud.openfeign.FeignClientFactoryBean
beanClass=org.springframework.cloud.openfeign.FeignClientFactoryBean
autowireMode=2
Copy the code

At the end of the article, the object attribute comparison diagram of BeanDefinition’s @Component declared bean, @Bean declared bean, and Bean of FactoryBean type is posted

How does beanDefinition generate bean instance

@feignClient generates the bean instance/ instantiation process as follows

AbstractBeanFactory.getBean AbstractBeanFactory.doGetBean AbstractBeanFactory.getObjectForBeanInstance FactoryBeanRegistrySupport.getObjectFromFactoryBean FactoryBeanRegistrySupport.doGetObjectFromFactoryBean object = factory.getObject(); // Factory is a FactoryBean typeCopy the code

Mybatis Mapper generates bean instance/ instantiation as follows

AbstractBeanFactory.getBean AbstractBeanFactory.doGetBean DefaultSingletonBeanRegistry.getSingleton AbstractAutowireCapableBeanFactory.createBean AbstractAutowireCapableBeanFactory.doCreateBean AbstractAutowireCapableBeanFactory.populateBean AbstractAutowireCapableBeanFactory.autowireByType(beanName, mbd, bw, PVS) / / to PVS assignment and MBD setPropertyValues (PVS) AbstractAutowireCapableBeanFactory. ApplyPropertyValues (beanName, MBD, bw, PVS) // Assigns PVS to bw. PropertyValues // assigns the sqlSession property of userMapper's MapperFactoryBean AbstractAutowireCapableBeanFactory.initializeBean AbstractAutowireCapableBeanFactory.invokeInitMethods // MapperFactoryBean DaoSupport and InitializingBean subtype DaoSupport. AfterPropertiesSet AbstractBeanFactory.getObjectForBeanInstance FactoryBeanRegistrySupport.getObjectFromFactoryBean FactoryBeanRegistrySupport.doGetObjectFromFactoryBean object = factory.getObject(); // Factory is a FactoryBean(MapperFactoryBean) object is a proxy object, Generated as follows other MapperProxy = MapperProxyFactory. NewInstance (sqlSession) / / generated proxy class object MapperProxy userMapper Above is ComboMapper generated proxy class MapperProxy process, and then assigned to ComboService.com boMapper, assignment method is field. The set (target, MapperProxy), field for ComboMapper; Target for ComboServiceCopy the code

Comparison diagram of BeanDefinition object properties of different types

The picture is a little unclear, so I’ll take a screenshot of it on the big screen

it`s time to summary

The purpose of this article is to give you a ladder by sorting out where the different types of beans come in to generate BeanDefinitions. With this ladder, you can quickly locate a given location and track code and filaments in greater detail. To truly understand how technology works, it takes more than reading articles. You have to do it yourself. So, the purpose of this article is to shorten your path to learning technical principles and save valuable time for more important things.

extension

After peeling the skin, we can see how beanDefinition generates bean instance. We can see from the source code that all bean instances end up calling the getObjectForBeanInstance() method. The getObjectForBeanInstance() method determines whether the bean is a FactoryBean, and if not, returns the bean directly. If it is a FactoryBean and name begins with an ampersand, it represents the native object from which the FactoryBean was obtained, and will be returned directly. If name does not begin with an ampersand, it means that you want to get the object returned by the getObject() method in FactoryBean. The code is as follows

AbstractBeanFactory class
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
	final String beanName = transformedBeanName(name);
	Object bean;

	Object sharedInstance = getSingleton(beanName);
	if(sharedInstance ! = null && args == null) { bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); }else {
		if (mbd.isSingleton()) {
			
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
		}
		else if (mbd.isPrototype()) {
			bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
		}
		else{ bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); }}return (T) bean;
}
Copy the code

A picture of a lake relaxes your mind