《Spring系列》第4章 依赖注入的六种方式

时间:2021-05-10 01:20:31

常见注入Spring容器的六种方式

  1. @Configuration + @Bean
  2. @ComponentScan + @Component
  3. @Import 配合接口进行导入
  4. 使用FactoryBean
  5. 实现BeanDefinitionRegistryPostProcessor进行后置处理
  6. Supplier接口,但很少使用

1.@Configuration + @Bean

@Configuration用来声明一个配置类,然后使用 @Bean 注解,用于声明一个bean,将其加入到Spring容器中。这种方式是我们最常用的一种

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

2.@Component + @ComponentScan

@Componet中文译为组件,放在类名上面,然后@ComponentScan放置在我们的配置类上,然后可以指定一个路径,进行扫描带有@Componet注解的bean,然后加至容器中。这种方式也较为常用,spring扫描包路径就是使用这种方式,这样可以一下子扫描很多个bean到容器

// 该类在com.shawn.*包下面
@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.shawn.*")
public class Demo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.class);
        Person bean = applicationContext.getBean(Person.class);
        //结果输出Person{name='null'}
        System.out.println(bean);
    }
}

3.使用FactoryBean接口

1) 什么是FactoryBean

FactoryBean是为IOC容器中的Bean的实现提供了更加灵活的方式,FactoryBean在IOC容器的基础上,给Bean的实现加上了一个简单工厂模式和装饰模式。
​ 一般情况下实例化一个Bean对象:Spring通过反射机制利用的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。为此Spring提供了该接口,用户可以通过实现该接口,然后在getObject()方法中灵活配置,来定制实例化Bean的逻辑。
​ FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现——(xxxFactoryBean)。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。
​ 从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean的形式。

public interface FactoryBean<T> {

   String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
	
   // 返回Bean对象
   @Nullable
   T getObject() throws Exception;

   // 返回Bean类型
   @Nullable
   Class<?> getObjectType();

   // 是否为单例对象
   default boolean isSingleton() {
      return true;
   }

}

2) 使用场景

​ FactoryBean 用来生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。

​ 比如说你有一些同属于某一类型的Bean对象需要被创建,但是它们自己有各自的特点,你只需要把他们的特点注入FactoryBean中,就可以生产出属于该类型的各种实例。

3) 使用示例

【基础代码】

// 第1步:实体类
public class User {
}


// 第2步:创建FactoryBean
@Component
public class MyFactoryBean implements FactoryBean<User> {

    @Override
    public User getObject() throws Exception {
        User user = new User();
        return user;
    }

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

    @Override
    public boolean isSingleton() {
        return true;
    }
}

// 第3步:测试类
@SpringBootTest
public class Test {

    @Autowired
    private ApplicationContext applicationContext;

    @org.junit.jupiter.api.Test
    public void m1(){
        // 获取bean对象
        Object bean1 = applicationContext.getBean("myFactoryBean");
        Object bean2 = applicationContext.getBean("myFactoryBean");
        System.out.println(bean1);
        System.out.println(bean2);

        // 名称签名加&,获取FactoryBean本身
        Object bean3 = applicationContext.getBean("&myFactoryBean");
        Object bean4 = applicationContext.getBean("&myFactoryBean");
        System.out.println(bean3);
        System.out.println(bean4);

    }

}

【测试结果】

// 多次返回的1个对象,代表单例
com.h3c.spring.User@3c54ddec
com.h3c.spring.User@3c54ddec
// 前面加&代表返回的是MyFactoryBean本身
com.h3c.spring.MyFactoryBean@6d69a0d3
com.h3c.spring.MyFactoryBean@6d69a0d3

在上面代码的基础上,将单例改为多例

@Component
public class MyFactoryBean implements FactoryBean<User> {

    @Override
    public User getObject() throws Exception {
        User user = new User();
        return user;
    }

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

    @Override
    public boolean isSingleton() {
        // 这里变成false,代表多例
        return false;
    }
}

【测试结果】

