Source code learning of BeanFactoryPostProcessor and BeanPostProcessor of Spring hook

Keywords: Programming Spring xml SpringBoot Scala

BeanFactoryPostProcessor and BeanPostProcessor are two interfaces that are exposed when initializing beans. They are similar to Aware (PS: for spring hook, please see) Detailed explanation of Spring hook method and hook interface This article also mainly studies the details of the specific hooks, so that we can be efficient in the actual development, such as how to obtain the springboot startup class in scala and so on. In order to monitor the service of the whole system, some middleware also needs to obtain the spring container data and status.
Next, learn about BeanFactoryPostProcessor and BeanPostProcessor.

BeanFactoryPostProcessor

The bean property processing container of the bean factory can manage all the bean definition data in our bean factory and modify the properties at will.

Usage method

public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    /**
     * It is mainly used to customize and modify the bean s held
     * ConfigurableListableBeanFactory In fact, the DefaultListableBeanDefinition object
     * @param beanFactory
     * @throws BeansException
     */

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("Called custom BeanFactoryPostProcessor " + beanFactory);
        Iterator it = beanFactory.getBeanNamesIterator();

        String[] names = beanFactory.getBeanDefinitionNames();
        // Got a list of all bean names
        for(int i=0; i<names.length; i++){
            String name = names[i];

            BeanDefinition bd = beanFactory.getBeanDefinition(name);
            System.out.println(name + " bean properties: " + bd.getPropertyValues().toString());
            // This content is just a demo to print the properties of the bean held
        }
    }
}
<context:property-placeholder location="pro.properties" />
<bean id="student" class="Student" init-method="init" />
<bean class="CustomBeanFactoryPostProcessor" id="customBeanFactoryPostProcessor" />

Output result

 
image.png

 

Source code analysis

There's no doubt that xml has been parsed. Keep looking at the refresh function

AbstractApplicationContext file

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();
        // Parsing xml is completed and stored in a specific bean factory
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // Initialization of bean factory
        prepareBeanFactory(beanFactory);
        try {
            // This class is implemented by subclass inheritance. Currently, this method is empty
            postProcessBeanFactory(beanFactory);
            // invoke the BeanFactoryPostProcessors in it, which we are talking about now
            invokeBeanFactoryPostProcessors(beanFactory);
        ...

Postprocessor registrationdelegate file

The parameters of the invokeBeanFactoryPostProcessors method are the bean factory ConfigurableListableBeanFactory and the currently known postprocessor object. The function is divided into several parts to process. Just intercept the part we care about (in fact, it also includes the similar processing procedures of priority, property, etc.)

String[] postProcessorNames =
                beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
        // Filter out all the class names that implement the BeanFactoryPostProcessor class existing in the bean project

        List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
        List<String> orderedPostProcessorNames = new ArrayList<String>();
        List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
        for (String ppName : postProcessorNames) {
            if (processedBeans.contains(ppName)) {
                // It already exists and will not be processed
            }
            else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
                // Is of type PriorityOrdered
            }
            else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                orderedPostProcessorNames.add(ppName);
                // Is of Ordered type
            }
            else {
                nonOrderedPostProcessorNames.add(ppName);
                // This is the PostProcessors we need to be concerned about at present
            }
        }

        ...
        List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
        for (String postProcessorName : nonOrderedPostProcessorNames) {
            nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
            // Get the customized BeanFactoryPostProcessor
            // Here's one thing to notice!!!!
        }
        invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

In the above code, nonOrderedPostProcessors. Add (beanfactory. GetBean (postprocessor name, beanfactory postprocessor. Class)); GetBean is used in. At first, I didn't notice that it was supposed to generate specific objects and modify them. In fact, there is a parameter BeanFactoryPostProcessor.class after getBean. Notice that this function returns an abstract class. The conclusion is that what nonOrderedPostProcessors added is not a bean instance, but a beandefinition, before instantiation!!! That's important

private static void invokeBeanFactoryPostProcessors(
        Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
    for (BeanFactoryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanFactory(beanFactory);
        // Call the method of each customized BeanFactoryPostProcessor
        // In this article, we will call the custom CustomBeanFactoryPostProcessor's postProcessBeanFactory method
    }
}

