The pre-instantiation phase of Spring Beans

In the last section, we saw that some information about the Class in the BeanDefinition is converted from a text form to a real Class object. This process is called ClassLoading of the BeanDefinition, or Bean.

Next, we’ll continue to discuss how information about this class can become an example of a Bean, the instantiation phase of a Spring Bean, which is divided into three phases, the pre-phase, the mid-phase, and the post-phase. In this section, we’ll discuss the pre-phase.

Non-mainstream Lifecycle – The phase before Bean instantiation

In fact, the pre-bean instantiation phase is actually a non-mainstream phase, and the operations in this phase are rarely touched directly or encountered in daily work.

InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation

public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

   /**
    * Apply this BeanPostProcessor <i>before the target bean gets instantiated</i>.
    * The returned bean object may be a proxy to use instead of the target bean,
    * effectively suppressing default instantiation of the target bean.
    * <p>If a non-null object is returned by this method, the bean creation process
    * will be short-circuited. The only further processing applied is the
    * {@link #postProcessAfterInitialization} callback from the configured
    * {@link BeanPostProcessor BeanPostProcessors}.
    * <p>This callback will be applied to bean definitions with their bean class,
    * as well as to factory-method definitions in which case the returned bean type
    * will be passed in here.
    * <p>Post-processors may implement the extended
    * {@link SmartInstantiationAwareBeanPostProcessor} interface in order
    * to predict the type of the bean object that they are going to return here.
    * <p>The default implementation returns {@code null}.
    * @param beanClass the class of the bean to be instantiated
    * @param beanName the name of the bean
    * @return the bean object to expose instead of a default instance of the target bean,
    * or {@code null} to proceed with default instantiation
    * @throws org.springframework.beans.BeansException in case of errors
    * @see #postProcessAfterInstantiation
    * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getBeanClass()
    * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getFactoryMethodName()
    */
   @Nullable
   default Object postProcessBeforeInstantiation(Class
        beanClass, String beanName) throws BeansException {
      return null; }... Omit the following}Copy the code

So what’s going on at this stage? Break at this stage we have both a recognition of Spring Bean instantiation, we see InstantiationAwareBeanPostProcessor interface, this interface is to extend the BeanPostProcessor interface is a subinterface, A method called postProcessBeforeInstantiation inside it,

It is a pre-operation that gets a proxy object for the Bean to be instantiated to replace the target Bean, effectively preventing the container from instantiating the target Bean by default. If the return is null, the default initialization is performed, and the Spring container is instantiated.

This action breaks Spring’s default process of registering and instantiating beans, which can be demonstrated here, so let’s go back to idea.

Code sample

org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

org.geekbang.thinking.in.spring.bean.lifecycle.BeanInstantiationLifecycleDemo org.geekbang.thinking.in.spring.bean.lifecycle.MyInstantiationAwareBeanPostProcessor

Example class writing

Create an example to do this,

BeanInstantiationLifecycleDemo is to run the main class, most of the copy before the main class code

public class BeanInstantiationLifecycleDemo {

    public static void main(String[] args) {
        executeBeanFactory();
    }

    private static void executeBeanFactory(a) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        / / methods: add MyInstantiationAwareBeanPostProcessor BeanPostProcessor implementation
        // beanFactory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor());
        / / method 2: MyInstantiationAwareBeanPostProcessor as Bean registration
        // Implementation based on the XML resource BeanDefinitionReader
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        String[] locations = {"META-INF/dependency-lookup-context.xml"."META-INF/bean-constructor-dependency-injection.xml"};
        int beanNumbers = beanDefinitionReader.loadBeanDefinitions(locations);
        System.out.println("Number of BeanDefinitions loaded:" + beanNumbers);
        // Do a dependency lookup by Bean Id and type
        User user = beanFactory.getBean("user", User.class);
        System.out.println(user);

