该文篇幅较长,主要包括两个章节,第一个章节主要是对Spring的注入方式进行讲解与演示。第二个章节主要通过源码方式对注入流程进行讲解。

Spring依赖注入(一) - 注入方式_System

Spring IOC 依赖注入

依赖注入的方式

在Spring中,依赖注入方式通常分为两种形式,手动注入和自动注入形式。

手动注入: 手动注入通常来说会比较麻烦,但是逻辑也会比较清晰,不容易出现问题。

自动注入: 使用起来方便,但是通常情况下依赖会变得模糊不清,不方便维护管理,并且如果被注入的依赖名称发生改变的时候还会导致注入失败。

常规依赖注入

set方式注入

set注入,顾名思义,他要求被注入的对象必须包含set方法,然后Spring通过调用对应属性的set方法进行注入赋值。

被注入对象信息:

public class SetterInjectionEntity {

  private String id;

  private String desc;

  // 必须存在set方法从而进行注入
  public void setId(String id) {
    this.id = id;
  }

  public void setDesc(String desc) {
    this.desc = desc;
  }

  @Override
  public String toString() {
    return "SetterInjectionEntity{" +
      "id='" + id + '\'' +
      ", desc='" + desc + '\'' +
      '}';
  }
}

手动模式

  • • 基于XML文件配置方式:

XML配置:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
                           http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean class="cn.phshi.domain.SetterInjectionEntity">
    <property name="id" value="001"/>
    <property name="desc" value="注入描述信息"/>
  </bean>
</beans>

Java代码:

public class XmlDependencySetterInjectionDemo {

  public static void main(String[] args) {
    // 使用DefaultListableBeanFactory创建上下文
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

    // 使用XmlBeanDefinitionReader读取xml文件
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    // 加载 XML 资源,解析并且生成 BeanDefinition
    beanDefinitionReader.loadBeanDefinitions("classpath:/META-INF/dependency-setter-injection.xml");

    // 依赖查找并且创建 Bean
    SetterInjectionEntity entity = beanFactory.getBean(SetterInjectionEntity.class);
    System.out.println(entity);
  }
}
  • • Java元注解方式:
public class AnnotationDependencySetterInjectionDemo {

  public static void main(String[] args) {
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(AnnotationDependencySetterInjectionDemo.class);
    annotationConfigApplicationContext.refresh();

    SetterInjectionEntity bean = annotationConfigApplicationContext.getBean(SetterInjectionEntity.class);
    System.out.println(bean);

    annotationConfigApplicationContext.close();
  }

  @Bean
  public SetterInjectionEntity setterInjectionEntity() {
    SetterInjectionEntity entity = new SetterInjectionEntity();
    entity.setId("002");
    entity.setDesc("注解进行set注入方式");
    return entity;
  }
}
  • • API元信息注入:

最原生方式,直接通过构建BeanDefinition信息进行注入。

public class ApiDependencySetterInjectionDemo {

  public static void main(String[] args) {
    DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();

    // 构建BeanDefinition信息
    BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(SetterInjectionEntity.class);
    definitionBuilder.addPropertyValue("id", "003")
      .addPropertyValue("desc","api元信息注入方式");
    // 将BeanDefinition注册到BeanFactory中
    defaultListableBeanFactory.registerBeanDefinition("setterInjectionEntity",definitionBuilder.getBeanDefinition());

    SetterInjectionEntity bean = defaultListableBeanFactory.getBean(SetterInjectionEntity.class);
    System.out.println(bean);
  }
}

自动模式

创建一个新的注入的java对象:

public class SetterInjectionEntityHolder {

  private SetterInjectionEntity setterInjectionEntity;

  // 添加set方法方便注入
  public void setSetterInjectionEntity(SetterInjectionEntity setterInjectionEntity) {
    this.setterInjectionEntity = setterInjectionEntity;
  }

  @Override
  public String toString() {
    return "SetterInjectionEntityHolder{" +
      "setterInjectinotallow=" + setterInjectionEntity +
      '}';
  }
}

XML配置:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
                           http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 引入上面xml配置文件的内容  -->
  <import resource="dependency-setter-injection.xml"/>

  <bean class="cn.phshi.domain.SetterInjectionEntityHolder" autowire="byType"/>
