将 Bean 注入 Spring 容器的方法

时间:2022-12-18 11:51:54


1. @Configuration + @Bean

@Configuration 来声明1个配置类,
@Bean 注解声明1个bean,将其加入到Spring容器中。

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

2. @Componet + @ComponentScan

@Componet放在类名上面;
@ComponentScan放在配置类上,可以指定一个路径,进行扫描带有@Componet注解的bean,然后加至容器中。

@Component
public class Person {
}
 
@ComponentScan(basePackages = "com.haoqian.beans.*")
public class Demo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        
        Person bean = applicationContext.getBean(Person.class);
    }
}

3. @Import 注解导入

@Import 注解在进行Spring扩展时经常会用到,它经常搭配自定义注解进行使用,然后往容器中导入一个配置文件。

关于@Import注解,它有四种使用方式。@Import注解的源码如下,可以发现它只能放置在类上。

@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 @Import 直接导入类

public class Person {
}


/**
 * 直接使用@Import导入person类
 **/
@Import(Person.class)
public class Demo1 {
 
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        
        Person bean = applicationContext.getBean(Person.class);
    }
}

3.2 @Import + ImportSelector接口

自定义了一个ImportSelector 接口的实现类,重写selectImports方法,然后将要导入bean的全限定类名写在返回值里面即可。

class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.haoqian.beans.Person"};
    }
}


@Import(MyImportSelector.class)
public class Demo1 {
 
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        
        Person bean = applicationContext.getBean(Person.class);
    }
}

3.3 @Import + DeferredImportSelector接口

DeferredImportSelector 它是 ImportSelector 的子接口,所以实现的方法和3.2无异。只是Spring的处理方式不同,它和Spring Boot中的自动导入配置文件 延迟导入有关,非常重要。

class MyDeferredImportSelector implements DeferredImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // 也是直接将Person的全限定名放进去
        return new String[]{Person.class.getName()};
    }
}

@Import(MyDeferredImportSelector.class)
public class Demo1 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        
        Person bean = applicationContext.getBean(Person.class);
    }
}

3.4 @Import + ImportBeanDefinitionRegistrar接口

自定义了一个ImportBeanDefinitionRegistrar接口的实现类,重写registerBeanDefinitions方法,然后将要导入bean的beanDefinition 注入到容器中,容器会创建对应的bean。

class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
 
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 构建一个beanDefinition.
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
        // 将beanDefinition注册到Ioc容器中.
        registry.registerBeanDefinition("person", beanDefinition);
    }
}


@Import(MyImportBeanDefinitionRegistrar.class)
public class Demo1 {
 
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        
        Person bean = applicationContext.getBean(Person.class);
    }
}

@Import 还可以搭配@Configuration注解使用,用于导入一个配置类。


4. FactoryBean接口

FactoryBean接口和BeanFactory千万不要弄混了:

  • FactoryBean, 后缀为bean,那么它其实就是一个bean;
  • BeanFactory,顾名思义 bean工厂,它是IOC容器的*接口;

使用@Configuration + @Bean的方式将 PersonFactoryBean 加入到容器中,注意,没有向容器中注入 Person, 而是直接注入的 PersonFactoryBean 然后可以从容器中拿到Person这个类型的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;
    }
}


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

5. BeanDefinitionRegistryPostProcessor接口

其实这种方式也是利用到了 BeanDefinitionRegistry,在Spring容器启动的时候会执行 BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法,大概意思就是等beanDefinition加载完毕之后,对beanDefinition进行后置处理,可以在此进行调整IOC容器中的beanDefinition,从而干扰到后面进行初始化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 {
 
    }
}

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