Spring Framework 5.3.10 - Bean Life Cycle

Keywords: Java Spring

Bean's life cycle process

Bean Generation Process

1.Generate BeanDefinition

When Spring starts, it scans and calls first
Org.springframework.context.annotation.ClassPathScanning Candidate Component Provider#findCandidate Components (String base Package) scans a package path and obtains a Set of Sets for BeanDefinition
Spring scan underlying process: https://www.processon.com/view/link/61370ee60e3e7412ecd95d43
Source Parsing: https://app.mockplus.cn/s/xb5V_uRM8ROu

  1. First, use ResourcePatternResolver to get all.class files under the specified package path (the Spring source will
    This file is wrapped as a Resource object)
  2. Traverse through each Resource object
  3. Resource object is parsed using MetadataReaderFactory to get MetadataReader (in Spring source code)
    The MetadataReaderFactory implementation class is CachingMetadataReaderFactory.
    The specific implementation class for MetadataReader is SimpleMetadataReader)
  4. Screening excludeFilters and includeFilters with MetadataReader and conditional comment @Conditional
    (Conditional annotations do not understand whether the @Conditional annotation exists on a class and, if so, calls the one specified in the annotation
    The matching method of the class is matched, the matching success is filtered, and the matching failure is pass removed.)
  5. After filtering is passed, ScannedGenericBeanDefinition is generated based on metadataReader
  6. Then, based on metadataReader, determine if the corresponding class is an interface or an abstract class
  7. If the filter passes, then a Bean is scanned and ScannedGenericBeanDefinition is added to the result set

MetadataReader Represents a metadata reader for classes. It mainly contains an AnnotationMetadata with functions such as

  1. Get the name of the class,
  2. Get the name of the parent class
  3. Get all interface names implemented
  4. Get the names of all internal classes
  5. Determine whether or not an abstract class is present
  6. Determine if it is an interface
  7. Determine if it's a comment
  8. Gets the collection of methods that have a comment
  9. Get all the annotation information added to the class
  10. Gets the collection of all annotation types added to the class

It is worth noting that CachingMetadataReaderFactory parses a.class file and finds that the MetadataReader object is
Using ASM technology, this class is not loaded into the JVM. Furthermore, the resulting ScannedGenericBeanDefinition pair
Like, the beanClass property stores the name of the current class, not the class object. (The type of the beanClass property is Object,
It can store either the name of a class or a class object)
Finally, the BeanDefinition object is scanned, and we can also define BeanDefinition directly, or
Parse the spring.xml file or @Bean annotation to get the BeanDefinition object.

2.Merge BeanDefinition

Once all BeanDefinitions have been scanned, Bean objects can be created from BeanDefinition, but
Parent-child BeanDefinition is supported in Spring, similar to Java parent-child classes, but not at all.
Parent and child BeanDefinition are actually used less, such as:

<bean id="parent" class="com.zhouyu.service.Parent" scope="prototype"/>
<bean id="child" class="com.zhouyu.service.Child"/>

By this definition, child is a single Bean.

<bean id="parent" class="com.zhouyu.service.Parent" scope="prototype"/>
<bean id="child" class="com.zhouyu.service.Child" parent="parent"/>

But by definition, child is the prototype Bean.
Because the child's parent BeanDefinition is parent, the scope property defined on the parent is inherited.
Before you can generate Bean objects from a child, you need to merge BeanDefinition to get a complete child's
BeanDefinition.

3.Load Class

After the BeanDefinition is merged, you can create Bean objects, which must be instantiated, and instantiation must first load the class corresponding to the current BeanDefinition, in the AbstractAutowireCapableBeanFactory class
In the createBean() method, it is called at the beginning:

Class<?> resolvedClass = resolveBeanClass(mbd, beanName);

This line of code loads the class, which is how this method works:

if (mbd.hasBeanClass()) {
	return mbd.getBeanClass();
}
if (System.getSecurityManager() != null) {
	return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>) () ‐>
		doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
}
else {
	return doResolveBeanClass(mbd, typesToMatch);
}
public boolean hasBeanClass() {
	return (this.beanClass instanceof Class);
}