</beans>

Java代码:

public class XmlDependencyAutowiringSetterInjectionDemo {

  public static void main(String[] args) {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-autowiring-setter-injection.xml");

    SetterInjectionEntityHolder bean = beanFactory.getBean(SetterInjectionEntityHolder.class);
    System.out.println(bean);
  }
}

构造器注入

被注入的对象:

public class ConstructorInjectionEntity {

  private Long id;

  private String decs;

  public ConstructorInjectionEntity(Long id, String decs) {
    this.id = id;
    this.decs = decs;
  }

  @Override
  public String toString() {
    return "ConstructorInjectionEntity{" +
      "id=" + id +
      ", decs='" + decs + '\'' +
      '}';
  }
}

// 支持对象
public class ConstructorInjectionEntityHolder {

  private ConstructorInjectionEntity constructorInjectionEntity;

  public ConstructorInjectionEntityHolder(ConstructorInjectionEntity constructorInjectionEntity) {
    this.constructorInjectionEntity = constructorInjectionEntity;
  }

  @Override
  public String toString() {
    return "ConstructorInjectionEntityHolder{" +
      "constructorInjectinotallow=" + constructorInjectionEntity +
      '}';
  }
}

手动注入

  • • 基于XML文件配置方式:

XML配置:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
                           http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean name="constructorInjectionEntity" class="cn.phshi.domain.ConstructorInjectionEntity">
    <constructor-arg value="1"/>
    <constructor-arg value="xml构造器注入方式"/>
  </bean>
</beans>

Java代码:

public class XmlDependencyConstructorInjectionDemo {

  public static void main(String[] args) {
    DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();

    XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
    xmlBeanDefinitionReader.loadBeanDefinitions("classpath:/META-INF/dependency-constructor-injection.xml");

    ConstructorInjectionEntity bean = defaultListableBeanFactory.getBean(ConstructorInjectionEntity.class);
    System.out.println(bean);
  }
}
  • • Java元注解方式:
public class AnnotationDependencyConstructorInjectionDemo {

  private final ConstructorInjectionEntity constructorInjectionEntity;

  public AnnotationDependencyConstructorInjectionDemo(ConstructorInjectionEntity constructorInjectionEntity) {
    this.constructorInjectionEntity = constructorInjectionEntity;
  }

  public static void main(String[] args) {
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

    applicationContext.register(AnnotationDependencyConstructorInjectionDemo.class);
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
    beanDefinitionReader.loadBeanDefinitions("classpath:/META-INF/dependency-constructor-injection.xml");
    applicationContext.refresh();

    AnnotationDependencyConstructorInjectionDemo bean = applicationContext.getBean(AnnotationDependencyConstructorInjectionDemo.class);
    System.out.println(bean.constructorInjectionEntity);

    applicationContext.close();
  }
}
  • • API元信息注入:
public class ApiDependencyConstructorInjectionDemo {

  public static void main(String[] args) {
    DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();

    // 读取配置中的对象实例注册到容器中
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
    beanDefinitionReader.loadBeanDefinitions("classpath:/META-INF/dependency-constructor-injection.xml");

    // 注册BeanDefinition
    AbstractBeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(ConstructorInjectionEntityHolder.class)
      .addConstructorArgReference("constructorInjectionEntity")
      .getBeanDefinition();
    defaultListableBeanFactory.registerBeanDefinition("constructorInjectionEntityHolder",definition);

    ConstructorInjectionEntityHolder bean = defaultListableBeanFactory.getBean(ConstructorInjectionEntityHolder.class);
    System.out.println(bean);
  }
}

自动注入

XML配置:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
                           http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

  <import resource="dependency-constructor-injection.xml"/>

  <bean name="constructorInjectionEntityHolder" class="cn.phshi.domain.ConstructorInjectionEntityHolder"
        autowire="constructor"/>
</beans>

Java代码:

public class XmlDependencyAutowiringSetterInjectionDemo {

  public static void main(String[] args) {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-autowiring-setter-injection.xml");

    SetterInjectionEntityHolder bean = beanFactory.getBean(SetterInjectionEntityHolder.class);
    System.out.println(bean);
  }
}

