Spring的DI

242 阅读5分钟

一、Dependency Injection: 依赖注入

在Spring中,DI是依赖注入的缩写,全称为Dependency Injection。它是一种设计模式,用于解决对象之间的依赖关系的管理问题。

DI的核心思想是将对象的创建和对象之间的依赖关系的管理交给外部容器来完成,而不是由对象自身来创建和管理它所依赖的其他对象。这种通过外部容器来管理对象之间的依赖关系的方式,能够提高代码的可扩展性、可维护性和测试性。

总结一下:

Ioc:作用是把对象的创建权,反转到Spring,让Spring容器来管理

DI:作用是Spring创建对象的过程中,将对象依赖属性通过配置进行注入

举例说明。比如说我现在有两个类

@Service
public class UserServiceImpl implements UserzService{

}
@Service
public class OrderServiceImpl implements OrderService{

}

IOC会将UserServiceImpl和OrderServiceImpl这两个类的实例创建出来。这两个空类,目前还体现不出来DI的思想。但如果我们将这两个类稍微调整一下

@Service
public class UserServiceImpl implements UserzService{
    @Autowired
    private Orderservice orderService;
}
@Service
public class OrderServiceImpl implements OrderService{

}

此时,UserServiceImpl类中用到了OrderService这个类的实例。DI就是负责将OrderService的实例注入到UserService的实例当中来

在Spring中,通过DI可以实现以下功能:

  1. 解耦对象之间的依赖关系:DI可以帮助我们降低对象之间的耦合度,使得对象之间的关系更加灵活、可配置和可扩展。
  2. 管理对象的生命周期:通过DI容器,我们可以统一管理对象的创建、初始化、销毁等生命周期,避免对象的重复创建和资源的泄露。
  3. 提供依赖关系的自动装配:DI容器可以根据对象之间的依赖关系,自动将依赖的对象注入到目标对象中,从而使得对象可以直接使用依赖的对象,无需手动进行依赖关系的配置和维护。

二、自动装配注入

autowire属性:让spring按照一定的方式自己去找合适的对象,并完成DI

  • default:不要自动注入
  • no:不要自动注入
  • byName:按照名字注入(按照属性的名字在spring中找bean) factory.getBean("属性的名字")
  • byType:按照依赖对象的类型注入(factory.getBean(属性的类型))
  • constructor:按照对象的构造器上面的参数类型注入

注意:

1,如果按照byName自动注入,要求所有的属性名字和id的名字必须保证一种规范的命名方式;

2,如果按照byType注入,如果spring中同一个类型有多个实例-->报bean不是唯一类型错误;

AutoBean

import lombok.Data;

@Data
public class AutoBean {

}

AutoWireBean

public class AutoWireBean {
    private AutoBean autoBean;

    public void setAutoBean(AutoBean bean) {
        this.autoBean = bean;
    }

    public void sayAutoWireBean() {
        System.out.println("AutoBean:==" + this.autoBean);
    }
}

autoBean.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属性:让Spring按照一定的方式自己去找合适的对象,并完成DI
            - default:不要自动注入
            - no     :不要自动注入
            - byName :按照名字注入(按照属性的名字在Spring中找bean)factory.getBean("属性的名字")
            - byType :按照依赖对象的类型注入(factory.getBean(属性的类型))
            - constructor :按照对象的构造器上面的参数类型注入
    -->
    <bean id="auto" class="com.revanwang.autowire.AutoBean"/>
    <bean id="autoBean" class="com.revanwang.autowire.AutoWireBean" autowire="byType"/>

</beans>

三、setter方法注入

1、使用setter注入:使用bean元素的<property>子元素设置;

  • 简单类型值,直接使用value赋值;
  • 引用类型,使用ref赋值;
  • 集合类型,直接使用对应的集合类型元素即可。

2:spring通过属性的setter方法注入值;

3:在配置文件中配置的值都是string,spring可以自动的完成类型的转换

4:属性的设置值是在init方法执行之前完成的

5:改进spring的测试,直接在测试类里面注入需要测试的对象 set方法注入简单类型、集合类型、对象类型

Address


import lombok.Data;

@Data
public class Address {
    private String  id;
    private String  name;

    public Address() {}

    public Address(String id, String name) {
        this.id = id;
        this.name = name;
    }
}

Employee


import lombok.Data;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

@Data
public class Employee {
    private Long    id;
    private String  name;
    private Set<String>     emailSet;
    private List<String>    emailList;
    private String[]        emailArray;
    private Map<String, Object> map;
    private Properties      ppts;

    private Address         address;
}

employee.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">


    <!-- 通过构造器给Address注入值 -->
    <bean id="address" class="com.revanwang.setter.Address">
        <constructor-arg name="id" value="11"/>
        <constructor-arg name="name" value="Revan"/>
    </bean>

    <bean id="employee" class="com.revanwang.setter.Employee">
        <property name="id" value="28"/>
        <property name="name" value="Revan"/>
        <property name="emailSet">
            <set>
                <value>emailSet1</value>
                <value>emailSet2</value>
                <value>emailSet3</value>
            </set>
        </property>

        <property name="emailArray">
            <array>
                <value>emailArray1</value>
                <value>emailArray2</value>
                <value>emailArray3</value>
            </array>
        </property>

        <property name="emailList">
            <list>
                <value>emailList1</value>
                <value>emailList2</value>
                <value>emailList3</value>
            </list>
        </property>

        <property name="map">
            <map>
                <entry key="k1" value="v1"/>
                <entry key="k2" value="v2"/>
                <entry key="k3" value="v3"/>
            </map>
        </property>

        <property name="ppts">
            <props>
                <prop key="p1">pV1</prop>
                <prop key="p2">pV2</prop>
                <prop key="p3">pV3</prop>
            </props>
        </property>

        <property name="address" ref="address"/>

    </bean>

</beans>

四、构造器注入

constructor-arg:构造器参数

  • spring在实例化对象的时候,如果对象没有配置constructor-arg,则使用默认的构造器实例化对象
  • 如果有constructor-arg,那么spring使用这些constructor-arg来唯一确定一个构造器

默认情况下,constructor-arg的顺序就是构造器参数的顺序

  • index:在构造器中的参数位置
  • type:在构造器中的参数的类型
  • name:在构造器中按照构造器的参数名字设置值

构造器示例 Address


import lombok.Data;

@Data
public class Address {

    private Long    id;
    private String  name;
}

User

public class User {

    private Long    id;
    private String  name;

    private Address address;


    public User(Long id, String name, Address address) {
        this.id = id;
        this.name = name;
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
}

constructor.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 id="address" class="com.revanwang.constructor.Address">
        <property name="id" value="11"/>
        <property name="name" value="Revan"/>
    </bean>

    <bean id="user" class="com.revanwang.constructor.User">
        <constructor-arg name="id" value="1"/>
        <constructor-arg name="name" value="WRW"/>
        <constructor-arg name="address" ref="address"/>
    </bean>

</beans>

五、Spring 表达式语言

通过setter或者构造器给bean注入的值都是静态定义的,若要为属性动态装配值时,只能使用Spring表达式语言

import lombok.Data;

@Data
public class SP {
    private Long    id;
    private String  name;
}

SPEL


import lombok.Data;

@Data
public class SPEL {
    private Long    id;
    private String  name;

}

spel.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 name="sp" class="com.revanwang.spel.SP">
        <property name="id" value="111"/>
        <property name="name" value="Revan"/>
    </bean>

    <bean name="spel" class="com.revanwang.spel.SPEL">
        <property name="id" value="#{11}"/>
        <property name="name" value="#{sp.name}"/>
    </bean>

</beans>