In-depth Spring principles-1.The difference between BeanFactory and ApplicationContext

  • About the author: Hello everyone, I am Tudou Ni who loves cheese. I am a Java contestant in the 24th class of school admissions. Nice to meet you all.
  • Series of columns: Spring source code, JUC source code
  • If you feel that the blogger’s article is not bad, please support the blogger three times in a row.
  • The blogger is working hard to complete the 2023 plan: source code tracing to find out.
  • Contact information: nhs19990716, add me to the group, let’s learn together, make progress together, and fight against the Internet winter together

Article directory

    • Container interface
    • BeanFactory
      • Calling sequence
    • ApplicationContext
    • BeanFactory VS ApplicationContext

Container interface

Take the startup of a SpringBoot project as an example

public class Application{<!-- -->
public static void main(String[] args){<!-- -->
SpringApplication.run(Application.class);
}
}

When we click run to enter the inside of the method

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {<!-- -->
        return run(new Class[]{<!-- -->primarySource}, args);
    }

There is actually a return value, and the return value type is ConfigurableApplicationContext. When you open its class diagram structure, you will find this:

Ctrl + Alt + U (how to open a class diagram)

You can see that ConfigurableApplicationContext is a sub-interface, ApplicationContext is a parent interface, and BeanFactory is the parent interface of ApplicationContext.

When printing the return value of the ConfigurableApplicationContext type, you will find that there is a singletionObjects in its beanFactory.

In fact, that is to say, after the loading of ConfigurableApplicationContext is completed, the corresponding singletonObjects in the beanFactory are also loaded. This is a great advantage of ApplicationContext, which enables automatic loading.

It can also be seen from the above figure that BeanFactory and ApplicationContext are not just a simple interface inheritance relationship. ApplicationContext combines and extends the functions of BeanFactory, such as MessageSource’s ability to handle international resources, ResourcePatternResolver’s wildcard matching resource ability (disk path class path File found), ApplicationEventPublisher (publish event object), EnvironmentCapable (read environment information and environment variables in Spring)

System.out.println(context.getMessage("hi", null, Locale.CHINA));
        System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
        System.out.println(context.getMessage("hi", null, Locale.JAPANESE));

        System.out.println("--------------------------------------------- ----------------");

        Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
        for (Resource resource : resources) {<!-- -->
            System.out.println(resource);
        }

        System.out.println("--------------------------------------------- ----------------");

        System.out.println(context.getEnvironment().getProperty("java_home"));
        System.out.println(context.getEnvironment().getProperty("server.port"));

        System.out.println("--------------------------------------------- ----------------");


        context.getBean(Component1.class).register();
        
@Component
public class Component1 {<!-- -->

    private static final Logger log = LoggerFactory.getLogger(Component1.class);

    @Autowired
    private ApplicationEventPublisher context;

    public void register() {<!-- -->
        log.debug("User Registration");
        context.publishEvent(new UserRegisteredEvent(this));
    }

}

@Component
public class Component2 {<!-- -->

    private static final Logger log = LoggerFactory.getLogger(Component2.class);

    @EventListener
    public void aaa(UserRegisteredEvent event) {<!-- -->
        log.debug("{}", event);
        log.debug("Send SMS");
    }
}

BeanFactory

When we double-clicked the BeanFactory to enter, ctrl + f12 found:

I found that on the surface there is only getBean, but in fact control inversion, basic dependency injection, and various functions up to the Bean life cycle are actually provided by its implementation class.

Enter its implementation class (ctrl + N) DefaultListableBeanFactory and find that it impl ConfigurableListableBeanFactory, and ConfigurableListableBeanFactory impl ListableBeanFactory.

For example, create a DefaultListableBeanFactory object in code as follows:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

Register a set of configurations

//bean definition (class, scope, initialization, destruction)
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();

beanFactory.registerBeanDefinition("config", beanDefinition);

