Netease Interview: How many ways are there to put beans into the Spring container?

Yesterday, a classmate went to Netease for an interview, and came back to me and told me that he was asked: What are the ways to put beans in the Spring container? This classmate said that he answered three kinds of answers, but he always felt that his answers were not very beautiful, so I will summarize them below.

When we usually use Spring in development, we hand over beans to Spring for management.

So what are the ways to add an object to the Spring container?

1,@Configuration + @Bean

In fact, this method has been introduced in the previous 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 a bean and add it to Spring in the container. The specific code is as follows:

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

2,@Componet + @ComponentScan

This method is also the way we use more. @Componet is translated into a component in Chinese, placed on the class name, and then @ComponentScan is placed on our configuration class, and then you can specify a path to scan with @Componet annotations bean, and then added 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 = "com.springboot.initbean.*")
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 the Person is successfully placed in the IOC container.

3. @ImportAnnotation import

The first two methods may be used by many people, and they must be known in normal development. The @Import annotation may not be used very much, but it is also very important. It is often used when expanding Spring. It is often used. Use it with custom annotations and import a configuration file into the container. Regarding the @Import annotation, I will introduce a little more, it has four ways to use it. This is the source code of the @Import annotation, which means that it can only be placed on a class.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
 
    /**
   * 用于导入一个class文件
     * {@link Configuration @Configuration}, {@link ImportSelector},
     * {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
     */
    Class<?>[] value();
 
}

3.1  @ImportImport 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 + '\'' +
                '}';
    }
}
/**
* 直接使用@Import导入person类,然后尝试从applicationContext中取,成功拿到
**/
@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 directly uses @Import to import a class, and then it is automatically placed in the IOC container. Note that we don't need any annotations on our Person class, just import it directly.

3.2 @Import + ImportSelector

In fact, in the source code of the @Import annotation, it is very clear. If you are interested, you can read it. We implement an ImportSelector interface, and then implement the methods in it to import.

@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[]{"com.springboot.pojo.Person"};
    }
}

I customized a MyImportSelector to implement the ImportSelector interface, rewrite the selectImports method, and then write the fully qualified name of the class we want to import in it, which is very simple to implement.

3.3 @Import + ImportBeanDefinitionRegistrar

This method also requires us to implement the methods in the ImportBeanDefinitionRegistrar interface. 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) {
        // 构建一个beanDefinition, 关于beanDefinition我后续会介绍,可以简单理解为bean的定义.
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
        // 将beanDefinition注册到Ioc容器中.
        registry.registerBeanDefinition("person", beanDefinition);
    }
}

The above implementation is actually similar to the second method of Import, all need to implement the interface and then import it. I came into contact with a new concept, BeanDefinition, which can be simply understood as the definition of a bean (the metadata of the bean), which also needs to be managed in the IOC container. First, there is the metadata of the bean, and then the applicationContext is created according to the metadata of the bean. Bean.

3.4 @Import + DeferredImportSelector

This method also requires us to implement the interface. In fact, it is similar to the second method of @Import. DeferredImportSelector is a sub-interface of ImportSelector, so the implementation method is the same as the second method. It's just that Spring handles it differently. It is related to the delayed import of the automatic import configuration file in Spring Boot, which is very important. It is used 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) {
        // 也是直接将Person的全限定名放进去
        return new String[]{Person.class.getName()};
    }
}

Regarding @Importthe use of annotations, there are about the above three types. Of course, it can also be used with @Configurationannotations to import a configuration class.

4. Use the FactoryBeaninterface

FactoryBeanDo not confuse the interface and the interface BeanFactory. In fact, it can be roughly distinguished from the name FactoryBean. If the suffix is ​​bean, then it is actually a bean  BeanFactory. As the name suggests, the bean factory is the top-level interface of the IOC container. These two interfaces are 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> {
 
    /**
     *  直接new出来Person进行返回.
     */
    @Override
    public Person getObject() throws Exception {
        return new Person();
    }
    /**
     *  指定返回bean的类型.
     */
    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }
}

The above code, @Configuration + @Beanthe method I use will be  PersonFactoryBean added to the container. Note that I did not inject Person into the container, but directly injected it  PersonFactoryBean and then took the Person type bean from the container and ran it successfully.

5. Use BeanDefinitionRegistryPostProcessor

In fact, this method is also used  . The  method that BeanDefinitionRegistrywill be executed when the Spring container starts  , which probably means that after the beanDefinition is loaded, the beanDefinition is post-processed, and the beanDefinition in the IOC container can be adjusted here, so as to interfere with the later. To initialize the bean.BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry ()

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 the person in the beanDefinitionRegistry, BeanDefinitionand finally successfully added the person to the applicationContext.

Summarize

There are several ways to add beans to the spring container.

  • @Configuration + @Bean

  • @ComponentScan + @Component

  • @Import Import with the interface

  • useFactoryBean

  • Implement BeanDefinitionRegistryPostProcessorpost-processing.

Guess you like

Origin blog.csdn.net/m0_71777195/article/details/127035757