Spring5应用之依赖注入

作者简介:☕️大家好,我是Aomsir,一个爱折腾的开发者!
个人主页:Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客
当前专栏:Spring5应用专栏_Aomsir的博客-CSDN博客

参考文献

引言

之前我们已经初步了解了Spring框架的一些基本概念,今天我们将深入学习依赖注入(Dependency Injection,DI)这一重要概念,它是实现控制反转(Inversion of Control,IoC)的关键手段和思想。在Spring中,我们可以使用多种注入方式来实现依赖注入,其中包括以下几种常见的方式:Set注入、构造注入、反射注入以及基于字段的注入等等

概念详解

什么是依赖注入?

依赖注入是通过Spring容器以及配置文件,为所创建的对象(Bean)的成员变量赋值的过程

为什么需要依赖注入?

在以下示例中,我们有一个Person类,其中包含id和name两个属性。在测试代码中,我们从工厂获取了Person对象,并手动为其属性赋值。然而,这种方式引入了紧耦合,如果将来需要更改id和name的属性值,就必须修改代码、重新编译、打包和部署。这种做法并没有充分体现依赖注入的优势。依赖注入的核心概念在于使用Spring容器和配置文件来实现对象间的解耦,从而提高灵活性和可维护性

public class TestSpring {
    
    @Test
    public void test4() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person = ctx.getBean("person", Person.class);

        // 通过编码为属性赋值存在耦合
        person.setId(1);
        person.setName("Aomsir");
        System.out.println("person = " + person);
    }
}

开发步骤

  • 为类成员变量提供Set/Get方法
  • 编写Spring的配置文件
  • 编写测试代码
<bean id="person" class="com.aomsir.basic.entity.Person">
    <property name="id" >
        <value>10</value>
    </property>
    <property name="name">
        <value>Aomsir</value>
    </property>
</bean>

简易实现原理

Spring通过底层调用对象属性对应的set方法,从而完成成员变量的赋值。这种方式也被称为set注入。通过这种机制,我们可以在配置文件中指定依赖对象,然后让Spring容器自动完成对象的创建和属性赋值,从而实现解耦和依赖注入的目标
在这里插入图片描述

Set注入详解

在Spring容器中,一个Bean的成员变量类型可能涵盖多种种类,例如Integer、String、List、HashMap,以及我们开发者自定义的类型等。这些成员变量的类型主要可以分为两种:第一种是由JDK提供的内置类型,第二种是我们开发者自己定义的类型。如下图所示:
在这里插入图片描述

JDK类型

简单的JDK类型

以下示例涵盖了对所有常见的简单JDK数据类型的测试,展示了如何在Spring中进行Set注入

@Data
public class Customer implements Serializable {
    private String name;
    private int age;
    private String[] emails;
    private Set<String> tels;
    private List<String> address;
    private Map<String, String> qqs;
    private Properties p;
}
<bean class="com.aomsir.basic.entity.Customer" id="customer">
    <property name="name">
        <value>Aomsir</value>
    </property>

    <property name="age">
        <value>21</value>
    </property>

    <property name="emails">
        <list>
            <value>aomsir1@apache.org</value>
            <value>aomsir2@apache.org</value>
            <value>aomsir3@apache.org</value>
        </list>
    </property>

    <property name="tels">
        <set>
            <value>010-1</value>
            <value>010-2</value>
            <value>010-3</value>
        </set>
    </property>

    <property name="address">
        <list>
            <value>北京市</value>
            <value>武汉市</value>
        </list>
    </property>

    <property name="qqs">
        <map>
            <entry>
                <key><value>name</value></key>
                <value>Aomsir</value>
            </entry>
        </map>
    </property>

    <property name="p">
        <props>
            <prop key="name">Aomsir</prop>
        </props>
    </property>
</bean>

复杂的JDK类型

更为复杂的JDK类型,比如Date等,用得相对较少。等知识储备足够,单独开设一个章节来深入讲解这些复杂类型的处理方式

自定义类型

下面这个示例,UserService依赖于UserDAO。UserDAO是我们自定义的类型,将会通过Set注入的方式来实现