        User superUser = beanFactory.getBean("superUser", User.class);
        System.out.println(superUser);

        // Constructor injection by type, resolveDependency
        UserHolder userHolder = beanFactory.getBean("userHolder", UserHolder.class); System.out.println(userHolder); }}Copy the code

MyInstantiationAwareBeanPostProcessor is InstantiationAwareBeanPostProcessor interface implementation class

/ * * * *@author <a href="mailto:[email protected]">Mercy</a>
 * @since* /
class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    / * * * 93 | stage before Spring Bean instantiation: Bean instantiation can be bypassed? * *@param beanClass
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInstantiation(Class
        beanClass, String beanName) throws BeansException {
        if (ObjectUtils.nullSafeEquals("superUser", beanName) && SuperUser.class.equals(beanClass)) {
            // Overwrite the superUser Bean
            return new SuperUser();
        }
        return null; // Keep Spring IoC container instantiated}... }Copy the code

InstantiationAwareBeanPostProcessor is implemented

There are two InstantiationAwareBeanPostProcessor implementation way, we can see from the Spring after 5, it is actually used the Java 8, so the interface can provide a default implementation inside this way, you can see interface do have a default implementation code.

We can directly use interface to implement, so it can simplify the operation, only to realize postProcessBeforeInstantiation method is enough.

If it was before Spring 5 it would have an implementation of Adapter,

org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter

package org.springframework.beans.factory.config;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;

import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;

/**
 * Adapter that implements all methods on {@link SmartInstantiationAwareBeanPostProcessor}
 * as no-ops, which will not change normal processing of each bean instantiated
 * by the container. Subclasses may override merely those methods that they are
 * actually interested in.
 *
 * <p>Note that this base class is only recommendable if you actually require
 * {@link InstantiationAwareBeanPostProcessor} functionality. If all you need
 * is plain {@link BeanPostProcessor} functionality, prefer a straight
 * implementation of that (simpler) interface.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since2.0 * /
public abstract class InstantiationAwareBeanPostProcessorAdapter implements SmartInstantiationAwareBeanPostProcessor {...@Override
   public Object postProcessBeforeInstantiation(Class
        beanClass, String beanName) throws BeansException {
      return null;
   }

   @Override
   public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
      return true; }... }Copy the code

In fact, do a simple implementation of all the methods inside, inherit the Adapter and override the method is the same.

Autotype postProcessBeforeInstantiation method, to understand its meaning

Good, know how to change, we need to copy postProcessBeforeInstantiation method, namely realize InstantiationAwareBeanPostProcessor interface, then what is the method to realize the above,

So let’s look at this method. The first impression of this method implementation is a little strange. We know that the Class was loaded during ClassLoading, and the beanName is actually known at registration time. And I don’t need to change anything, because I need to return an object, and I need to go back to the Java DOC, and I need to understand it again, okay

/ * * *... * Apply this BeanPostProcessor before the target bean gets instantiated. * The returned bean object may be a proxy to use instead of the target bean, * effectively suppressing default instantiation of the target bean. * ... * /
@Nullable
default Object postProcessBeforeInstantiation(Class
        beanClass, String beanName) throws BeansException {
   return null;
}

Copy the code

It is a pre-operation that gets a proxy object for the Bean to be instantiated to replace the target Bean, effectively preventing the container from instantiating the target Bean by default.

You see this statement, it is clear that the normal way to instantiate the Bean.

In short, it is a non-mainstream operation where I can generate an instance of a Bean that is not composed by Spring as I want it to be.

Then look at the method body. Aren’t there two configured beans? One is the User, the other is the SuperUser, and we’re going to intercept the input of the SuperUser, and then we’re going to access the normal object instead of our configuration object,

Here we use some of the Spring apis, using

ObjectUtils.nullSafeEquals("superUser", beanName)
Copy the code

This method means that it is safe to allow both arguments to be null. And then a little bit more rigorously here, to judge,

SuperUser.class.equals(beanClass)
Copy the code

If yes, override the superUser Bean, otherwise return null, and keep the spring container assembling it.

/ * * *... * @return the bean object to expose instead of a default instance of the target bean, * or {@code null} to proceed with default instantiation * ... */ @Nullable default Object postProcessBeforeInstantiation(Class<? > beanClass, String beanName) throws BeansException { return null; }Copy the code

If the return is null, the default initialization is performed, and the Spring container is instantiated.

Next, you need to pass this BeanPostProcessor to the BeanFactory. There are two ways to do this, and this section uses one of them.

Previously InstantiationAwareBeanPostProcessor is a BeanPostProcessor, therefore no burden, when I was here to add the add directly.

Find out the call stack InstantiationAwareBeanPostProcessor postProcessBeforeInstantiation method

Now we’re going to run it and see how it works:

When you see the console output, all of the properties of the SuperUser are empty, so the reason why they are empty is because you intercepted them. With this performance, we can analyze the source code and make bold guesses.

When postProcessBeforeInstantiation method returns a non-null object, instantiate the initialization of logic behind me all all don’t go,

So we can analyze the Alt + F7 postProcessBeforeInstantiation method invocation logic, pay attention to the version

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInstantiation

The call stack to see it, there will be a method, applyBeanPostProcessorsBeforeInstantiation, Can see method is actually traversal within the BeanPostProcessor, to see whether there is the realization of the InstantiationAwareBeanPostProcessor in, if there is a transformation and then do

Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
Copy the code

Here if the result is not equal to empty, then return directly, so in short applyBeanPostProcessorsBeforeInstantiation method, in fact, it will also be other local calls, again upwards.

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation

See the resolveBeforeInstantiation, find up again,

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])