字段注入

字段注入主要是使用@Autowired或者@Resource标注在要对象注入的属性上面,当然,前提是被标注的对象以及标注的对象两个都要被Spring进行管理。

被注入的对象:

public class CommonInjectionEntity {

  private Long id;

  private String desc;

  public CommonInjectionEntity(Long id, String desc) {
    this.id = id;
    this.desc = desc;
  }

  @Override
  public String toString() {
    return "CommonInjectionEntity{" +
      "id=" + id +
      ", desc='" + desc + '\'' +
      '}';
  }
}
  • • Java元注解方式:
public class AnnotationDependencyFieldInjectionDemo {

  @Autowired
  private CommonInjectionEntity autowiredCommonInjectionEntity;

  @Resource
  private CommonInjectionEntity resourceCommonInjectionEntity;

  public static void main(String[] args) {
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(AnnotationDependencyFieldInjectionDemo.class);
    annotationConfigApplicationContext.refresh();

    AnnotationDependencyFieldInjectionDemo bean = annotationConfigApplicationContext.getBean(AnnotationDependencyFieldInjectionDemo.class);

    System.out.println("Autowired注入的值:"+bean.autowiredCommonInjectionEntity);
    System.out.println("Resource注入的值:"+bean.resourceCommonInjectionEntity);
    // 返回true
    System.out.println("Autowired注入和Resource注入的值" + (bean.autowiredCommonInjectionEntity == bean.resourceCommonInjectionEntity));

    annotationConfigApplicationContext.close();
  }

  @Bean
  public CommonInjectionEntity commonInjectionEntity() {
    return new CommonInjectionEntity(1L, "字段注入方式测试");
  }
}

方法注入

和字段注入相仿,只不过字段注入是使用注解标注在字段上面,而方法注入是标注在方法上面。

Java代码:

public class AnnotationDependencyMethodInjectionDemo {

  private CommonInjectionEntity autowiredCommonInjectionEntity;

  private CommonInjectionEntity resourceCommonInjectionEntity;

  @Autowired
  public void initAutowiredCommonInjectionEntity(CommonInjectionEntity autowiredCommonInjectionEntity) {
    this.autowiredCommonInjectionEntity = autowiredCommonInjectionEntity;
  }

  @Resource
  public void initResourceCommonInjectionEntity(CommonInjectionEntity resourceCommonInjectionEntity) {
    this.resourceCommonInjectionEntity = resourceCommonInjectionEntity;
  }

  public static void main(String[] args) {
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(AnnotationDependencyMethodInjectionDemo.class);
    annotationConfigApplicationContext.refresh();

    AnnotationDependencyMethodInjectionDemo bean = annotationConfigApplicationContext.getBean(AnnotationDependencyMethodInjectionDemo.class);
    System.out.println("Autowired注入的值:"+bean.autowiredCommonInjectionEntity);
    System.out.println("Resource注入的值:"+bean.resourceCommonInjectionEntity);
    System.out.println("Autowired注入和Resource注入的值" + (bean.autowiredCommonInjectionEntity == bean.resourceCommonInjectionEntity));

    annotationConfigApplicationContext.close();
  }

  @Bean
  public CommonInjectionEntity commonInjectionEntity() {
    return new CommonInjectionEntity(1L, "方法注入测试");
  }
}

接口回调注入

Spring中提供了一个Aware,可以用于一些特定的Bean实例对象,比如BeanFactoryApplicationContext注入等等。

接口名称

描述

BeanFactoryAware

获取 IoC 容器 - BeanFactory

ApplicationContextAware

获取 Spring 应用上下文 - ApplicationContext 对象

EnvironmentAware

获取 Environment 对象

ResourceLoaderAware

获取资源加载器 对象 - ResourceLoader

BeanClassLoaderAware

获取加载当前 Bean Class 的 ClassLoader

BeanNameAware

获取当前 Bean 的名称

MessageSourceAware

获取 MessageSource 对象,用于 Spring 国际化

ApplicationEventPublisherAware

获取 ApplicationEventPublishAware 对象,用于 Spring 事件

