Deep parsing of Spring core source code invokeBeanFactoryPostProcessors

Invokebeanfactoryprocessors (Part 2)

In the previous section, we analyzed invokebeanefinitionregistrypostprocessors (currentregistryprocessors, registry); This method has done those things. Now let's continue to analyze the following text, as shown in the figure:

In the above figure, Spring cycles to determine whether there is any missing post processor that has not been parsed. Generally, currentRegistryProcessors will be 0, and we will directly skip it when running the code and continue on:

This is the last time Spring parses the BeanDefinitionRegistryPostProcessor postprocessor. It is mainly aimed at some parent class hierarchical relationships, which are usually 0, so it still has no effect. All right, here's the key code

For invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); Trace ->postprocessbeanfactory (configurablelistablebeanfactory beanfactory). Let's stop the code in the following figure:

Note appconfig Class changes

Here it is still a normal class. Let's debug the next step

It can be found that Spring has enhanced it into a cglib enhanced class, so why is AppConfig enhanced? Let's make a detailed analysis below

@Configuration

When we cancel @Configuration,

We debug the code stopped above

It can be seen that the Configuration class here has not been enhanced, so it can be concluded that our @Configuration class can enhance our common class. Do you still remember what we said about full in the previous chapter? If it is a @Configuration class, Spring will think that it is a Configuration class and will identify it as full. This full represents whether the Configuration class needs to be enhanced. We will prove it in the source code later, Having said so much, I believe readers should be right about @Configuration
Now let's discuss why Spring should enhance the Configuration class. Here we test adding @Configuration and canceling @Configuration. Let's see the following code:

The first case - cancel @Configuration

The second case - < plus @Configuration >

We can see that with @Configuration, the object returned by our user1() and user2() methods is the same object. Of course, there is a third case plus static. Readers can test it by themselves to see what the situation is. As a result, they will find that the proxy function will fail. As for why it will fail, we will explain it in detail in the following source code. OK, we know about the role of @Configuration. How does Spring enhance @Configuration? Let's go back to the source code:

Enter enhanceConfigurationClasses

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
    Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
​
    //Find the config annotation class. Here you can see full and lite. If full becomes an enhanced class, the purpose is to implement the singleton
    for (String beanName : beanFactory.getBeanDefinitionNames()) {
      BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
      if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
        if (!(beanDef instanceof AbstractBeanDefinition)) {
          throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
              beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
        }
        else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
          logger.info("Cannot enhance @Configuration bean definition '" + beanName +
              "' since its singleton instance has been created too early. The typical cause " +
              "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
              "return type: Consider declaring such methods as 'static'.");
        }
        configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
      }
    }
    if (configBeanDefs.isEmpty()) {
      // nothing to enhance -> return immediately
      return;
    }
​
    ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
    for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
      AbstractBeanDefinition beanDef = entry.getValue();
      // If a @Configuration class gets proxied, always proxy the target class
      beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
      try {
        // Set enhanced subclass of the user-specified bean class
        Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
        if (configClass != null) {
          Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
          if (configClass != enhancedClass) {
            if (logger.isTraceEnabled()) {
              logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
                  "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
            }
            beanDef.setBeanClass(enhancedClass);
          }
        }
      }
      catch (Throwable ex) {
        throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
      }
    }
  }

Here (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) will judge whether there is a configuration class that identifies FULL. If there is no following configBeanDefs
It will be returned directly if it is empty. If it is not empty, the configuration class will be enhanced. We will lock this code class <? > Enhancedclass = enhancer Enhance (configclass,
this.beanClassLoader);->Class<?> enhancedClass createClass(newEnhancer(configClass,classLoader));->
Newenhancer (class <? > configsuperclass, @nullable classloader classloader) finally came here:

When making a detailed analysis of @Configuration, the author can first think about how Spring can create objects in different ways after enhancing the Configuration class. Why is there only one Bean stored in the Spring container? If you want to achieve this function, do you need to change the way you create objects? So since our factory, our @Bean Class is to create objects to be managed by Spring. If we first obtain the objects through the factory, can we create objects to be managed by Spring if we can't obtain them? Well, I think the readers should have some ideas. Let's look at another picture

The figure roughly explains what the configuration class will look like after it is transformed into an enhanced class. From the figure, we can see that the enhanced proxy class has a private variable, which stores our factory. I think the reader should have an insight into why this factory is needed. OK, let's go back to the source code:

Here, Spring is enhanced by CGLIB. If readers haven't learned about CGLIB, they'd better learn about the use of CGLIB online. I won't repeat it here. First, give the target class to enhancer, and then add an interface, enhanceconfiguration Class. This class is the core of CGLIB implementation enhancement. If we click this interface, we will find that it implements the BeanFactoryAwae interface. Then we continue to go down and see setStrategy (newBeanFactoryAwareGeneratorStrategy(classLoader)). We click in

As shown in the figure, Spring has added a policy to declare the public attribute of a variable named "$$beanFactory". If we recall whether the interface we added before can get the factory, we are still one step away. How to process the previous raw materials is what our agent needs to do:

The author will not analyze this Callback, because it involves CGLIB knowledge. If the reader is interested, he can study it by himself. Well, we still need one thing after we generate the proxy, that is, how to assign the factory we get to the variable of the proxy class. See the following code

Let's click in

The readers here are suddenly enlightened. Do they feel that Spring design exists like a God? Well, when we understand that
The invokebeanfactoryprocessors method is almost finished. Of course, there are some more details that I will describe as a separate chapter. Hey, it's half done. What's half done?, After all, our real object instances haven't started yet. OK, we'll do some in-depth analysis on the rest of Spring in the next section

Source: wechat official account "seven days 0"

In the next chapter, Spring object instantiation and initialization

Tags: Java Spring Programming Interview JavaEE

Posted by j-dearden on Mon, 30 May 2022 08:33:49 +0530