// 每次获取都是创建新的bean
com.h3c.spring.User@1118d539
com.h3c.spring.User@601d6622
// 获取的FactoryBean一直都是1个
com.h3c.spring.MyFactoryBean@76216830
com.h3c.spring.MyFactoryBean@76216830

4.@Import注解导入

@Import注解用到的并不是很多,但是非常重要,在进行Spring扩展时经常会用到。它通过搭配自定义注解进行使用,然后往容器中导入一个配置文件。它有四种使用方式。

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

1) Import直接导入类

直接使用@Import导入了一个类,然后自动的就被放置在IOC容器中了。注意我们的Person类上 就不需要任何的注解了,直接导入即可

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 Demo {
 
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}

2) @Import + ImportSelector

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

@Import(MyImportSelector.class)
public class Demo {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.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.shawn.Person"};
    }
}

3) @Import + ImportBeanDefinitionRegistrar

这种方式需要实现 ImportBeanDefinitionRegistrar 接口中的方法。BeanDefinition可以简单理解为bean的定义(bean的元数据),也是需要放在IOC容器中进行管理的,先有bean的元数据,applicationContext再根据bean的元数据去创建Bean

@Import(MyImportBeanDefinitionRegistrar.class)
public class Demo {
 
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(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);
    }
}

4) @Import + DeferredImportSelector

这种方式也需要我们进行实现接口,其实它和@Import的第二种方式差不多,DeferredImportSelector 它是 ImportSelector 的子接口,所以实现的方法和第二种无异。只是Spring的处理方式不同,它和Spring Boot中的自动导入配置文件延迟导入有关,非常重要

@Import(MyDeferredImportSelector.class)
public class Demo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.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()};
    }
}

5.使用 BeanDefinitionRegistryPostProcessor

利用后置处理器也可以注入Bean,在Spring初始化的时候,执行refresh(),会执行后置处理器,其中就包括BeanDefinitionRegistryPostProcessor

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

   void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

上面方法的参数,会把BeanDefinitionRegistry传过来,这样我们就可以操作IOC容器中的BeanDefinition,但此时IOC容器还没有把全部的BeanDefinition注入进来,所以,我们更多是注入Bean,而不是删除Bean

【测试实例】

下述代码中我们手动向beanDefinitionRegistry中注册了Person的BeanDefinition,最终成功将person加入到applicationContext中

public class Demo {
    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 {
        // 创建一个BeanDefinition
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
        // 注入
        registry.registerBeanDefinition("person", beanDefinition);
    }
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
 
    }
}
 

6.Supplier

Supplier也是注入方式的一种,但使用非常少,非常冷门

它存储BeanDefinition中的属性,所以我们使用的时候,也需要修改其BeanDefinition

public <T> RootBeanDefinition(@Nullable Class<T> beanClass, String scope, @Nullable Supplier<T> instanceSupplier) {
   super();
   setBeanClass(beanClass);
   setScope(scope);
   // 看到在创建 RootBeanDefinition 的时候会赋值
   setInstanceSupplier(instanceSupplier);
}

使用示例

// 第3步:测试
public class Test {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        MyBeanFactoryPostProcessor beanFactoryPostProcessor = new MyBeanFactoryPostProcessor();
        applicationContext.addBeanFactoryPostProcessor(beanFactoryPostProcessor);
        applicationContext.refresh();

        Object person = applicationContext.getBean("person");
        // 获取到的结果
        System.out.println(person); // Person{name='张三'}
    }

}

// 第2步:依旧通过后置处理器操作
class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 1.创建新的RootBeanDefinition
        RootBeanDefinition beanDefinition = (RootBeanDefinition) BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
        // 2.注册Supplier, 这里为了简单直接设置为静态方法,如果时普通方法,需要先创建对象
        beanDefinition.setInstanceSupplier(Person::getPerson);
        // 3.注册
        registry.registerBeanDefinition("person", beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }

}

// 第1步:创建Entity
class Person {

    private String name;

    public static Person getPerson() {
        Person person = new Person();
        person.name = "张三";
        return person;
    }

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