第一种方式

在Bean内部进行嵌套,以创建另一个Bean并进行依赖注入的操作

<bean id="userService" class="com.aomsir.basic.service.impl.UserServiceImpl">
    <property name="userDAO">
        <bean class="com.aomsir.basic.dao.impl.UserDAOImpl"/>
    </property>
</bean>

第二种方式

上述第一种方式中存在一些问题。首先,会产生代码冗余的情况。其次,被注入的对象(UserDAO)可能会被多次创建,从而浪费了JVM内存资源。因此,我们可以采用以下引用的方式来进行依赖注入

<bean id="userDAO" class="com.aomsir.basic.dao.impl.UserDAOImpl"/>

<bean id="userService" class="com.aomsir.basic.service.impl.UserServiceImpl">
    <property name="userDAO">
        <ref bean="userDAO" />
    </property>
</bean>

简化写法

属性简化

上面的注入写法还是太臃肿了,可以对其简化,将之前使用的value标签替换为bean标签中的value属性,同时将之前的ref标签替换为bean标签中的ref属性。需要注意的是:value属性仅适用于8种基本类型以及String类型

<!--JDK类型 value属性-->
<bean id="person" class="com.aomsir.basic.entity.Person">
    <property name="id" value="10" />
    <property name="name" value="Aomsir" />
</bean>

<!--自定义类型 ref属性-->
<bean id="userService" class="com.aomsir.basic.service.impl.UserServiceImpl">
    <property name="userDAO" ref="userDAO" />
</bean>

p标签简化

另外,基于p标签,我们可以进一步简化上述的value属性设置。要使用这种方式,需要引入p命名空间标签。具体使用方式如下所示。这种方式的注意点和之前的方式相同

<bean id="person" class="com.aomsir.basic.entity.Person" p:id="1" p:name="Aomsir"/>

<bean id="userService" class="com.aomsir.basic.service.impl.UserServiceImpl" p:userDAO-ref="userDAO" />

构造注入详解

在上述的Set注入中,Spring会调用类的Set方法,通过配置文件为成员变量赋值。而构造注入则是Spring会调用类的构造方法,在配置文件中指定参数值,从而为成员变量赋值

开发步骤

  • 类提供有参构造方法
  • 使用constructor-arg标签替换propertity标签
public class Employer {
    private Integer id;
    private String name;

    public Employer(Integer id, String name) {
        this.id = id;
        this.name = name;
    }
}
<bean id="employer" class="com.aomsir.basic.entity.Employer">
    <constructor-arg name="id" value="1" />
    <constructor-arg name="name" value="Aomsir" />
</bean>

重载构造

在实际开发中,我们可能会为一个类提供多个重载构造函数。如果这些构造函数参数个数不同,我们可以通过constructor-arg标签的数量来区分。而如果参数个数相同,但类型不同,我们则可以在constructor-arg标签中使用type属性来进行区分

控制反转&依赖注入

  • 控制反转(IoC):全称为Inversion of Control,即控制权的反转。它涉及到成员变量的赋值控制。这种机制的关键在于,将控制权从代码中转移到Spring容器和配置文件中。这种反转带来的最大好处之一是解耦合,底层实现基于之前章节中提到的工厂设计模式
  • 依赖注入(DI):全称为Dependency Injection,是一种思想。通过Spring的工厂和配置文件,为对象的成员变量赋值。当一个类需要另一个类时,就存在依赖关系。为了解决这种依赖,我们将另一个类作为本类的成员变量,最终通过Spring配置文件完成注入。这种机制能够更好地管理对象之间的关系,提高了灵活性和可维护性

总结

在未来的实际应用中,Set注入可能会更加常见。这主要是因为构造注入在处理重载构造函数时可能较为繁琐,容易出现参数数量和顺序的问题。另外,虽然Spring官方并未直接明确推荐使用Set注入,但实际上,其底层源码中却广泛地应用了Set注入的方式。这说明了Spring团队自己也意识到了Set注入的一些优势,并在内部使用这种方式来处理依赖关系

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aomsir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值