If the type of the beanClass attribute is Class, it is returned directly, if not, it is loaded according to the class name
(What the doResolveBeanClass method does)
Classes are loaded using the class loader set by BeanFactory and, if not, by default
Class loader returned by ClassUtils.getDefaultClassLoader() to load.

ClassUtils.getDefaultClassLoader()

  1. Priority return to ClassLoader in current thread
  2. Returns the class loader for the ClassUtils class if the class loader in the thread is null
  3. If the ClassUtils class loader is empty, then it means the ClassUtils class loaded by the Bootstrap class loader is returned to the system class loader

4.Before instantiation

Once the current BeanDefinition corresponding class has been successfully loaded, the object can be instantiated, but...
In Spring, before instantiating an object, Spring provides an extension point that allows users to control whether to do some startup action before instantiating a Bean or some Bean. This extension point is called

InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(). For example:
@Component
public class ZhouyuBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)throws BeansException {
	if ("userService".equals(beanName)) {
		System.out.println("Before instantiation");
		}
		return null;
	}
}

As a result of the above code, the userService Bean will print before it is instantiated.
It is worth noting that postProcessBeforeInstantiation() has a return value if implemented:

@Component
public class ZhouyuBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)throws BeansException {
		if ("userService".equals(beanName)) {
			System.out.println("Before instantiation");
			return new UserService();
		}
		return null;
	}
}

UserServiceThis Bean will return a UserService object that we defined directly before instantiation. If so,
Indicates that Spring is not needed to instantiate, and subsequent Spring dependency injections will not occur, skipping some steps and performing the post-initialization step directly.

5.instantiation

In this step you will create an object based on BeanDefinition

5.1 Supplier Create Object

First, determine if Supplier is set in BeanDefinition, and if so, call get() of Supplier to get the object.
You have to set the Supplier directly using the BeanDefinition object, for example:

AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setInstanceSupplier(new Supplier<Object>() {
	@Override
	public Object get() {
		return new UserService();
	}
});
context.registerBeanDefinition("userService", beanDefinition);

5.2 Factory Method Create Object

If Supplier is not set, check whether factoryMethod, or factory method, is set in BeanDefinition. There are two ways to set factoryMethod, such as:
Mode 1:

<bean id="userService" class="com.zhouyu.service.UserService" factory‐
method="createUserService" />

The corresponding UserService class is:

public class UserService {
	public static UserService createUserService() {
		System.out.println("implement createUserService()");
		UserService userService = new UserService();
		return userService;
	}
	public void test() {
		System.out.println("test");
	}
}

Mode 2:

<bean id="commonService" class="com.zhouyu.service.CommonService"/>
<bean id="userService1" factory‐bean="commonService" factory‐method="createUserService"
/>

The corresponding CommonService classes are:

public class CommonService {
	public UserService createUserService() {
		return new UserService();
	}
}

When Spring finds that the current BeanDefinition method has a factory method set, it distinguishes the two and calls the factory method to get the object.
Notably, the BeanDefinition defined by @Bean is the existence of factoryMethod and factoryBean
The @Bean annotated method is factoryMethod, and the AppConfig object is factoryBean. If @Bean annotated method is static, then the corresponding method is method one.

5.3 Inference construction method

The first section has gone through the general principles, followed by a separate analysis of source code implementations. After inferring the construction method, the construction method is used for instantiation.
Additionally, in the inference construction method logic, in addition to meeting to select a construction method and finding an incoming object that is unexpected, it will also determine whether a method exists in the corresponding class using the **@Lookup annotation**. If so, it will be encapsulated as a LookupOverride object and added to BeanDefinition.
When instantiating, if it is determined that there is no LookupOverride in the current BeanDefinition, an instance object is reflected directly by the construction method. If there is a LookupOverride object, that is, a method annotated with @Lookup in the class, a proxy object is generated.
The @Lookup annotation is method injection, using demo as follows:

@Component
public class UserService {
	private OrderService orderService;
	public void test() {
		OrderService orderService = createOrderService();
		System.out.println(orderService);
	}
	@Lookup("orderService")
	public OrderService createOrderService() {
		return null;
	}
}

6.Post-processing of BeanDefinition

After the Bean object has been instantiated, it is time to assign values to the object's properties. Before assigning values to the properties, Spring provides an extension point, MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition(), which can process BeanDefinition at this time, for example:

@Component
public class ZhouyuMergedBeanDefinitionPostProcessor implements
MergedBeanDefinitionPostProcessor {
	@Override
	public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,Class<?> beanType, String beanName) {
		if ("userService".equals(beanName)) {
			beanDefinition.getPropertyValues().add("orderService", new OrderService());
		}
	}
}

In the Spring source, AutowiredAnnotationBeanPostProcessor is one
MergedBeanDefinitionPostProcessor, whose postProcessMergedBeanDefinition(), looks for injection points and caches them in a Map of the AutowiredAnnotationBeanPostProcessor object (injectionMetadataCache).

7.After instantiation

After processing the BeanDefinition, Spring designed another extension point:
InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(), for example:

@Component
public class ZhouyuInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
	@Override
	public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
		if ("userService".equals(beanName)) {
			UserService userService = (UserService) bean;
			userService.test();
		}
		return true;
	}
}

The above code handles the object instantiated by userService.
This extension point is rarely used in Spring source.

8.Automatic Injection

Auto-injection here refers to Spring's auto-injection

9.Processing Properties

In this step, annotations such as @Autowired, @Resource, @Value are processed, also by
**InstantiationAwareBeanPostProcessor.postProcessProperties()**Extension Points, such as we can even implement our own auto-injection capabilities, such as:

@Component
public class ZhouyuInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
	@Override
	public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
		if ("userService".equals(beanName)) {
			for (Field field : bean.getClass().getFields()) {
				if (field.isAnnotationPresent(ZhouyuInject.class)) {
					field.setAccessible(true);
					try {
						field.set(bean, "123");
					} catch (IllegalAccessException e) {
					e.printStackTrace();
					}
				}
			}
		}
		return pvs;
	}
}

10.Execute Aware

Once the attribute assignment is complete, Spring performs some callbacks, including:

  1. BeanNameAware: Returns the beanName to the bean object.
  2. BeanClassLoaderAware: Returns the classLoader to the bean object.
  3. BeanFactoryAware: Returns the beanFactory to the object.

11.Before initialization

Before initialization, it is also an extension point provided by Spring:
BeanPostProcessor.postProcessBeforeInitialization(), for example

@Component
public class ZhouyuBeanPostProcessor implements BeanPostProcessor {
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if ("userService".equals(beanName)) {
			System.out.println("Before initialization");
		}
		return bean;
	}
}

Bean s with dependent injection can be processed prior to initialization.
In Spring source:

  1. InitDestroyAnnotationBeanPostProcessor will stick to the @PostConstruct method in this step before initialization.
  2. The ApplicationContextAwareProcessor makes callbacks to other Awares in this step before initialization:
    I.EnvironmentAware: Return environment variables
    Ii.EmbeddedValueResolverAware: Return placeholder parser
    iii. ResourceLoaderAware: Return Resource Loader
    iv. ApplicationEventPublisherAware: Return Event Publisher
    V.MessageSourceAware: Return Internationalized Resources
    vi. ApplicationStartupAware: Return application other listeners, ignorable
    Vii.ApplicationContextAware: Return Spring Container ApplicationContext

12.Initialization

  1. Check to see if the current Bean object implements the InitializingBean interface and, if so, call its afterPropertiesSet() method
  2. Executes the initialization method specified in BeanDefinition

13.After initialization

This is the last step in Bean's creation life cycle and an extension point provided by Spring:
BeanPostProcessor.postProcessAfterInitialization(), for example:

@Component
public class ZhouyuBeanPostProcessor implements BeanPostProcessor {
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if ("userService".equals(beanName)) {
			System.out.println("After initialization");
		}
		return bean;
	}
}

In this step, the Bean is processed eventually, and the AOP in Spring is based on initialization, which returns
The object returned is the final Bean object.

Summary of BeanPostProcessor

  1. InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
  2. instantiation
  3. MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()
  4. InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
  5. Automatic Injection
  6. InstantiationAwareBeanPostProcessor.postProcessProperties()
  7. Aware object
  8. BeanPostProcessor.postProcessBeforeInitialization()
  9. Initialization
  10. BeanPostProcessor.postProcessAfterInitialization()

Posted by Dimitri89 on Wed, 22 Sep 2021 10:31:05 -0700