EmbeddedValueResolverAware

获取 StringValueResolver 对象,用于占位符处理

Java代码:

只演示了BeanFactoryAware接口和ApplicationContextAware接口。

public class DependencyAwareInjectionDemo implements BeanFactoryAware, ApplicationContextAware {

    private static BeanFactory beanFactory;

    private static ApplicationContext applicationContext;

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
        annotationConfigApplicationContext.register(DependencyAwareInjectionDemo.class);
        annotationConfigApplicationContext.refresh();

        System.out.println("BeanFactory注入对象是否相等:" + Objects.equals(beanFactory, annotationConfigApplicationContext.getBeanFactory()));
        System.out.println("ApplicationContext注入对象是否相等:" + Objects.equals(applicationContext, annotationConfigApplicationContext));

        annotationConfigApplicationContext.close();
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        DependencyAwareInjectionDemo.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        DependencyAwareInjectionDemo.applicationContext = applicationContext;
    }
}

注入选型

各种注入方式没有绝对的好与坏,只有绝对的合理。所以我们在针对于注入选型的时候,更应该按照场景进行选择。

  • • 低依赖: 构造器注入
  • • 多依赖: Setter 方法注入
  • • 便利性: 字段注入
  • • 声明类: 方法注入

其他方式注入

除了常规的对象类型注入外,在Spring中还存在着很多其他的方式注入,比如基本类型注入、枚举注入、资源注入、集合注入以及限定注入等等。下面我们就讨论一下这些注入方式的使用。

基础类型注入

原生类型注入

原生类型主要指的就是基础类型,在Java中主要存在booleanbytecharshortintfloatlongdouble八大基础类型,基础类型注入比较简单,之前也有所涉及,直接输入值就可以了,这里就不进行演示了。

标量类型注入

Spring中标量注入的类型主要包括NumberCharacterBooleanEnumLocaleCharsetCurrency、 PropertiesUUID,下面我们对几种类型注入进行简单演示:

注入对象:

public enum SexEnum {
  MAN,
  WOMAN
}

public class ScalarInjectionEntity {

  private Boolean aBoolean;

  private SexEnum sex;

  private Properties properties;
  
  // ... 省略set、toString方法
}

XML配置:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
                           http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean class="cn.phshi.domain.ScalarInjectionEntity">
    <property name="aBoolean" value="true"/>
    <property name="sex" value="MAN"/>
    <property name="properties">
      <map>
        <entry key="key1" value="value1"/>
        <entry key="key2" value="value2"/>
        <entry key="key3" value="value3"/>
      </map>
    </property>
  </bean>
</beans>

Java代码:

public class DependencyScalarInjectionDemo {

  public static void main(String[] args) {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-scalar-injection.xml");

    ScalarInjectionEntity bean = beanFactory.getBean(ScalarInjectionEntity.class);
    System.out.println(bean);
  }
}

集合类型注入

集合一般可以看作是三大类,数组、Collection和map,即便Collection可以划分为更细的List、Set等等,map也可以划分为properties等等,当是他们的注入方式都是差不多的。

注入对象:

public class CollectionInjectionEntity {

  private int[] arr;

  private List<SexEnum> list;

  private Map<String, SexEnum> map;

  // ... 省略set、toString方法
}

XML配置:

<?xml versinotallow="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
                           http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean class="cn.phshi.domain.CollectionInjectionEntity">
    <property name="arr" value="1,2,3"/>
    <!--        <property name="list" value="MAN,WOMAN"/>-->
    <!--    或者    -->
    <property name="list">
      <list>
        <value>MAN</value>
        <value>WOMAN</value>
      </list>
    </property>
    <property name="map">
      <map key-type="java.lang.String" value-type="cn.phshi.domain.SexEnum">
        <entry key="sex1" value="MAN"/>
        <entry key="sex2" value="WOMAN"/>
        <entry key="sex3" value="MAN"/>
      </map>
    </property>
  </bean>
</beans>

Java代码:

public class DependencyCollectionInjectionDemo {

  public static void main(String[] args) {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-collection-injection.xml");

    CollectionInjectionEntity bean = beanFactory.getBean(CollectionInjectionEntity.class);
    System.out.println(bean);
  }
}

