SpringIoC Bean life cycle source code analysis (Part 1)

Bean generation process

Insert image description here

GenerateBeanDefinition

Spring will scan when it starts. It will first call org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents(String basePackage)
to scan a package path and get the Set collection of BeanDefinition.

Spring scan underlying process:
Insert image description here

  1. First, obtain all .class files under the specified package path through ResourcePatternResolver (this file is packaged into a Resource object in the Spring source code)
  2. Traverse each Resource object
  3. Use MetadataReaderFactory to parse the Resource object to obtain MetadataReader (in the Spring source code, the specific implementation class of MetadataReaderFactory is CachingMetadataReaderFactory, and the specific implementation class of MetadataReader is SimpleMetadataReader)
  4. Use MetadataReader to filter excludeFilters and includeFilters, as well as the conditional annotation @Conditional (conditional annotations cannot understand: whether there is a @Conditional annotation on a certain class. If it exists, call the match method of the class specified in the annotation to match. If the match is successful, Pass the filter, and pass if the match fails.)
  5. After passing the screening, ScannedGenericBeanDefinition is generated based on metadataReader
  6. Then based on metadataReader, determine whether the corresponding class is an interface or abstract class.
  7. If the filter passes, it means that a Bean has been scanned, and ScannedGenericBeanDefinition is added to the result set.

MetadataReader represents the metadata reader of the class, which mainly contains an AnnotationMetadata with the following functions:

  • Get the name of the class
  • Get the name of the parent class
  • Get all implemented interface names
  • Get the names of all inner classes
  • Determine whether it is an abstract class
  • Determine whether it is an interface
  • Determine whether it is an annotation
  • Get the collection of methods with a certain annotation
  • Get all annotation information added to the class
  • Get the collection of all annotation types added to the class

CachingMetadataReaderFactory parses a .class file to obtain the MetadataReader object using ASM technology and does not load this class into the JVM. Moreover, in the resulting ScannedGenericBeanDefinition object, the beanClass attribute stores the name of the current class, not the class object . (The type of the beanClass attribute is Object, which can store both the name of the class and the class object)

In addition to obtaining the BeanDefinition object through scanning, you can also obtain the BeanDefinition object by directly defining the BeanDefinition, parsing the <bean/> of the spring.xml file, or the @Bean annotation.

MergeBeanDefinition

After all BeanDefinitions are obtained through scanning, Bean objects can be created based on BeanDefinitions. However, Spring supports parent-child BeanDefinitions, which are similar to Java parent-child classes, but are completely different.

The actual use of parent-child BeanDefinition is relatively rare. The usage is as follows, for example

// 这么定义的情况下,child是单例Bean
<bean id="parent" class="com.tacy.service.Parent" scope="prototype"/>
<bean id="child" class="com.tacy.service.Child"/>
// 这么定义的情况下,child就是原型Bean
//因为child的父BeanDefinition是parent,所以会继承parent上所定义的scope属性
<bean id="parent" class="com.zhouyu.service.Parent" scope="prototype"/>
<bean id="child" class="com.zhouyu.service.Child" parent="parent"/>

Before generating a Bean object based on the child, BeanDefinitions need to be merged to obtain the complete BeanDefinition of the child.

Load class

After the BeanDefinition is merged, you can create a Bean object. To create a Bean, you must instantiate the object, and instantiation must first load the class corresponding to the current BeanDefinition. In the createBean() method of the AbstractAutowireCapableBeanFactory class, it will be called at the beginning. :

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

This line of code is to load the class. The method is implemented as follows:

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, then it will be returned directly. If not, it will be loaded according to the class name (what the doResolveBeanClass method does)

The class loader set by BeanFactory will be used to load the class. If it is not set, the class loader returned by ClassUtils.getDefaultClassLoader() will be used by default.

ClassUtils.getDefaultClassLoader()

  1. Return the ClassLoader in the current thread first
  2. When the class loader in the thread is null, the class loader of the ClassUtils class is returned.
  3. If the class loader of the ClassUtils class is empty, it means that the ClassUtils class is loaded by the Bootstrap class loader, then the system class loader is returned.

Before instantiation

After the class corresponding to the current BeanDefinition is successfully loaded, the object can be instantiated.

In Spring, before instantiating an object, Spring provides an extension point that allows users to control whether to perform some startup actions before instantiating one or more beans. This extension point is called InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation(). for example:

@Component
public class TacyBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    
    

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    
    
		if ("userService".equals(beanName)) {
    
    
			System.out.println("实例化前");
		}
		return null;
	}
}

The above code will cause printing to occur before the userService Bean is instantiated.

It is worth noting that postProcessBeforeInstantiation() has a return value. If it is implemented like this:

@Component
public class TacyBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    
    

	@Override
	public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    
    
		if ("userService".equals(beanName)) {
    
    
			System.out.println("实例化前");
			return new UserService();
		}
		return null;
	}
}

The userService Bean will directly return a UserService object defined by us before instantiation. If this is the case, it means that Spring is not needed for instantiation, and subsequent Spring dependency injection will not be performed. Some steps will be skipped and the post-initialization step will be performed directly.

Instantiate

In this step, an object will be created based on the BeanDefinition.

Supplier creates objects

First determine whether the Supplier is set in the BeanDefinition. If it is set, call the get() of the Supplier to get the object.
You have to use the BeanDefinition object directly to set the Supplier, such as:

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

Factory method creates objects

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

  • Way 1
<bean id="userService" class="com.tacy.service.UserService" factory-method="createUserService" />

