首页 > 其他分享 >Spring IOC官方文档学习笔记(三)之依赖项

Spring IOC官方文档学习笔记(三)之依赖项

时间:2022-12-22 19:22:52浏览次数:38  
标签:依赖 ExampleB ExampleA Spring class bean 文档 IOC public

1.依赖注入

(1) 依赖注入(DI)的概念:某个bean的依赖项,由容器来负责注入维护,而非我们自己手动去维护,以此来达到bean之间解耦的目的,如下

//情况一:不使用依赖注入
public class A {}

public class B {
  //B依赖了A对象,这种依赖关系是由我们自己手动来维护的,编码于代码之中,是强依赖
  private A a = new A();
}

//情况二:使用依赖注入
@Component
public class A {}

@Component
public class B {
  //B依赖了A对象,这个A对象是由容器来提供的,无需我们关心
  @Autowired
  private A a;
}

(2) 依赖注入的两种方式

  • 基于构造函数的依赖注入:容器通过调用带有参数的构造函数来完成依赖项的注入,其中构造函数中的每个参数都代表一个依赖项,其参数解析规则如下
//例一:
//ExampleA继承自ExampleB
public class ExampleA extends ExampleB {}

public class ExampleB {}

public class Combine {
    //Combine依赖了ExampleA和ExampleB
    public Combine(ExampleA a, ExampleB b) {
        
    }
}

<!-- xml配置文件 -->
<beans ...>
    <bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean>
    <bean id="exampleB" class="cn.example.spring.boke.ExampleB"></bean>

    <bean id="combine" class="cn.example.spring.boke.Combine">
        <!-- Spring会按照类型进行精确匹配,因此1会被注入到构造函数的第二个参数b中,而2会被注入到构造函数的第一个参数a中,此时与这些构造函数标签声明的先后顺序无关 -->
        <constructor-arg ref="exampleB"></constructor-arg> <!-- 1 -->
        <constructor-arg ref="exampleA"></constructor-arg> <!-- 2 -->
    </bean>
</beans>

//例二:
//将Combine构造函数变更一下
public class Combine {
    //构造函数参数均为ExampleB
    public Combine(ExampleB b1, ExampleB b2) {
        
    }
}

<!-- xml配置文件 -->
<bean id="combine" class="cn.example.spring.boke.Combine">
    <!-- 此时无法进行精确匹配,因为构造函数参数均为ExampleB,这时就会按照这些构造函数标签声明的先后顺序进行依赖项的注入,
         结果为1会被注入到构造函数的第一个参数b1中,而2会被注入到构造函数的第二个参数b2中,如果将这两个构造函数标签的声明顺序颠倒一下,结果也会随之相反 -->
    <constructor-arg ref="exampleB"></constructor-arg> <!-- 1 -->
    <constructor-arg ref="exampleA"></constructor-arg> <!-- 2 -->
</bean>

//例三
public class ExampleC {
    public ExampleC(int number, String str) {
        
    }
}

<!-- xml配置文件1 -->
<beans ...>
    <bean id="c" class="cn.example.spring.boke.ExampleC">
        <!-- 此时的Spring无法判断2000或50000究竟是一个int还是一个String,因此它首先会采取例二中的办法,按构造函数标签的声明顺序进行注入,结果为number被注入2000,str被注入字符串50000 -->
        <constructor-arg value="2000"></constructor-arg>
        <constructor-arg value="50000"></constructor-arg>
    </bean>
</beans>

<!-- xml配置文件2 -->
<beans ...>
    <bean id="c" class="cn.example.spring.boke.ExampleC">
        <!-- 可以使用type属性,指定希望注入的参数的类型,来解决歧义问题,结果number被注入50000,str被注入字符串2000,但是如果构造函数参数列表中有多个类型相同的参数,则又会按照构造函数标签的声明顺序进行注入  -->
        <constructor-arg type="java.lang.String" value="2000"></constructor-arg>
        <constructor-arg type="int" value="50000"></constructor-arg>
    </bean>
