I heard that there is a shortcut to create Spring Bean?

In the Spring Bean creation method, there is the following code:

AbstractAutowireCapableBeanFactory#createBean:

@Override 
protected Object createBean (String beanName, RootBeanDefinition mbd, @Nullable Object[] args) 
        throws BeanCreationException {
     //... 
    try {
         // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. 
        Object  bean  = resolveBeforeInstantiation (beanName, mbdToUse);
         if (bean != null ) {
             return bean;
        }
    }
    try {
         Object  beanInstance  = doCreateBean(beanName, mbdToUse, args);
         return beanInstance;
    }
    //... 
}

The Bean creation logic we usually talk about refers to the logic in the doCreateBean method. In Song Ge’s previous articles, whenever it comes to the Bean creation process, I am also talking about the process of the doCreateBean method.

But friends, please note that before the doCreateBean method is executed, there is actually a resolveBeforeInstantiation method that will be executed first, and this method may directly generate a Bean! If this method directly generates a Bean, the logic in the doCreateBean method will not take effect.

So what is the significance of the resolveBeforeInstantiation method? In fact, you can probably get some clues from the comments on this method:

Gives the BeanPostProcessor a chance to create a proxy object and use this proxy object in place of the target Bean.

1. resolveBeforeInstantiation

Friends who look at the source code below must first understand the two similar words, otherwise they will be confused after reading:

  • Instantiation: Instantiation, from Class to Bean is instantiation.
  • Initialization: Initialization, making various configurations for the Bean is initialization.

To understand these two words, let’s look at the source code.

First, let me sort out the resolveBeforeInstantiation method with my friends.

AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation:

@Nullable 
protected Object resolveBeforeInstantiation (String beanName, RootBeanDefinition mbd) {
     Object  bean  =  null ;
     if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
         // Make sure bean class is actually resolved at this point. 
        if (!mbd .isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            Class<?> targetType = determineTargetType(beanName, mbd);
            if (targetType != null ) {
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                if (bean != null ) {
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
        }
        mbd.beforeInstantiationResolved = (bean != null );
    }
    return bean;
}

Friends, take a look, here is a judgment condition, mbd.isSynthetic is to judge whether it is a synthetic bean, this is generally defined by Spring, our custom beans generally do not fall into this category, and then the hasInstantiationAwareBeanPostProcessors method follows It is to determine whether there is currently a post-processor of type InstantiationAwareBeanPostProcessor. If it exists, enter the if branch.

If we want to complete the Bean processing in the resolveBeforeInstantiation method, then we need to provide a post-processor of type InstantiationAwareBeanPostProcessor ourselves.

Next, two methods will be called:

  • applyBeanPostProcessorsBeforeInstantiation: As you can see from the name, this is a method that is triggered before instantiation, so the parameter of this method is still Class because it has not been instantiated yet.
  • applyBeanPostProcessorsAfterInitialization: As you can see from the name, this is a method that starts after initialization, so the parameter of this method is Bean, because the initialization has been completed at this time.

1.1 applyBeanPostProcessorsBeforeInstantiation

@Nullable 
protected Object applyBeanPostProcessorsBeforeInstantiation (Class<?> beanClass, String beanName) {
     for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
         Object  result  = bp.postProcessBeforeInstantiation(beanClass, beanName);
         if (result != null ) {
             return result ;
        }
    }
    return  null ;
}

This place is very simple, it is to execute the postProcessBeforeInstantiation method of the post-processor of type InstantiationAwareBeanPostProcessor.

1.2 applyBeanPostProcessorsAfterInitialization

@Override 
public 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 is the postProcessAfterInitialization method that executes BeanPostProcessor.

So the source code of this piece is actually not difficult, the reason is very simple.

1.3 Case

Brother Song wrote a simple case, let’s take a look.

Suppose I have a BookService as follows:

public  class  BookService {

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

Then I create a post-processor of type InstantiationAwareBeanPostProcessor and override the postProcessBeforeInstantiation and postProcessAfterInitialization methods mentioned earlier:

public  class  MyInstantiationAwareBeanPostProcessor  implements  InstantiationAwareBeanPostProcessor {
     @Override 
    public Object postProcessBeforeInstantiation (Class<?> beanClass, String beanName)  throws BeansException {
         if (beanClass == BookService.class) {
             Enhancer  enhancer  =  new  Enhancer ();
            enhancer.setSuperclass(beanClass);
            enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
                String  name  = method.getName();
                System.out.println(name + "The method started executing..." );
                 Object  invoke  = proxy.invokeSuper(obj, args);
                System.out.println(name + "Method execution ended..." );
                 return invoke;
            });
            BookService  bookService  = (BookService) enhancer.create();
             return bookService;
        }
        return InstantiationAwareBeanPostProcessor. super .postProcessBeforeInstantiation(beanClass, beanName);
    }

    @Override 
    public Object postProcessAfterInitialization (Object bean, String beanName)  throws BeansException {
        System.out.println( "bean.getClass() ========= " + bean.getClass());
         return InstantiationAwareBeanPostProcessor. super .postProcessAfterInitialization(bean, beanName);
    }
}

In the postProcessBeforeInstantiation method, if the bean to be created is BookService, a CGLIB proxy object is created through Enhancer. If it is the creation of other beans, just call the parent class method. After rewriting in this way, the result obtained in section 1.1 will be a proxy BookService object.