The corresponding UserService class is:

public class UserService {
    
    

	public static UserService createUserService() {
    
    
		System.out.println("执行createUserService()");
		UserService userService = new UserService();
		return userService;
	}

	public void test() {
    
    
		System.out.println("test");
	}

}
  • Way 2
<bean id="commonService" class="com.tacy.service.CommonService"/>
<bean id="userService1" factory-bean="commonService" factory-method="createUserService" />

The corresponding CommonService class is:

public class CommonService {
    
    

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

After Spring finds that the current BeanDefinition method has set a factory method, it will distinguish between the two methods, and then call the factory method to get the object.

It is worth noting that the BeanDefinition we define through @Bean has factoryMethod and factoryBean, which is very similar to the second method above. The method annotated by @Bean is factoryMethod, and the AppConfig object is factoryBean. If the method annotated by @Bean is static, then the corresponding method is method one.

inferred constructor

After the constructor is inferred, the constructor is used for instantiation.

In addition, in the logic of inferring the constructor method, in addition to selecting the constructor method and searching for the input parameter object, it also determines whether there is a method annotated with @Lookup in the corresponding class. If it exists, encapsulate the method into a LookupOverride object and add it to the BeanDefinition.

During instantiation, if it is determined that there is no LookupOverride in the current BeanDefinition, then directly use the constructor method to reflect to obtain an instance object. If there is a LookupOverride object, that is, there is a @Lookup annotated method in the class, a proxy object will be generated.

The @Lookup annotation is method injection. Use the 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;
	}

}

BeanDefinition post-processing

After the Bean object is instantiated, the next step is to assign values ​​to the object's properties. Before actually assigning values ​​to properties, Spring provides an extension point, MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition(), which can process the BeanDefinition at this time, such as:

@Component
public class TacyMergedBeanDefinitionPostProcessor 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 code, AutowiredAnnotationBeanPostProcessor is a MergedBeanDefinitionPostProcessor. Its postProcessMergedBeanDefinition() will find the injection point and cache it in a Map of the AutowiredAnnotationBeanPostProcessor object (injectionMetadataCache)

After instantiation

After processing the BeanDefinition, Spring designed an extension point: InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation(), such as:

@Component
public class TacyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    
    

	@Override
	public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
    
    

		if ("userService".equals(beanName)) {
    
    
			UserService userService = (UserService) bean;
			userService.test();
		}

		return true;
	}
}

automatic injection

Automatic injection refers to Spring’s automatic injection

Handling attributes

In this step, annotations such as **@Autowired, @Resource, and @Value will be processed , which is also implemented through the InstantiationAwareBeanPostProcessor.postProcessProperties()** extension point. For example, we can even implement our own automatic injection function, such as:

@Component
public class TacyInstantiationAwareBeanPostProcessor 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(TacyInject.class)) {
    
    
					field.setAccessible(true);
					try {
    
    
						field.set(bean, "123");
					} catch (IllegalAccessException e) {
    
    
						e.printStackTrace();
					}
				}
			}
		}

		return pvs;
	}
}

ExecuteAware

After completing the attribute assignment, Spring will execute some callbacks, including:

  • BeanNameAware: Return the beanName to the bean object.
  • BeanClassLoaderAware: Return classLoader to the bean object.
  • BeanFactoryAware: Return beanFactory to the object.

Before initialization

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

@Component
public class TacyBeanPostProcessor implements BeanPostProcessor {
    
    

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
		if ("userService".equals(beanName)) {
    
    
			System.out.println("初始化前");
		}

		return bean;
	}
}

Before initialization, beans that have been dependency injected can be processed.
In the Spring source code:

  • InitDestroyAnnotationBeanPostProcessor will execute the @PostConstruct method in this step before initialization.
  • ApplicationContextAwareProcessor will perform other Aware callbacks in this step before initialization:
    a. EnvironmentAware: return environment variables
    b. EmbeddedValueResolverAware: return placeholder parser
    c. ResourceLoaderAware: return resource loader
    d. ApplicationEventPublisherAware: return event publishing e
    . MessageSourceAware: Returns internationalized resources
    f. ApplicationStartupAware: Returns other monitoring objects of the application, which can be ignored
    g. ApplicationContextAware: Returns the Spring container ApplicationContext

initialization

  • Check whether the current Bean object implements the InitializingBean interface. If so, call its afterPropertiesSet() method.
  • Execute the initialization method specified in the BeanDefinition

After initialization

This is the last step in the Bean creation life cycle and is also an extension point provided by Spring: BeanPostProcessor.postProcessAfterInitialization() , such as:

@Component
public class TacyBeanPostProcessor implements BeanPostProcessor {
    
    

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
		if ("userService".equals(beanName)) {
    
    
			System.out.println("初始化后");
		}

		return bean;
	}
}

In this step, the Bean can be finally processed. AOP in Spring is implemented based on initialization. The object returned after initialization is the final Bean object.

SummaryBeanPostProcessor

  1. InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()
  2. Instantiate
  3. MergedBeanDefinitionPostProcessor.postProcessMergedBeanDefinition()
  4. InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()
  5. automatic injection
  6. InstantiationAwareBeanPostProcessor.postProcessProperties()
  7. Aware object
  8. BeanPostProcessor.postProcessBeforeInitialization()
  9. initialization
  10. BeanPostProcessor.postProcessAfterInitialization()

Guess you like

Origin blog.csdn.net/beautybug1126/article/details/132330052