Notice what method is this? CreateBean (), which is the core method for creating a Bean. Here pay attention to mark out the if judgment, that is to say resolveBeforeInstantiation this method if returned to a non-empty, did not go down below, Object beanInstance = doCreateBean(beanName, mbdToUse, args);

Otherwise I’m going to go ahead and create it, so I’m going to put a break point here.

On postProcessBeforeInstantiation method also make a breakpoint

debugger:

First here is dependent on search so Bean load is still in looking for order, we find the User object first, then the object without postProcessBeforeInstantiation intercept method, Know internal resolveBeforeInstantiation method we go after this premise,

ResolveBeforeInstantiation method inside there is nothing to say, inside applyBeanPostProcessorsBeforeInstantiation continue to go again

Began to traverse BeanPostProcessor can see here, and then get to the custom MyInstantiationAwareBeanPostProcessor, then

Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
Copy the code

Rewrite the into postProcessBeforeInstantiation method

Since it is not a SuperUser Bean, this will not be intercepted and null will be returned, which corresponds to our analysis above.

So return null all the way up to the createBean method, and then doCreateBean will cover the rest of the chapter, which will not be covered here.

Then there is the dependency search for the SuperUser, so with the above steps, there is no doubt that the SuperUser will be intercepted

You can see that the SuperUser is returned directly, and the doCreateBean method doesn’t go anywhere.

So this is a non-mainstream operation, which can be used to generate some of our proxy objects in advance, for example if it is a remote JRPC or a remote RPC operation, this is helpful because the local class does not have remote capability and can intercept in this way.

Of course, you need to make some judgments in this class.

conclusion

Through a simple example, we know that the use of a basic InstantiationAwareBeanPostProcessor way,

Through its postProcessBeforeInstantiation method, before instantiating a front-facing operations, access to the object to instantiate the Bean’s agent to replace the target Bean, can effectively prevent the container instantiation of target Bean by default.

If the return is null, the default initialization is performed, and the Spring container is instantiated.

Then echoed our title, Bean instantiation can be bypassed, through InstantiationAwareBeanPostProcessor postProcessBeforeInstantiation method.