More about the above getBeanNamesForType function. It must be easy to understand from the name. Get the beanName in the container according to the type passed. Understand the internal implementation principle

getBeanNamesForType function of DefaultListableBeanFactory file

// Type: the type name of the class
// Include non singlets: return data contains non singleton bean name
// Alloweageinit: initialization can be loaded in advance
public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
    if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
       // Unavailable cache, invalid type, not allowed to load initialization in advance
       // Need to get the original type of the current type, continue to get data
        return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
    }
    Map<Class<?>, String[]> cache =
            (includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
    String[] resolvedBeanNames = cache.get(type);
    // If the data has been stored in the cache, it does not need to be recalculated and directly returned
    if (resolvedBeanNames != null) {
        return resolvedBeanNames;
    }
    resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
    // This step is to actually get the data, traverse each data of beanDefinitionNames, and the qualified data will be added to the returned list
    
    if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
        cache.put(type, resolvedBeanNames);
        // Easy to get next time and add to cache
    }
    return resolvedBeanNames;
}

BeanPostProcessor

In terms of scope, all the beans above become specific beans. Secondly, BeanFactoryPostProcessor can modify the properties of beans before initialization. However, BeanPostProcessor can only perform some operations after initialization (note that initialization does not include init method).
Many articles on the Internet say that BeanPostProcessor can't modify bean properties. In fact, I don't think so. After instantiation, the instantiated object can be obtained completely, and some value modification operations can also be performed on the object

Usage method

public class Student {

    @Value("${name}")
    private String className;

    public Student() {
        System.out.println("constructor loading");
    }

    public void init(){
        System.out.println("init loading");
    }
}
public class CustomBeanPostProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof Student){
            // Print the log if the current bean is Student
            System.out.println("postProcessBeforeInitialization bean : " + beanName);
        }
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof Student){
            System.out.println("postProcessAfterInitialization bean : " + beanName);
        }
        return bean;
    }
}
<bean id="student" class="Student" init-method="init" />
<bean class="CustomBeanPostProcessor" id="customBeanPostProcessor" />

Output result

 
image.png
  • Printing out the internal execution of the constructor first means that the Student class is instantiated at this time,
  • postProcessBeforeInitialization executed before init method
  • postProcessAfterInitialization performed after init method

Source code analysis

The entry is still the refresh function. After the initialization, enter the finishBeanFactoryInitialization(beanFactory) to perform the operations related to BeanPostProcessor. The middle process is too long, including the getBean operation. The genBean operation is too tedious, which will be introduced later.

 
image.png

AbstractAutowireCapableBeanFactory file

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                invokeAwareMethods(beanName, bean);
                return null;
            }
        }, getAccessControlContext());
    }
    else {
        invokeAwareMethods(beanName, bean);
        // aware is also an external hook
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        // This step is to perform the before operation of the customized bean post processor
    }

    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
        // Execute init method
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
    }

    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        // This step is to perform the after operation of the customized bean post processor
    }
    return wrappedBean;
}
public Object applyBeanPostProcessorsBeforeInitialization(
                    Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
       // Get all BeanPostProcessor objects and execute the postProcessBeforeInitialization method
        result = beanProcessor.postProcessBeforeInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
    return result;
    // Then return the execution result
}
public Object applyBeanPostProcessorsAfterInitialization(
                Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        result = beanProcessor.postProcessAfterInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
    return result;
}
protected void invokeInitMethods(String beanName, 
                    final Object bean, RootBeanDefinition mbd)
        throws Throwable {

    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        if (logger.isDebugEnabled()) {
            logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
        }
        if (System.getSecurityManager() != null) {
            try {
                AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                    @Override
                    public Object run() throws Exception {
                        ((InitializingBean) bean).afterPropertiesSet();
                        return null;
                    }
                }, getAccessControlContext());
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }

    if (mbd != null) {
       // invoke reflection executes init method
        String initMethodName = mbd.getInitMethodName();
        if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.isExternallyManagedInitMethod(initMethodName)) {
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}


Author: jwfy
Link: https://www.jianshu.com/p/a90a3e617ba6
Source: Jianshu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

Posted by webhamster on Sun, 08 Mar 2020 22:36:17 -0700