Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the principle of BeanPostProcessor in Spring source code

2025-04-21 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

Today, I will talk to you about the principle of BeanPostProcessor in the Spring source code, which may not be well understood by many people. in order to make you understand better, the editor has summarized the following content for you. I hope you can get something according to this article.

Summary of premises

Spring has good extensibility, but where is the extensibility of this extension? And the BeanPostProcessor we want to talk about is one of the excellent performance of Spring extensibility.

The role of BeanPostProcessor

To put it simply, BeanPostProcessor provides a method of callback before and after initialization, and what we call extension is to extend Bean before and after instantiation.

Register after the BeanDefinition registration is completed, and call the methods defined in the Bean before and after instantiation during the creation of the Bean

The operation object is the Bean instance that has been instantiated and populated with attributes and is to be initialized.

Source code analysis public interface BeanPostProcessor {/ @ Nullable default Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException {return bean;} / * after initialization, call * / @ Nullable default Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException {return bean;}}

The above is the definition of the BeanPostProcessor interface, and you can see from the method name that one of the two methods is called before initialization and the other after initialization.

Note that the return value of the method is the original instance or the wrapped instance. If null is returned, subsequent BeanPostProcessor will not take effect (multiple BeanPostProcessor can be registered).

How to use

The BeanPostProcessorDemo code is as follows:

Public class BeanPostProcessorDemo {public static void main (String [] args) {/ / create the base container DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory (); / / load the xml configuration file XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader (beanFactory); reader.loadBeanDefinitions ("spring-bean-post-processor.xml"); / / add BeanPostProcessor beanFactory.addBeanPostProcessor (new UserBeanPostProcessor ()); User user = beanFactory.getBean (User.class) System.out.println (user);}} @ Dataclass User {private String userName; private Integer age; private String beforeMessage; private String afterMessage; public void initMethod () {System.out.println ("initialization:" + this "); this.setUserName (" Xiaoming "); this.setAge (18) }} class UserBeanPostProcessor implements BeanPostProcessor {@ Override public Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException {if (bean instanceof User) {System.out.println ("pre-initialization:" + bean); ((User) bean) .setBeforeMessage ("pre-initialization message");} return bean } @ Override public Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException {if (bean instanceof User) {System.out.println ("after initialization:" + bean); ((User) bean) .setAfterMessage ("post-initialization message");} return bean;}}

Other omissions.

After running, the print result is as follows:

Before initialization: User (userName=null, age=null, beforeMessage=null, afterMessage=null) initialization: User (userName=null, age=null, beforeMessage= pre-initialization information, afterMessage=null) after initialization: User (userName= Xiaoming, age=18, beforeMessage= pre-initialization information, afterMessage=null) User (userName= Xiaoming, age=18, beforeMessage= pre-initialization information, afterMessage= post-initialization information)

The above code is very simple to create the basic container, because I use BeanFactory,BeanFactory as the basic container here I use manual registration of BeanPostProcessor to the container. At the same time, it can also be generated into the container by scanning or definition.

The following is an analysis of the print results:

Before initialization: User (userName=null, age=null, beforeMessage=null, afterMessage=null) this result is the output of the postProcessBeforeInitialization method. At this time, the User instance has only been instantiated and the initialization step has not yet been completed, so all attributes are null, indicating that the method is indeed initialized. -- (initialization at this time refers to the init method of the bean object)

Initialization: User (userName=null, age=null, beforeMessage= pre-initialization message, afterMessage=null) this result is the output of the custom initialization method initMethod method. At this time, the User instance is really initialized, and the value in beforeMessage is exactly what we set in postProcessBeforeInitialization.

After initialization: User (userName= Xiaoming, age=18, beforeMessage= pre-initialization message, afterMessage=null) this result is the output of postProcessAfterInitialization, from the print results we can see that it is indeed after the custom initMethod.

Life cycle of Spring

Generally speaking, Bean in Spring can be divided into four cycles: instantiation, attribute assignment, initialization, and destruction. BeanPostProcessor is executed before and after the initialization phase.

First look at the doCreateBean method in AbstractAutowireCapableBeanFactory, which is actually the way to create a specified Bean.

Three important method calls are as follows: createBeanInstance, populateBean, and initializeBean.

These three methods represent the three life cycles of instantiation, attribute assignment and initialization in Spring Bean, respectively.

BeanPostProcessor is called before and after initialization, so let's just look at the method details in initializeBean. Details of the method are as follows:

Protected Object initializeBean (String beanName, Object bean, @ Nullable RootBeanDefinition mbd) {/ / process BeanNameAware, BeanClassLoaderAware, BeanFactoryAware if (System.getSecurityManager ()! = null) {AccessController.doPrivileged ((PrivilegedAction) ()-> {invokeAwareMethods (beanName, bean); return null;}, getAccessControlContext ()) } else {invokeAwareMethods (beanName, bean);} / / process BeanPostProcessor Object wrappedBean = bean If (mbd = = null | |! mbd.isSynthetic ()) {/ / callback postProcessBeforeInitialization method wrappedBean = applyBeanPostProcessorsBeforeInitialization (wrappedBean, beanName) } try {/ / processes initMethod invokeInitMethods (beanName, wrappedBean, mbd) specified in InitializingBean and BeanDefinition;} catch (Throwable ex) {throw new BeanCreationException ((mbd! = null?) Mbd.getResourceDescription (): null), beanName, "Invocation of init method failed", ex) } if (mbd = = null | |! mbd.isSynthetic ()) {/ / callback postProcessAfterInitialization method wrappedBean = applyBeanPostProcessorsAfterInitialization (wrappedBean, beanName);} return wrappedBean;}

From the above source code, we can see that the first step is to deal with some Aware-related APIs, and then to deal with the postProcessBeforeInitialization method in BeanPostProcessor. The details of this method are as follows:

Public Object applyBeanPostProcessorsBeforeInitialization (Object existingBean, String beanName) throws BeansException {Object result = existingBean; / / processes BeanPostProcessor for (BeanPostProcessor processor: getBeanPostProcessors ()) {Object current = processor.postProcessBeforeInitialization (result, beanName) in turn; / / if null is returned, the / / postProcessBeforeInitialization in the subsequent BeanPostProcessor is directly returned and if (current = = null) {return result;} result = current;} return result;} is no longer executed

This method is to perform the details of the postProcessBeforeInitialization callback, and as you can see from this implementation, there can be multiple BeanPostProcessor and will be processed sequentially. If any one of them returns null, the postProcessBeforeInitialization of the subsequent BeanPostProcessor will no longer be processed.

The initialization method is then executed, that is, the invokeInitMethods method is called.

Protected void invokeInitMethods (String beanName, Object bean, @ Nullable RootBeanDefinition mbd) throws Throwable {boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean & & (mbd = = null | |! mbd.isExternallyManagedInitMethod ("afterPropertiesSet")) {if (logger.isTraceEnabled ()) {logger.trace ("Invoking afterPropertiesSet () on bean with name'" + beanName + ")) } / / if the current Bean implements the InitializingBean interface, it will execute its afterPropertiesSet () method if (System.getSecurityManager ()! = null) {try {AccessController.doPrivileged ((PrivilegedExceptionAction) ()-> {((InitializingBean) bean) .afterPropertiesSet (); return null;}, getAccessControlContext ()) } catch (PrivilegedActionException pae) {throw pae.getException ();}} else {((InitializingBean) bean) .afterPropertiesSet ();}} / / if initMethod is defined in BeanDefinition, execute initialization method if (mbd! = null & & bean.getClass ()! = NullBean.class) {String initMethodName = mbd.getInitMethodName () If (StringUtils.hasLength (initMethodName) & & (isInitializingBean & & "afterPropertiesSet" .equals (initMethodName)) & &! mbd.isExternallyManagedInitMethod (initMethodName)) {invokeCustomInitMethod (beanName, bean, mbd);}}

From the above code, it is further verified that the postProcessBeforeInitialization method in BeanPostProcessor is indeed called before initialization.

When the invokeInitMethods is executed, the applyBeanPostProcessorsAfterInitialization method is then executed.

Overridepublic Object applyBeanPostProcessorsAfterInitialization (Object existingBean, String beanName) throws BeansException {Object result = existingBean; for (BeanPostProcessor processor: getBeanPostProcessors ()) {Object current = processor.postProcessAfterInitialization (result, beanName); if (current = = null) {return result;} result = current;} return result;}

This method is almost the same as applyBeanPostProcessorsBeforeInitialization, except that it executes postProcessAfterInitialization. At this point, the initialization of the Spring Bean is complete.

Support for @ PostConstruct

You learned about the initialization process in the Spring Bean life cycle above, but in fact Spring also supports JSR250, such as the @ PostConstruct annotation, but the initialization process of Spring Bean is not found in the previous source code.

The secret here is our BeanPostProcessor.

There is a CommonAnnotationBeanPostProcessor class in Spring, and it is mentioned in the comments of this class that this class is used to support JSR250 and other specifications.

Next, I'll use the source code of this class to illustrate how Spring uses BeanPostProcessor to support @ PostContruct.

As we can see from the figure above, CommonAnnotationBeanPostProcessor does not implement BeanPostProcessor directly, it inherits the InitDestroyAnnotationBeanPostProcessor class, and the implementation of @ PostConstruct is mainly in this class.

The implementation code for BeanPostProcessor is as follows:

@ Overridepublic Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException {/ / Lifecycle metadata encapsulation LifecycleMetadata metadata = findLifecycleMetadata (bean.getClass ()); try {/ / execute InitMethods metadata.invokeInitMethods (bean, beanName);} catch (InvocationTargetException ex) {throw new BeanCreationException (beanName, "Invocation of init method failed", ex.getTargetException ());} catch (Throwable ex) {throw new BeanCreationException (beanName, "Failed to invoke init method", ex) } return bean;} @ Overridepublic Object postProcessAfterInitialization (Object bean, String beanName) throws BeansException {return bean;}

The implementation of BeanPostProcessor is mainly in the before method, which is mainly composed of two parts. The first part mainly encapsulates the information into LifecycleMetadata, which facilitates the implementation of the relevant initialization methods in the second step.

Through the above method, we know that Spring implements JSR250 with the help of BeanPostProcessor.

Public class BeanPostProcessorDemo2 {public static void main (String [] args) {/ / create the base container DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory (); / / build BeanDefinition and register AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition (Person.class) .getBeanDefinition (); beanFactory.registerBeanDefinition ("person", beanDefinition) / / Register CommonAnnotationBeanPostProcessor CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor = new CommonAnnotationBeanPostProcessor (); beanFactory.addBeanPostProcessor (commonAnnotationBeanPostProcessor); / / get Bean Person person = beanFactory.getBean (Person.class); System.out.println (person);}} class Person {@ PostConstruct public void annotationInitMethod () {System.out.println ("@ PostConstruct");}}

The above code is relatively simple. We define a Person and mark its initialization method with @ PostConstruct, then we create the BeanFactory and create the BeanDefinition of the Person to register it with the BeanFactory (just like reading the configuration file), and then we create the CommonAnnotationBeanPostProcessor and add it to the BeanFactory.

The final print result prints out @ PostConstruct. If we comment on the following code.

BeanFactory.addBeanPostProcessor (commonAnnotationBeanPostProcessor)

If you execute it again, you will find that @ PostConstruct will fail and the result will not be printed.

? Sequentiality

Multiple BeanPostProcessor can be registered, and the BeanPostProcessor is stored inside the AbstractBeanFactory through the List variable beanPostProcessors. The execution is done one by one in the order of BeanPostProcessor in List, so we need to pay attention to the order when we want to add BeanPostProcessor to the container. If we do not add it manually (most of the time not), but define multiple BeanPostProcessor in the code or configuration file, we can control its order by implementing the Ordered interface.

BeanPostProcessor dependent Bean will not execute BeanPostProcessor BeanPostProcessor dependent Bean will not execute BeanPostProcessor, this is because the Bean that the BeanPostProcessor depends on needs to be initialized, and the BeanPostProcessor has not been initialized yet.

In addition, we need to know that @ PostConstruct execution point (beforeInitialization) is earlier than afterProperitesSet (invokeInitMethod-1) before the execution of the corresponding Bean-defined initMethod (invokeinitiMethod-2) method.

The example code is as follows:

Public class App3 {public static void main (String [] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext (); context.scan ("com.buydeem.beanpostprocessor"); context.register (App3.class); context.refresh ();} @ Componentclass ClassA {} @ Componentclass ClassB {} @ Componentclass MyBeanPostProcessor implements BeanPostProcessor {@ Autowired private ClassA classA @ Override public Object postProcessBeforeInitialization (Object bean, String beanName) throws BeansException {System.out.println ("MyBeanPostProcessor" + bean); return bean;}}

Note: in the end, the ClassA will not be printed, but the ClassB will be printed. Because MyBeanPostProcessor relies on ClassA instances.

Summary

There are many subinterfaces or implementation classes for BeanPostProcessor in Spring, for example.

InstantiationAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor and so on.

These interfaces are at different stages of the Spring Bean life cycle, and their functions are similar to those of BeanPostProcessor to provide extension points for each declaration cycle of Spring Bean.

After reading the above, do you have any further understanding of the principle of BeanPostProcessor in the Spring source code? If you want to know more knowledge or related content, please follow the industry information channel, thank you for your support.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report