限定注入

限定注入主要指的是使用注解@Qualifier或者他的派生进行分组注入。

  • • @Qualifier 分组限定

Java代码:

public class DependencyQualifierInjectionDemo  {

  @Autowired
  @Qualifier(value = "group1")
  private Map<String,String> group1Str;

  @Autowired
  @Qualifier(value = "group2")
  private Map<String,String> group2Str;

  public static void main(String[] args) {
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(DependencyQualifierInjectionDemo.class);
    annotationConfigApplicationContext.refresh();

    DependencyQualifierInjectionDemo bean = annotationConfigApplicationContext.getBean(DependencyQualifierInjectionDemo.class);
    System.out.println("分组1中的对象为:"+bean.group1Str);
    System.out.println("分组2中的对象为:"+bean.group2Str);

    annotationConfigApplicationContext.close();
  }

  @Bean
  @Qualifier(value = "group1")
  private String group1Str1() {
    return "group1-str1";
  }

  @Bean
  @Qualifier(value = "group1")
  private String group1Str2() {
    return "group1-str2";
  }

  @Bean
  @Qualifier(value = "group2")
  private String group2Str1() {
    return "group2-str";
  }

  @Bean
  @Qualifier(value = "group2")
  private String group2Str2() {
    return "group2-str2";
  }
}
  • • @Qualifie 派生注解分组限定

Java代码:

// 要打上标注@Qualifier
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface StrGroup {}

public class DependencyStrGroupInjectionDemo {

  @Autowired
  @StrGroup
  private Map<String, String> strGroupStr;

  @Autowired
  @Qualifier
  private Map<String, String> qualifierStr;

  public static void main(String[] args) {
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(DependencyStrGroupInjectionDemo.class);
    annotationConfigApplicationContext.refresh();

    DependencyStrGroupInjectionDemo bean = annotationConfigApplicationContext.getBean(DependencyStrGroupInjectionDemo.class);
    System.out.println("StrGroup分组中的内容为:" + bean.strGroupStr);
    // Qualifier分组中会包含StrGroup分组中的内容
    System.out.println("Qualifier分组的内容为:" + bean.qualifierStr);

    annotationConfigApplicationContext.close();
  }

  @Bean
  @StrGroup
  private String group1Str1() {
    return "strGroup-str1";
  }

  @Bean
  @StrGroup
  private String group1Str2() {
    return "strGroup-str2";
  }

  @Bean
  @Qualifier
  private String group2Str1() {
    return "qualifier-str1";
  }

  @Bean
  @Qualifier
  private String group2Str2() {
    return "qualifier-str2";
  }
}

Spring Cloud @LoadBalanced便是使用这种方式进行限定注入:

Spring依赖注入(一) - 注入方式_System_02

image-20230321223606237

延迟依赖注入

延迟依赖注入在前面也说过很多,主要是采用ObjectFactory及其派生对象ObjectProvider来进行实现,注意,由于ObjectProvider在实现了ObjectFactory的同时,还实现了Iterable,然后对Java8的函数式编程的支持,所以大多数情况下都是使用ObjectProvider

Java代码:

public class LazyDependencyInjectionDemo {

  @Autowired
  private ObjectFactory<Set<String>> objectFactoryStr;

  // ObjectProvider实现了Iterable,所以注入集合更简单
  @Autowired
  private ObjectProvider<String> objectProvider;

  public static void main(String[] args) {
    AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    annotationConfigApplicationContext.register(LazyDependencyInjectionDemo.class);
    annotationConfigApplicationContext.refresh();

    LazyDependencyInjectionDemo bean = annotationConfigApplicationContext.getBean(LazyDependencyInjectionDemo.class);
    System.out.println("objectFactoryStr中的内容为:");
    bean.objectFactoryStr.getObject().forEach(System.out::printf);
    System.out.println("\nobjectProvider的内容为:");
    bean.objectProvider.forEach(System.out::printf);

    annotationConfigApplicationContext.close();
  }

  @Bean
  private String group1Str1() {
    return "lazy-str1";
  }

  @Bean
  private String group1Str2() {
    return "lazy-str2";
  }
}