@Configuration
    static class Config {<!-- -->
        @Bean
        public Bean1 bean1() {<!-- -->
            return new Bean1();
        }

        @Bean
        public Bean2 bean2() {<!-- -->
            return new Bean2();
        }
    }

 static class Bean1 {<!-- -->
        private static final Logger log = LoggerFactory.getLogger(Bean1.class);

        public Bean1() {<!-- -->
            log.debug("Construct Bean1()");
        }

        @Autowired
        private Bean2 bean2;

        public Bean2 getBean2() {<!-- -->
            return bean2;
        }
    }

    static class Bean2 {<!-- -->
        private static final Logger log = LoggerFactory.getLogger(Bean2.class);

        public Bean2() {<!-- -->
            log.debug("Construct Bean2()");
        }
    }

At this time, beanFactory.getBeanDefinitionNames() prints out and finds that only config is registered, which means that @Configuration is not parsed. BeanFactory lacks the ability to parse @bean and other annotations, and its functions are incomplete.

Therefore, some commonly used post-processors (mainly configuration related to configuration annotations), BeanFactoryPostProcessor, and BeanPostProcessor should be added:

AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {<!-- -->
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
        });
        
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
                .forEach(beanPostProcessor -> {<!-- -->
            System.out.println(">>>>" + beanPostProcessor);
            beanFactory.addBeanPostProcessor(beanPostProcessor);
        });

At this time, it is found that bean1 and bean2 can be printed out normally, which means that bean1 and bean2 are registered. When we get here, we can actually find that now BeanFactory is very similar to ApplicationContext after we add these post-processors, but it is not enough. Before ApplicationContext was loaded, the singletonObjects in beanFactory were also loaded, and here we are Obviously preloading cannot be achieved, and System.out.println(beanFactory.getBean(Bean1.class).getBean2()); is still needed to perform lazy loading, and ApplicationContext has already implemented preloading, so this should be added:

beanFactory.preInstantiateSingletons(); // Prepare all singletons

At this point, preloading can be achieved, and at this time, BeanFactory has become basically the same as ApplicationContext.

Calling sequence

AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);

As mentioned before, this is actually to add some annotation-related configurations, enter to view and find

def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);

def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);

Among them are the parsing of Autowired and Resource annotations respectively.

In fact, you can see in the code that there is an obvious order. If we sort in the stream, then it is actually the getOrder method in AutowiredAnnotationBeanPostProcessor and CommonAnnotationBeanPostProcessor that is compared.

Therefore, the order of Autowired and Resource is still important.

ApplicationContext

There are many extensions based on ApplicationContext, but as compared before, not only does it not need to introduce Bean post-processor and BeanFactory post-processor like BeanFactory, but it can also be preloaded. Each Bean is initialized after ApplicationContext is started.

As follows:

ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("a02.xml");

for (String name : context.getBeanDefinitionNames()) {<!-- -->
    System.out.println(name);
}

System.out.println(context.getBean(Bean2.class).getBean1());
<!-- Inversion of control, allowing bean1 to be managed by the Spring container -->
    <bean id="bean1" class="com.itheima.a02.A02.Bean1"/>

    <!-- Inversion of control, allowing bean2 to be managed by the Spring container -->
    <bean id="bean2" class="com.itheima.a02.A02.Bean2">
        <!-- Dependency injection, establish dependency with bean1 -->
        <property name="bean1" ref="bean1"/>
    </bean>

Or don’t use the configuration class and use annotations:

AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(Config.class);

for (String name : context.getBeanDefinitionNames()) {<!-- -->
    System.out.println(name);
}

System.out.println(context.getBean(Bean2.class).getBean1());
@Configuration
    static class Config {<!-- -->
        @Bean
        public Bean1 bean1() {<!-- -->
            return new Bean1();
        }

        @Bean
        public Bean2 bean2(Bean1 bean1) {<!-- -->
            Bean2 bean2 = new Bean2();
            bean2.setBean1(bean1);
            return bean2;
        }
    }

BeanFactory VS ApplicationContext

Through the above analysis, we can actually know more clearly where the strength of ApplicationContext is:

  • Automatic BeanPostProcessor registration.
  • Automatic BeanFactoryPostProcessor registration.
  • Convenient MessageSource, ResourcePatternResolver, ApplicationEventPublisher, EnvironmentCapable implementation classes to use.
  • The publishing of ApplicationEvent is different from the lazy loading of BeanFactory. It is preloaded, so each bean is instantiated after the ApplicationContext is started.