</beans>

<!-- xml配置文件3 -->
<bean id="c" class="cn.example.spring.boke.ExampleC">
    <!-- 使用index属性,按构造函数参数索引位置进行注入,index属性为0的构造函数标签的值会被注入到构造函数的第一个参数,以此类推 -->
    <constructor-arg index="1" value="50000"></constructor-arg>
    <constructor-arg index="0" value="2000"></constructor-arg>
</bean>

<!-- xml配置文件4 -->
<bean id="c" class="cn.example.spring.boke.ExampleC">
    <!-- 使用name属性,按照构造函数参数名称进行注入,不过要注意,Spring要求使用这种方式时必须开启debug flag或结合@ConstructorProperties注解一起使用,详见官方文档(此处存疑,因为在我本地可直接使用下面这种方式,无需其他配置) -->
    <constructor-arg name="str" value="50000"></constructor-arg>
    <constructor-arg name="number" value="2000"></constructor-arg>
</bean>
  • 基于setter方法的依赖注入:在实例化bean之后,容器会调用setter方法来注入依赖项
public class ExampleA {}

public class ExampleC {
    private ExampleA exampleA;
    
    //使用setter方法注入依赖项exampleA
    public void setExampleA(ExampleA exampleA) {
        this.exampleA = exampleA;
    }
}

<!-- 方法一:使用自动装配(autowire),即容器自动去寻找依赖项来进行注入 -->
<beans ...>
    <!-- 被依赖项exampleA -->
    <bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean>

    <!-- 注意,在基于xml的配置中,如要使用基于setter的自动装配,则要指定autowire属性为byName或byType,如果不指定,则autowire属性默认为no,即不进行依赖注入 -->
    <bean id="exampleC" class="cn.example.spring.boke.ExampleC" autowire="byType"></bean>
</beans>

<!-- 方法二:使用手动装配 -->
<beans ...>
    <bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean>

    <bean id="exampleC" class="cn.example.spring.boke.ExampleC">
        <!-- 使用property标签手动注入,name即属性名称,ref代表注入对象 -->
        <property name="exampleA" ref="exampleA"></property>
    </bean>
</beans>

(3) 在依赖注入过程中,Spring会结合PropertyEditor一起使用,来将属性从一种类型转换为另一种类型,我们也可以利用PropertyEditor进行数据类型转换工作

(4) Spring对依赖项的解析规则:

  • 创建ApplicationContext,并填充bean的配置元数据
  • bean与bean之间的依赖关系蕴含于成员变量,构造函数参数等之中,当一个bean被创建时,它的依赖项会被提供
  • 每个成员变量值或构造函数参数值要么是我们所提供的值,要么是对容器中另一个bean的引用
  • Spring会对成员变量值或构造参数值进行确切的类型转换,以得到实际的类型,比如 <constructor-arg value="2000" ... 中的值2000,Spring会根据实际情况将其转换为字符串2000或int值2000

(5) Spring在容器创建完成后便会对bean的配置元数据进行检验(此时不会进行依赖项的注入,直到bean被成功创建后才会进行注入)。单例bean在默认情况下会被提前实例化(即在容器被创建后创建),而其他作用域的bean只有在需要的时候才会被创建(懒加载)。在实际创建bean时,Spring会尽可能晚设置该bean的属性值和依赖项,因此Spring容器可能在创建的时候正常但在之后的使用中会产生异常

(6) 在不存在循环依赖的情况下,bean A如果有一个依赖项为bean B,则Spring容器会在调用bean A的setBeanB方法来注入bean B之前会完全配置好bean B,即Bean B的依赖项已被完全注入,其生命周期回调也已被执行,如下

/**
  * 提供三个类,ExampleA,ExampleB和ExampleC,其中ExampleA依赖了ExampleB,而ExampleC又依赖了ExampleA,即B->A->C
  * 此时,容器会先配置好ExampleB,再注入给ExampleA,而ExampleA配置完毕后,最后会被注入给ExampleC,可观察控制台打印语句的输出顺序
  */
public class ExampleB {
    public ExampleB() {
        System.out.println("ExampleB构造器...");
    }
}

public class ExampleA {
    public ExampleA() {
        System.out.println("ExampleA构造器...");
    }

    private ExampleB exampleB;

    public void setExampleB(ExampleB exampleB) {
        System.out.println("ExampleA设置了"  + exampleB);
        this.exampleB = exampleB;
    }
}

public class ExampleC {

    public ExampleC() {
        System.out.println("ExampleC构造器...");
    }

    private ExampleA exampleA;

    public void setExampleA(ExampleA exampleA) {
        System.out.println("ExampleC设置了" + exampleA);
        this.exampleA = exampleA;
    }
}

<!-- xml配置文件 -->
<beans ...>
    <bean id="exampleC" class="cn.example.spring.boke.ExampleC">
        <property name="exampleA" ref="exampleA"></property>
    </bean>

    <bean id="exampleA" class="cn.example.spring.boke.ExampleA">
        <property name="exampleB" ref="exampleB"></property>
    </bean>

    <bean id="exampleB" class="cn.example.spring.boke.ExampleB"></bean>
</beans>

未完待续...

标签:依赖,ExampleB,ExampleA,Spring,class,bean,文档,IOC,public
From: https://www.cnblogs.com/shame11/p/16994777.html

相关文章

  • spring学习总结
    学习完之后感觉spring使用注解开发真的比以前方便了许多1.Spring的出现,是为了高效的完成软件开发,依照软件设计模式的“高内聚低耦合”原则,实现前后端分离,达到不改变原有......
  • springboot学习总结
    刚学完spring的时候就学了一部分的springboot 感觉区别就在于springboot更加的高效 他为了方便开发 在spring的基础上面更加简化了一些配置他有非常大的有点比如:1.......
  • tokio官方文档中一些值得记录的
    Rust真tema难啊...任务Tokio任务是一个异步绿色线程,它们通过向tokio::spawn中传递一个async块来创建。tokio::spawn函数返回一个JoinHandle,调用者可能使用它来与被创建的......
  • spring-ioc容器理解
    ioc:控制反转;将对象的创建、配置、销毁,生命周期的管理交予容器;容器的核心功能可拆分为:解析配置创建对象对象的生命周期管理解析配置容器对于一个项目要创建哪些对象......
  • SpringBoot项目Jar包构建Docker镜像
    在jar包同级目录创建Dockerfile文件,写入一下:FROMopenjdk:11MAINTAINERdemoADD[jar包文件][生成的镜像名称]EXPOSE8110//镜像暴露的端口号ENTRYPOINT["java",......
  • 使用WinSW将springboot jar项目安装为windows服务
    1、下载WinSW:https://github.com/winsw/winsw/releases,此处下载WinSWv2.11.0版,以下几个版本都行:  此处使用WinSW-x64.exe2、假如jar文件名称为:hello.jar,就将WinSW-x......
  • SpringBoot
    概述把繁琐的spring配置进行了简化,Springboot自带服务器,所以项目可以直接以jar的形式直接来运行,方便的开发测试以及部署特点核心理念:约定大于配置,比如模型名和表名一......
  • SpringBoot注解最全详解(整合超详细版本)
    使用注解的优势:1.采用纯java代码,不在需要配置繁杂的xml文件2.在配置中也可享受面向对象带来的好处3.类型安全对重构可以提供良好的支持4.减少复杂配置文件的同时亦能享......
  • 从源码层面深度剖析Spring循环依赖
    作者:郭艳红以下举例皆针对单例模式讨论图解参考https://www.processon.com/view/link/60e3b0ae0e3e74200e2478ce1、Spring如何创建Bean?对于单例Bean来说,在Spring容器......
  • Spring注解之@Import
     @Import可以导入以下几种种类:普通类实现ImportSelector接口的类实现DeferredImportSelector接口的类实现ImportBeanDefinitionRegistrar接口的类 普通......