0

I am studying the internal structure of Spring and trying to understand the stages of the life cycle of beans, how and when they are created and so on.

Spring provides us with 2 interfaces for configuring bins in context:

BeanPostProcessor

BeanFactoryPostProcessor

There is such a definition and a picture (i took this paragraph from here):

A bean implementing BeanFactoryPostProcessor is called when all bean definitions will have been loaded, but no beans will have been instantiated yet. This allows for overriding or adding properties even to eager-initializing beans. This will let you have access to all the beans that you have defined in XML or that are annotated (scanned via component-scan).

enter image description here

Now I want to test it, here is my simple project:

controller/
├─ HomeController.java
SpringIOCApplication.java

@SpringBootApplication
public class SpringIOCApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringIOCApplication.class, args);
    }
}

@Controller
public class HomeController {

    @GetMapping("/home")
    @ResponseBody
    public String test() {
        return "Hello";
    }
}

Now I run debug:

SpringApplication.java

public ConfigurableApplicationContext run(String... args) {
      ...
      ...
    prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); //309
    refreshContext(context); //310
      ...
      ...
}

The first interesting stop, the method prepareContext(), puts beanDefinition for my bin "springIOCApplication" in context, and a couple of other BeanDefinitions.

beanDefinitionMap =
"packege.internalConfigurationAnnotationProcessor" : ...
"package.internalEventListenerFactory" : ...
"package.internalEventListenerProcessor" : ...
"package.internalAutowiredAnnotationProcessor" : ...
"package.internalCommonAnnotationProcessor" : ...
"springIOCApplication" : ...

Next is the refreshContext(context); method, the invoke of which will create a BeanDefiniotn for all my bins and all system ones (there are more than 150 of them).

This is due to invocation this method:

AbstractApplicationContext.java
...
public void refresh() {
    ...
    invokeBeanFactoryPostProcessors(beanFactory); // 565 right here
    ...
}

That is, the quote I gave above has already been violated, BeanFactoryPostProcessor was invoked before all BeanDefinitions for my project were created. Why?

If you go a little from the other side.

If we take the @SpringBootApplication annotation, it contains the @ComponentScan annotation. The Component Scan Annotation Parser class parses the @ComponentScan annotation and creates a BeanDefiniton list based on the metadata in it (it delegates the creation process to the ClassPathBeanDefinitionScanner class, but we are not interested in this),

If we put a breakpoint here and examine the stack trace, we will see the following:

ComponentScanAnnotationParser.java

public Set<BeanDefinitionHolder> parse(...) {
        ClassPathBeanDefinitionScanner scanner ... //69 right here
}

parse:69, ComponentScanAnnotationParser (org.springframework.context.annotation)
doProcessConfigurationClass:289, ConfigurationClassParser (org.springframework.context.annotation)
processConfigurationClass:243, ConfigurationClassParser (org.springframework.context.annotation)
parse:196, ConfigurationClassParser (org.springframework.context.annotation)
parse:164, ConfigurationClassParser (org.springframework.context.annotation)
processConfigBeanDefinitions:398, ConfigurationClassPostProcessor (org.springframework.context.annotation)
postProcessBeanDefinitionRegistry:283, ConfigurationClassPostProcessor (org.springframework.context.annotation)
invokeBeanDefinitionRegistryPostProcessors:344, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:115, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:747, AbstractApplicationContext (org.springframework.context.support)
refresh:565, AbstractApplicationContext (org.springframework.context.support)
refresh:146, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:732, SpringApplication (org.springframework.boot)
refreshContext:434, SpringApplication (org.springframework.boot)
run:310, SpringApplication (org.springframework.boot)
run:1304, SpringApplication (org.springframework.boot)
run:1293, SpringApplication (org.springframework.boot)
main:12, SpringIOCApplication (com.edu.springioc)

We see that the invocation came from the ConfigurationClassPostProcessor which in turn is the BeanFactoryPostProcessor.

That is, BeanFactoryPostProcessor, which should be invoked after creating BeanDefinition, in fact creates these BeanDefinition itself. Why is that? It's confusing to me.

What happens if I register a BeanDefinition, for a component marked with the @Configuration and @ComponentScan annotation, already inside the BeanPostProcessor?

Will it force you to re-start the whole cycle that is drawn in the picture?

Can it happen that any BeanFactoryPostProcessor will be called after at least one BeanPostProcessor is called?

Santa Monica
  • 332
  • 3
  • 11

0 Answers0