首页 归档 关于 learn love 工具

the ways to inject beans into Spring container

Have you always been using @Service, @Component, @Contoller or @ Bean to add your Spring beans?

Have you seen some of the others’ code that looks fancy in injecting the beans but don’t know how it achieves the same purpose?

Let’s take a look at some common ways to inject Spring beans into the container.

1. @Configuration + @Bean

This might be the most common one.
@ Configuration declares a configuration class and we can use @ Bean to add the bean into the container.

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

2. @Component + @ComponentScan

For the same example, we can declare the Person object as a Component. We will then use @ComponentScan “basePackages” to declare the paths for the components.

@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);
    }
}

3. @Import

There are various flexible ways to use this annotation.

3.1 Direct import bean

Direct import bean. Once we are done constructing the Person class, we can then import the class into the Spring container.

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.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);
    }
}

3.2 @Import + ImportSelector

We can also define a MyImportSelector to realize ImportSelector interface and override selectImports method, in which we will specify beans we need.

Now we only need to import the MyImportSelector class.

@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"};
    }
}

3.3 @Import + ImportBeanDefinitionRegistrar

For this method, we also need to realize an interface ImportBeanDefinitionRegistrar.

@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) {
        // construct beanDefinition
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
        // register the bean into the  container
        registry.registerBeanDefinition("person", beanDefinition);
    }
}

The difference between this method and the previous one is an additional declaration of “beanDefinition” — the metadata of the bean.

4 @FactoryBean

FactoryBean is a bean itself, which, by the way, not to be confused with BeanFactory that manages beans.

@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> {

    @Override
    public Person getObject() throws Exception {
        return new Person();
    }

    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }
}

Here we use method 1 — @Configuration + @Bean to add PersonFactoryBean into the Spring container. However, notice that I didn’t inject Person bean directly in @Bean. Instead, PersonFactoryBean is injected. Spring container then gets the Person bean from the factory bean.

5 BeanDefinitionRegistryPostProcessor

When Spring container executes postProcessBeanDefinitionRegistry method in BeanDefinitionRegistryPostProcesso, it will process the beanDefinition separately, which we can use to adjust the beanDefinition.

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 {
 
    }
}