Spring 依赖注入的方式,你了解哪些?

简介: 前言依赖查找和依赖注入是 Spring 实现 IoC 容器提供的两大特性,相对于依赖查找,Spring 更推崇的是使用依赖注入,本篇先对 Spring 中依赖注入的几种方式进行介绍,后续再分享其实现。

前言


依赖查找和依赖注入是 Spring 实现 IoC 容器提供的两大特性,相对于依赖查找,Spring 更推崇的是使用依赖注入,本篇先对 Spring 中依赖注入的几种方式进行介绍,后续再分享其实现。


依赖注入方式


如果在 A 类中使用到了 B 类的实例,那么我们就说 A 依赖 B。依赖的位置包括字段、构造器方法参数、普通方法参数等,Spring 都对其进行了支持。Spring 针对依赖提供了自动依赖注入和手动依赖注入两种方式,下面对其进行介绍。


自动依赖注入


自动依赖注入是 Spring 早期提供的能力,使用自动依赖注入可以避免显式的配置属性和构造函数参数的依赖,Spring 中提供了四种自动依赖注入的模式。具体如下。


image.png


Spring 早期曾经有一个 autodetect 的自动依赖注入方式,用于自动探测依赖注入方式,如果存在带参数的构造方法则使用 constructor 自动依赖注入,否则使用 byType 自动依赖注入,在 Spring 3.0 已废弃。


自动依赖注入可以通过 XML 配置文件或 @Bean 注解中进行指定。

XML 配置的示例如下。


<?xml version="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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--autowire 可指定 no、default、byName、byType、constructor-->
    <bean class="com.zzuhkp.Bean" autowire="no"/>
</beans>


@Bean 指定自动依赖注入的示例如下。


@Configuration
public class Config {
  // 通过 @Bean 的 autowire 属性指定自动依赖注入,可以指定 Autowire.NO、Autowire.BY_NAME、Autowire.BY_TYPE
    @Bean(autowire = Autowire.NO)
    String bean() {
        return "zzuhkp";
    }
}


自动依赖注入的优势


任何事物都有两面性,那么自动依赖注入自然也有其优势和限制。自动依赖注入优点如下。


自动依赖注入避免了指定属性和构造器参数。

在类中添加新的依赖项时,不必修改依赖,在开发中比较有用。

自动依赖注入的限制

虽然自动依赖注入避免了显式指定依赖项,但是其仍有一些不足,因此 Spring 默认不开启自动依赖注入。其具备的一些限制如下。


显式设置的属性和构造方法参数依赖将会覆盖自动依赖注入,无法装配简单的属性,如基本类型、String、Class 等。

自动依赖注入将导致对象之间的关系不如显式依赖注入清晰。

自动依赖注入将影响从 Spring 容器中生成文档的工具。

byType 类型的自动依赖注入,如果存在多个类型相同的 bean 不会影响注入数组、集合、Map 等,而注入单个 bean 对象由于歧义 Spring 则会抛出异常。

手动依赖注入

手动依赖注入相对自动依赖注入来说比较灵活,手动依赖注入可以通过 XML 配置文件、Java 注解或 Spring 提供的 API 来完成,而从具体依赖注入的方法来说又包括 setter、字段、构造器、方法、接口回调,下面分别予以说明。


setter 依赖注入


setter 依赖注入是通过调用 bean 的 setXXX 方法设置 bean 的依赖。自动依赖注入时设置注入类型为 byName 或 byType 可以达到 setter 注入的目的,而手动依赖注入则可以通过 XML 手动配置配置依赖或通过 Spring API 编程设置。


XML 手动 setter 依赖注入示例如下。


<?xml version="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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean class="com.zzuhkp.Bean">
        <!--设置 setter 依赖注入-->
        <property name="prop" ref="prop2"/>
    </bean>
    <bean id="prop" class="com.zzuhkp.Prop">
        <property name="name" value="this is name"/>
        <property name="value" value="this is value"/>
    </bean>
</beans>


Spring API setter 依赖注入示例如下,和上述的 XML 配置向对应。


