Five Ways to Add bean to Spring Container

  • 2021-10-24 22:51:09
  • OfStack

Directory @ Configuration + @ Bean
@Componet + @ComponentScan
@ Import annotation import
@ Import Importing Classes Directly
@Import + ImportSelector
@Import + ImportBeanDefinitionRegistrar
@Import + DeferredImportSelector
Use the FactoryBean interface
Using BeanDefinitionRegistryPostProcessor
Summary

We know that when we use Spring in development, we all hand over objects to Spring to manage, so add 1 object to Spring container. What are the ways? I will summarize 1 below

@Configuration + @Bean

In fact, this method has been introduced in the last article, and it is also the most commonly used method. @ Configuration is used to declare a configuration class, and then @ Bean annotation is used to declare an bean and add it to the Spring container.
The specific code is as follows:


@Configuration
public class MyConfiguration {
    @Bean
    public Person person() {
        Person person = new Person();
        person.setName("spring");
        return person;
    }
}

@Componet + @ComponentScan

This is also the way we use more. @ Componet is translated into components in Chinese and placed on the class name, then @ ComponentScan is placed on our configuration class, and then we can specify a path, scan bean with @ Componet annotation, and then add it to the container.

The specific code is as follows:


@Component
public class Person {
    private String name;

    public String getName() {

        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

@ComponentScan(basePackages = "it.chusen.spring.*")
public class Demo1 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}

Result output:

Person{name='null'}

Indicates that Person was successfully placed in the IOC container.

@ Import Annotation Import

The first two methods may be used more by everyone, which must be known in normal development. @ Import annotations may not be used much, but they are also very important. They are often used when expanding Spring. They are often used with custom annotations, and then import a configuration file into the container. As for @ Import annotation, I will introduce one more point. It has three ways to use it, and I will introduce it 11 times. This is the source code of @ Import annotation, which means that it can only be placed on classes.


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

    /**
   *  Used to import 1 A class Documents 
     * {@link Configuration @Configuration}, {@link ImportSelector},
     * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
     */
    Class<?>[] value();

}

@ Import Importing Classes Directly

The code example is as follows:


public class Person {
    private String name;

    public String getName() {

        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}
/**
*  Direct use @Import Import person Class, and then try to start from the applicationContext Take it, get it successfully 
**/
@Import(Person.class)
public class Demo1 {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}

The above code imports a class directly using @ Import and is automatically placed in the IOC container. Attention
We don't need any annotations on our Person class, just import it directly.

@Import + ImportSelector

In fact, @ Import annotation in the source code, said has been very clear, interested can see, we achieve an ImportSelector interface, and then implement the method, import.

The code is as follows:


@Import(MyImportSelector.class)
public class Demo1 {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}

class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"it.chusen.spring.model.Person"};
    }
}

I customized an MyImportSelector implementation of selectImports method, and then we want to import the fully qualified name of the class can be written in it, the implementation is also very simple.

@Import + ImportBeanDefinitionRegistrar

This method also requires us to implement the method in ImportBeanDefinitionRegistrar, and the specific code is as follows:


@Import(MyImportBeanDefinitionRegistrar.class)
public class Demo1 {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}

class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //  Build 1 A beanDefinition,  About beanDefinition I will introduce it later, which can be simply understood as bean Definition of .
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
        //  Will beanDefinition Register to Ioc In a container .
        registry.registerBeanDefinition("person", beanDefinition);
    }
}

In fact, the above implementation is similar to the second way of Import, which requires implementing interfaces and then importing them. I came into contact with a new concept, BeanDefinition, which can be simply understood as the definition of bean (metadata of bean), and also needs to be managed in IOC container. First, there is metadata of bean, and then applicationContext creates Bean according to metadata of bean.

@Import + DeferredImportSelector

This way also requires us to implement the interface. In fact, it is similar to the second way of @ Import. DeferredImportSelector is a sub-interface of ImportSelector, so the implementation method is no different from the second way. It's just that Spring is handled differently, which is related to the automatic import configuration file delayed import in Spring Boot, which is very important. Use as follows:


@Import(MyDeferredImportSelector.class)
public class Demo1 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}
class MyDeferredImportSelector implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //  It is also directly Person Put the fully qualified name of 
        return new String[]{Person.class.getName()};
    }
}

There are only three ways to use @ Import annotation. Of course, it can also be used with @ Configuration annotation to import a configuration class.

Use the FactoryBean interface

Don't confuse FactoryBean interface with BeanFactory. It can be roughly distinguished from its name. FactoryBean and suffix are bean, so it is actually an bean and BeanFactory. As the name implies, bean factory is the top interface of IOC container. Both interfaces are actually very important.
Code example:


@Configuration
public class Demo1 {
    @Bean
    public PersonFactoryBean personFactoryBean() {
        return new PersonFactoryBean();
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}

class PersonFactoryBean implements FactoryBean<Person> {

    /**
     *   Direct new Come out Person Go back .
     */
    @Override
    public Person getObject() throws Exception {
        return new Person();
    }
    /**
     *   Specifies the return bean Type of .
     */
    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }
}

In the above code, I added PersonFactoryBean to the container using @ Configuration + @ Bean. Note that instead of injecting Person into the container, I injected PersonFactoryBean directly and then took Person from the container and ran successfully.

Using BeanDefinitionRegistryPostProcessor

In fact, BeanDefinitionRegistry is also used in this way. When Spring container starts, postProcessBeanDefinitionRegistry method of BeanDefinitionRegistryPostProcessor will be executed, which roughly means that after beanDefinition is loaded, beanDefinition will be post-processed, and beanDefinition in IOC container can be adjusted here, thus interfering with the initialization of bean later.

The specific code is as follows:


public class Demo1 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        MyBeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor = new MyBeanDefinitionRegistryPostProcessor();
        applicationContext.addBeanFactoryPostProcessor(beanDefinitionRegistryPostProcessor);
        applicationContext.refresh();
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}

class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
        registry.registerBeanDefinition("person", beanDefinition);
    }
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

In the above code, we manually registered BeanDefinition for person to beanDefinitionRegistry. Finally, person was successfully added to applicationContext. I will introduce the specific principles of the above methods later.

Summary

Several ways of adding bean into spring container are introduced.

@Configuration + @Bean @ComponentScan + @Component @ Import import with interface Use FactoryBean. Implement BeanDefinitionRegistryPostProcessor for post-processing.

In fact, there are many ways to add bean to Spring, and these are briefly introduced here.


Related articles: