Overview
There are multiple customizable extension points in the process of creating a bean to perform custom operations during the bean life cycle. The following are examples of customizable points and scenarios throughout the bean life cycle.
BeanDefinitionRegistryPostProcessor
Bean definition registration post-processor allows you to modify or register bean definitions before the Spring container is initialized. You can implement this interface to customize bean registration
injection
- injection
<bean class="org.example.bean.MyBeanDefinitionRegistryPostProcessor"/>
- customize
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } }
Where did it start? Above, where is spring called after we define a bean that implements BeanDefinitionRegistryPostProcessor? The following is the place where the initialization call is made
AbstractApplicationContext
?Method?refresh
?Invoke bean factory post-processor:?invokeBeanFactoryPostProcessors
?, at this time the bean factory?ConfigurableListableBeanFactory
code>?has been instantiated- The above method calls?
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())
;?
It is a static method, which is called in this method. The key code is as follows:
while (reiterate) { reiterate = false; //Get postProcessorNames based on the class type BeanDefinitionRegistryPostProcessor, Did you see that we now have our bean name? postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); reiterate = true; } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); //reflection call here invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup()); currentRegistryProcessors.clear(); } //Call invokeBeanFactoryPostProcessors here invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); }
- Method?
invokeBeanDefinitionRegistryPostProcessors
?Invoke
private static void invokeBeanDefinitionRegistryPostProcessors( Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {<!-- --> for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {<!-- --> StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process") .tag("postProcessor", postProcessor::toString); //Call postProcessBeanDefinitionRegistry here postProcessor.postProcessBeanDefinitionRegistry(registry); postProcessBeanDefRegistry.end(); } }
At this point, the analysis of the initialization call process is completed.
Method: postProcessBeanDefinitionRegistry
For some operations of ?BeanDefinitionRegistry
?, beans can be added and deleted dynamically
@Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {<!-- --> GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition(); genericBeanDefinition.setBeanClass(MyBean1.class); //Register bean registry.registerBeanDefinition("myBean", genericBeanDefinition); //Delete the bean, the premise exists if(registry.containsBeanDefinition("beanName")){<!-- --> registry.removeBeanDefinition("beanName"); } }
Scenario 1: Customize the scanning package and register the bean
- Custom annotations
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface CustomComponent { }
- Scan custom packages and register beans
@Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false); TypeFilter filter = new AnnotationTypeFilter(CustomComponent.class); scanner.addIncludeFilter(filter); //Scan the specified package for (BeanDefinition beanDefinition : scanner.findCandidateComponents("org.example.bean")) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( ClassUtils.resolveClassName(Objects.requireNonNull(beanDefinition.getBeanClassName()), ClassUtils.getDefaultClassLoader())); registry.registerBeanDefinition(beanDefinition.getBeanClassName(), builder.getBeanDefinition()); } }
Under the custom package, classes annotated with CustomComponent
will be registered in beans.
Method: postProcessBeanFactory
This is quite rich in some operations of ConfigurableListableBeanFactory
The following are examples of usage scenarios:
Scenario 1: Dynamically registering a singleton bean
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { beanFactory.registerSingleton("myBean1",new MyBean1()); }
Scenario 2: Add bean post-processing interceptor BeanPostProcessor
BeanPostProcessor is the interceptor for beans before and after initialization. As many beans are created and intercepted as many times, the interceptor can be dynamically added through postProcessBeanFactory
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { beanFactory.registerSingleton("myBean1",new MyBean1()); beanFactory.addBeanPostProcessor(new MyBeanPostProcessor()); }
MyBeanPostProcessor is defined as follows
import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("Start registration: " + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("Registration completed: " + beanName); return bean; } }
Scenario 3: Custom attribute configuration (very good)
What it means is that you can convert custom attributes and convert string values into attribute values of a specific type.
You will understand after watching the scene
The object whose bean is User has a property which is private Address address;
Address also has its own properties, as follows:
private String address1; private String address2; private String address3;
When I inject a bean, if what is transmitted is a string or other messy format, which is not an Address anyway, what should I convert as follows?
In the following bean configuration, the address given is Henan-Zhengzhou-High-tech Zone
, which is obviously not an object, so we can use a custom converter to convert it.
<bean id="user" class="org.example.bean.User"> <property name="id" value="1"/> <property name="name" value="Zhang San"/> <property name="address" value="Henan-Zhengzhou-High-tech Zone"/> </bean>
Custom converter definition
- The converter
AddressEditor
corresponding to the new Address should inheritPropertyEditorSupport
and override the methodsetAsText
. In fact, what we get here is the aboveHenan-Zhengzhou- High-tech Zone
string, we manually convert it, and finally callsetValue
to encapsulate it
public class AddressEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { String[] arrs = text.split("-"); Address address = new Address(); address.setAddress1(arrs[0]); address.setAddress2(arrs[1]); address.setAddress3(arrs[2]); super.setValue(address); } }
- Then
AddressEditor
definitely needs to be registered. How to register? Create a new custom conversion register to implementPropertyEditorRegistrar
public class CustomEditorRegistrar implements PropertyEditorRegistrar { @Override public void registerCustomEditors(PropertyEditorRegistry registry) { registry.registerCustomEditor(Address.class, new AddressEditor()); } }
- The registrar needs to be handed over to spring for processing, then we can add the registrar through the
postProcessBeanFactory
method.
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { beanFactory.addPropertyEditorRegistrar(new CustomEditorRegistrar()); }
After execution, the bean’s Address is our converted value
Scenario 4: Dynamic injection of attribute values
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { //Modify properties BeanDefinition user = beanFactory.getBeanDefinition("user"); MutablePropertyValues propertyValues = user.getPropertyValues(); propertyValues.addPropertyValue("id","123456"); } }
Scenario 5: Bean definition modification
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { //Modify properties BeanDefinition bd = beanFactory.getBeanDefinition("user"); //Modify the initial method, whether it is a singleton, and other attributes bd.setInitMethodName("test2"); }
FactoryBeans
Used to create and configure object instances. It allows you to encapsulate object creation and configuration logic in a separate class
public class MyBeanFactory implements FactoryBean<User> { @Override public User getObject() throws Exception { // Here you can write the logic to create and configure MyBean instances return new User(); } @Override public Class<?> getObjectType() { return User.class; } @Override public boolean isSingleton() { return true; //Here you can specify whether the object is a singleton } }
This can also create beans
InitializingBean
bean implements InitializingBean
and overrides method afterPropertiesSet
@Override public void afterPropertiesSet() throws Exception { System.out.println("afterPropertiesSet"); }
- Call time?
You should be familiar with class AbstractApplicationContext
refresh called after the bean has completed initialization (not yet instantiated). This method is executed until
finishBeanFactoryInitialization(beanFactory);
Instantiate beans
Look at the bean instantiation with questions and know when to execute the above afterPropertiesSet
- The above method will execute the beanFactory implementation class
DefaultListableBeanFactory
methodpreInstantiateSingletons
Simplified code:
public void preInstantiateSingletons() throws BeansException { //beanDefinitionNames You should be familiar with this. This is a list that stores all bean names. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); for (String beanName : beanNames) { //Just instantiate the bean based on the bean name getBean(beanName); } }
The above code is to loop the bean name and then instantiate it
- getBean will call
doGetBean
. This method is to get the member variablemergedBeanDefinitions
based on the bean name to get the metadata information of the bean, which is also a map, and what is stored in it isRootBeanDefinition
It stores the bean attribute information you defined in xml - With the name and the corresponding metadata, the bean can be created. The final creation code is in the class
AbstractAutowireCapableBeanFactory
protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) { try { Object beanInstance; beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this); BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; }
getInstantiationStrategy
will get the instantiation strategy class, here isSimpleInstantiationStrategy
, the code comes here, there is nothing to talk about here, just get the reflection classConstructor
Then create it based on .class, specifically using the tool classBeanUtils.instantiateClass(constructorToUse)
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { if (!bd.hasMethodOverrides()) { Constructor<?> constructorToUse; synchronized (bd.constructorArgumentLock) { constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod; if (constructorToUse == null) { final Class<?> clazz = bd.getBeanClass(); constructorToUse = clazz.getDeclaredConstructor(); bd.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } return BeanUtils.instantiateClass(constructorToUse); }
Class Constructor
usage:
Class<Person> personClass = Person.class; Constructor<Person> constructor = personClass.getConstructor(String.class, int.class); // Create object using constructor Person person = constructor.newInstance("Alice", 25);
- What is returned after the above creation is finally a
BeanWrapper
object, which is a bean tool class provided by spring for convenient access and manipulation of the properties of the bean object.
The specific usage is as follows:
public static void main(String[] args) { //Create a simple object Person person = new Person(); // Use BeanWrapper to wrap this object BeanWrapper wrapper = new BeanWrapperImpl(person); //Set attribute value wrapper.setPropertyValue("name", "Alice"); wrapper.setPropertyValue("age", 25); // Get attribute value String name = (String) wrapper.getPropertyValue("name"); int age = (int) wrapper.getPropertyValue("age"); System.out.println("Name: " + name); System.out.println("Age: " + age); //Get the object Object bean = wrapper.getWrappedInstance(); }
At this point, the bean instantiation is completed, and a bean wrapper class BeanWrapper
is finally returned.
Call afterPropertiesSet and the defined initialization method
Going back to the beginning, how can the bean name and RootBeanDefinition
be associated with BeanWrapper
?
- The method
doCreateBean
is associated with the code, mbd is theRootBeanDefinition
object, andgetWrappedClass
is the class to get the bean
Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; }
- Initialize the bean method
doCreateBean
to callinitializeBean
to callinvokeInitMethods
. The simplified code is as follows
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean & amp; & amp; (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { ((InitializingBean) bean).afterPropertiesSet(); } if (mbd != null & amp; & amp; bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) & amp; & amp; !(isInitializingBean & amp; & amp; "afterPropertiesSet".equals(initMethodName)) & amp; & amp; !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }
- The first if above is to enter the key code of
afterPropertiesSet
. What does it mean? If the bean inheritsInitializingBean
, it will be called. - Look at the second if
mbd.getInitMethodName()
mbd isRootBeanDefinition
,getInitMethodName
is the initialization method you defined in xml, xml configuration as follows:
public void test(){ System.out.println("Initialization"); }
If the method defined by init-method has a length, and the method name is not afterPropertiesSet
and is not mbd.isExternallyManagedInitMethod(initMethodName)
, what does this mean? This leads to another concept: to be continued
Then use reflection to execute the initialization method we defined
Summary
afterPropertiesSet
is called after the bean is initialized and instantiated. After its execution, other xml-defined init-method
will be called.
- Note that
init-method
only accepts a parameterless initialization method