public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 手动创建 BeanDefinition
        AbstractBeanDefinition prop = BeanDefinitionBuilder.genericBeanDefinition(Prop.class)
            // 添加 setter 注入的属性值
            .addPropertyValue("name", "this is name")
            .addPropertyValue("value", "this is value")
            // 获取 BeanDefinition
            .getBeanDefinition();
        // 向 Spring 中注入
        context.registerBeanDefinition("propBeanName", prop);
        AbstractBeanDefinition bean = BeanDefinitionBuilder.genericBeanDefinition(Bean.class)
            .addPropertyReference("prop", "propBeanName")
            .getBeanDefinition();
        context.registerBeanDefinition("beanName", bean);
        context.refresh();
        System.out.println(context.getBean("beanName"));
        context.close();
    }
}


Spring API setter 依赖注入示例中通过创建 BeanDefinition,并设置 BeanDefinition 中的属性来达到 setter 依赖注入的目的。关于 BeanDefinition 的知识参见 《掌握 Spring 必须知道的 BeanDefinition》 。


字段依赖注入


字段依赖注入是通过反射设置对象的字段值来实现。字段依赖注入通过在 bean 的字段上添加 @Autowired 、@Resource 或者 @Inject 来完成。关于 @Autowired 和 @Resource 的区别,可参见前面的文章 《Spring 中 @Autowired 和 @Resource 有什么区别?》。


字段依赖注入的示例如下。


@Component
public class Bean {
  // @Resource
    @Autowired
    private Prop prop;
    public Prop getProp() {
        return prop;
    }
    public Bean setProp(Prop prop) {
        this.prop = prop;
        return this;
    }
    @Override
    public String toString() {
        return "Bean{" +
            "prop=" + prop +
            '}';
    }
}


构造器依赖注入


构造器依赖注入是注入 Spring Bean 实例化时需要的参数。可以指定自动依赖注入的方式为 constructor 实现,也可以指定构造器参数、通过 @Autowired 注解或者 Spring API 手动依赖注入。


通过 XML 配置文件进行构造器手动依赖注入示例如下。


<?xml version="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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean class="com.zzuhkp.Bean">
        <!--构造器依赖注入-->
        <constructor-arg ref="prop"/>
    </bean>
    <bean id="prop" class="com.zzuhkp.Prop">
        <property name="name" value="this is name"/>
        <property name="value" value="this is value"/>
    </bean>
</beans>


通过 @Autowired 进行构造器依赖注入示例如下。


public class Bean {
    private Prop prop;
    public Bean(@Autowired Prop prop) {
        this.prop = prop;
    }
}

通过 Spring API 进行手动构造器依赖注入的示例如下。


public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 手动创建 BeanDefinition
        AbstractBeanDefinition prop = BeanDefinitionBuilder.genericBeanDefinition(Prop.class)
            // 添加 setter 注入的属性值
            .addPropertyValue("name", "this is name")
            .addPropertyValue("value", "this is value")
            // 获取 BeanDefinition
            .getBeanDefinition();
        // 向 Spring 中注入
        context.registerBeanDefinition("prop", prop);
        AbstractBeanDefinition bean = BeanDefinitionBuilder.genericBeanDefinition(Bean.class)
            // 添加构造器所需的依赖
            .addConstructorArgReference("prop")
            .getBeanDefinition();
        context.registerBeanDefinition("bean",bean);
        context.refresh();
        System.out.println(context.getBean("bean"));
        context.close();
    }
}


方法依赖注入


方法依赖注入和 setter 依赖注入类似,但是不限制方法必须为 setter 方法。方法依赖注入只能通过注解完成,在方法上添加 @Autowired、@Resource 或者 @Inject 可以为只有一个参数的方法进行依赖注入。配置类中 @Bean 注解定义的 bean 则会按照 constructor 自动依赖注入的方式进行处理。


方法依赖注入的示例如下。


@Component
public class Bean {
    private Prop prop;
    public Bean(@Autowired Prop prop) {
        this.prop = prop;
    }
    // @Resource
    // @Inject
    @Autowired
    public void prop(Prop prop) {
        this.prop = prop;
    }
}


回调依赖注入


回调依赖注入依托于 Spring Bean 的生命周期,需要实现 Spring 提供的 XxxAware 接口,在 Spring 生命周期的某一阶段会调用接口中定义的方法,从而拿到方法中的参数。以 ApplicationContextAware 为例,示例如下。


@Component
public class Bean implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}


依赖注入的来源


和依赖查找一样,依赖注入也具有自己的来源。与依赖查找不同的是,依赖注入的来源除了 BeanDefinition、单例 bean,还包括 Spring 中一些游离的对象,这些对象非 Spring 管理,Spring 只是简单记录了其引用。此外,通过 @Value 注解,Spring 还能够注入外部化配置。有关外部化配置,可参考文章 《Spring 中的 Environment 、Profile 与 PropertySource》。


游离对象的注册,感兴趣的小伙伴可查阅源码ConfigurableListableBeanFactory#registerResolvableDependency,而依赖注入最终则会委托给AutowireCapableBeanFactory#resolveDependency(DependencyDescriptor,String,Set<String>, TypeConverter) 解析依赖对象,由于其实现相对复杂,本篇不再展开。


总结

依赖注入是我们使用 Spring 最常用的方式,依赖注入包括自动依赖注入和手动依赖注入,具体又分为 setter 依赖注入、字段依赖注入、构造器自动依赖注入、方法依赖注入、接口回调。依赖注入的来源包括 BeanDefinition、单例 bean、游离对象、外部化配置。由于篇幅问题,本篇未对依赖处理过程展开说明,后续再进行分析。


目录
相关文章
|
4月前
|
XML Java 测试技术
Spring Boot中的依赖注入和控制反转
Spring Boot中的依赖注入和控制反转
|
6月前
|
XML Java 程序员
Spring6框架中依赖注入的多种方式(推荐构造器注入)
依赖注入(DI)是一种过程,对象通过构造函数参数、工厂方法的参数或在对象实例构建后设置的属性来定义它们的依赖关系(即与其一起工作的其他对象)。
90 3
|
6月前
|
Java 测试技术 开发者
Spring IoC容器通过依赖注入机制实现控制反转
【4月更文挑战第30天】Spring IoC容器通过依赖注入机制实现控制反转
61 0
|
3月前
|
Java Spring 容器
彻底改变你的编程人生!揭秘 Spring 框架依赖注入的神奇魔力,让你的代码瞬间焕然一新!
【8月更文挑战第31天】本文介绍 Spring 框架中的依赖注入(DI),一种降低代码耦合度的设计模式。通过 Spring 的 DI 容器,开发者可专注业务逻辑而非依赖管理。文中详细解释了 DI 的基本概念及其实现方式,如构造器注入、字段注入与 setter 方法注入,并提供示例说明如何在实际项目中应用这些技术。通过 Spring 的 @Configuration 和 @Bean 注解,可轻松定义与管理应用中的组件及其依赖关系,实现更简洁、易维护的代码结构。
51 0
|
3月前
|
设计模式 自然语言处理 Java
简单了解下Spring中的各种Aware接口实现依赖注入
在Spring框架中,Aware接口是一组用于提供特定资源或环境信息的回调接口。这些接口被设计用来允许Bean获取对Spring容器或其他相关资源的引用,并在需要时进行适当的处理。
36 2
|
3月前
|
自然语言处理 Java 开发者
简单了解下Spring中的各种Aware接口实现依赖注入
【8月更文挑战第21天】在Spring框架中,Aware接口系列是一种特殊的机制,它允许Bean在初始化过程中获取到Spring容器或容器中的特定资源,从而实现了更加灵活和强大的依赖注入方式。本文将围绕Spring中的各种Aware接口,详细探讨它们如何帮助开发者在工作和学习中更好地实现依赖注入。
107 0
|
4月前
|
缓存 Java Spring
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
Spring循环依赖问题之Spring不支持构造器内的强依赖注入如何解决
|
5月前
|
设计模式 Java 测试技术
Spring Boot中的依赖注入详解
Spring Boot中的依赖注入详解
|
5月前
|
缓存 Java 测试技术
Spring 框架,不只是依赖注入和面向切面那么简单!
【6月更文挑战第25天】Spring框架超越DI和AOP,涵盖事务管理、数据访问抽象如`JdbcTemplate`、消息驱动支持如`@JmsListener`、缓存管理和测试工具。示例代码展示了其简化复杂性的能力,如自动事务处理、数据库操作及消息队列监听。Spring是构建高效企业级应用的全面解决方案。
40 4
|
5月前
|
Java Spring 容器
spring如何进行依赖注入,通过set方法把Dao注入到serves
spring如何进行依赖注入,通过set方法把Dao注入到serves