In the postProcessAfterInitialization method, I did not do any additional processing, just printed the Bean we got. At this time, the Bean we got is actually the proxy object generated by the previous postProcessBeforeInstantiation method, and then the parent class method is called here to return. In fact, Just return the parameter Bean intact.

Finally, register these two beans into the Spring container:

<?xml version= "1.0" encoding= "UTF-8" ?> 
< beans  xmlns = "http://www.springframework.org/schema/beans" 
       xmlns:xsi = "http://www.w3.org /2001/XMLSchema-instance" 
       xsi:schemaLocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > 
    < bean  class = "org.javaboy.bean.aop2.BookService"  id = "bookService" /> 
    < bean  class = "org.javaboy.bean.aop2.MyInstantiationAwareBeanPostProcessor" 
          id = "myInstantiationAwareBeanPostProcessor" /> 
</ beans >

Then initialize the Spring container:

ClassPathXmlApplicationContext  ctx  =  new  ClassPathXmlApplicationContext ( "aop2.xml" );
 BookService  bs  = ctx.getBean(BookService.class);
System.out.println( "bs.getClass() = " + bs.getClass());
bs.hello();

The final execution result is as follows:

It can be seen that the BookService finally obtained is a proxy object. From the source code level, this proxy object is generated in the resolveBeforeInstantiation method, and the subsequent doCreateBean method is not actually executed.

This is the role of the resolveBeforeInstantiation method. It actually gives the BeanPostProcessor a chance to create a proxy object and use this proxy object to replace the target Bean.

2. Source code practice

Why does Brother Song pay attention to this method?

If any friends have studied the Spring AOP source code, they will find that this method has a place to use when dealing with Spring AOP.

When we are in Spring AOP, we often define aspects through the following code:

@Component 
@Aspect 
@EnableAspectJAutoProxy 
public  class  LogAspect {
     //... 
}

There is an @Aspect annotation on this class, so the question is, how does Spring identify that this is an aspect rather than an ordinary Bean?

The answer is in the applyBeanPostProcessorsBeforeInstantiation method in Section 1.1. This method will traverse all post-processors of the InstantiationAwareBeanPostProcessor type. InstantiationAwareBeanPostProcessor has a subclass called AnnotationAwareAspectJAutoProxyCreator. In this processor, LogAspect is recognized as an aspect.

The specific identification method is as follows:

First call the postProcessBeforeInstantiation method of AnnotationAwareAspectJAutoProxyCreator (actually the method in the parent class AbstractAutoProxyCreator of AnnotationAwareAspectJAutoProxyCreator):

@Override 
public Object postProcessBeforeInstantiation (Class<?> beanClass, String beanName) {
     Object  cacheKey  = getCacheKey(beanClass, beanName);
     if (!StringUtils.hasLength(beanName) || ! this .targetSourcedBeans.contains(beanName)) {
         if ( isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
             this .advisedBeans.put(cacheKey, Boolean.FALSE);
             return  null ;
        }
    }
    //... 
}

Starting from this place, it is divided into two lines:

  1. If it is an aspect bean, executing the first method isInfrastructureClass can return true.
  2. If it is a normal Bean, the first method will return false, and the second method shouldSkip will be executed (although this method will also return false), but this method has some other values ​​​​in it.

2.1 Section Bean

Let’s first look at the isInfrastructureClass method, and first look at how aspect beans are processed.

I extracted part of this method. We focus on the isInfrastructureClass method. This method is used to determine whether the current class is an Aspect:

@Override 
protected  boolean  isInfrastructureClass (Class<?> beanClass) {
     return ( super .isInfrastructureClass(beanClass) ||
            ( this .aspectJAdvisorFactory != null && this .aspectJAdvisorFactory.isAspect(beanClass)));
}

The judgment here is mainly based on two aspects:

  1. Call the method of the parent class to determine whether the current class is related to AOP:
protected  boolean  isInfrastructureClass (Class<?> beanClass) {
     boolean  retVal  = Advice.class.isAssignableFrom(beanClass) ||
            Pointcut.class.isAssignableFrom(beanClass) ||
            Advisor.class.isAssignableFrom(beanClass) ||
            AopInfrastructureBean.class.isAssignableFrom(beanClass);
    return retVal;
}

I don’t need to explain this, these are all our old acquaintances in AOP.

  1. Call aspectJAdvisorFactory.isAspectthe method to determine whether the current class contains the @Aspect annotation:

AbstractAspectJAdvisorFactory#isAspect

@Override 
public  boolean  isAspect (Class<?> clazz) {
     return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
private  boolean  hasAspectAnnotation (Class<?> clazz) {
     return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null );
}

This code is easy to understand, it just checks whether the current class has @Aspectannotations. The latter compiledByAjc method is to check whether ajc compilation is required. We generally do not need this, so as long as the hasAspectAnnotation method returns true, it will return true as a whole.

If our class contains @Aspectannotations, the current class name will eventually be added to the advisedBeans Map. In the advisedBeans Map, the key is the name of the current Bean, and the value is false, which is a mark indicating that the current class does not need to be generated. Agent class.

This is the general logic executed by the isInfrastructureClass method.

2.2 Common Beans

If it is an ordinary Bean, it is obvious that the isInfrastructureClass method will return false, which will cause the shouldSkip method to be executed. Although this method is called shouldSkip, it does a lot of practical things.

I will explain this method in detail when I share the AOP creation process with my friends in the next article. Let me start by saying that this method will collect various Aspect Beans and generate Advisor based on these Beans in the future.

Okay, this is the role of the resolveBeforeInstantiation method. Interested friends can DEBUG and see some by themselves~