搬砖啦,搬砖啦,这几天在看Spring相关的书,下面给大家分享一下这几天的心得与收获,Go Go Go!


Spring支持两种依赖注入方式,分别是属性注入,构造函数注入。除此之外,Spring还支持工厂注入方式。

接下来,我们一起来了解一下Spring的几种注入方式。


一.属性注入

首先来了解一下定义属性注入是指通过 setXxx()方法注入Bean的属性或依赖对象。

为什么要使用: 因为属性注入方式具有可选择性和高灵活性的特点,所以属性注入方式是实际应用中最常采用的注入方式。

来来来,直接上代码!

造个Car实体类

Car类中定义了3个属性,并分别提供了对应的Setter方法

package com.vtstar.entity;/*** @ClassName Car* @Description TODO* @Author XiaoWu* @Date 2018/9/6 9:53* @Version 1.0**/
public class Car {private int maxSpeed;public String brand;private double price;private Boss boss;public Car() {}public Car(double price, Boss boss) {System.out.println("I'm the Car's construct two " + price + " boss " + boss.getName());this.price = price;this.boss = boss;}public Car(int maxSpeed, String brand, double price) {System.out.println("车的时速是" + maxSpeed + " 品牌为:" + brand + " 价格为:" + price);this.maxSpeed = maxSpeed;this.brand = brand;this.price = price;}public int getMaxSpeed() {return maxSpeed;}public void setMaxSpeed(int maxSpeed) {System.out.println("The maximum speed is " + maxSpeed);this.maxSpeed = maxSpeed;}public String getBrand() {return brand;}public void setBrand(String brand) {System.out.println("It is a " + brand + " Car");this.brand = brand;}public double getPrice() {return price;}public void setPrice(double price) {System.out.println("The price of this car " + price);this.price = price;}
}

在Spring配置文件中对Car进行属性注入的配置桥段

<!--&&&&&&&&&&&&&&&&&&&&&&&&& setter属性方式注入 &&&&&&&&&&&&&&&&&&&&&& -->
<bean id="car" class="com.vtstar.entity.Car"><property name="brand" value="保时捷 k3"/><property name="maxSpeed" value="100"/><property name="price" value="30.1"/>
</bean>

上面的代码配置的一个Bean,class属性对应前面建好的实体Car,<property/>标签对应Bean中的每一个属性,name为属性的名称,value为参数值,在Bean中拥有与其对应的Setter方法,maxSpeed对应setMaxSpeed(),price对应setPrice();

运行一下 Tomcat,Spring容器会在Tomcat启动的时候创建;

控制台输出的结果:


二.构造函数注入

构造函数注入是除属性注入外的另外一种注入方式,它保证了一些必要的属性在Bean实例化时就得到设置,确保在Bean实例化后就可以使用。

值得一提的是构造函数注入又分为多种方式,我们慢慢来看。

1. 按类型匹配入参

如果任何可用的Car对象都需要使用到brand,maxSpeed,price的值,那使用Setter注入方式,则只能人为的在配置时提供保证而无法再语法上提供保证,那这个时候使用构造函数注入就能满足这一个要求,使用构造函数注入的前提是要保证Bean中有提供带参的构造函数。

     <!--1.根据参数类型注入--><bean id="car1" class="com.vtstar.entity.Car"><constructor-arg type="int" value="300"/><constructor-arg type="java.lang.String" value="红旗"/><constructor-arg type="double" value="20000000.9"/></bean>

在<constructor/>元素中有一个type元素,它为Spring提供了判断配置项和构造函数入参对应关系的“信息”。

控制台输出的结果:

2. 按索引匹配入参

Java语言通过入参的类型及顺序区分不同的重载方法。如果构造函数中有两个类型相同的入参,那么使用第一种方式是行不通的,因为type无法确认对应的关系。这时我们就需要使用索引匹配入参的方式来进行确认。

为了更好的演示按索引匹配入参,将Car构造函数进行了修改

 public Car(String brand, String corp, double price) {System.out.println("brand :" + brand + " corp :" + corp + " price :"+price);this.brand = brand;this.corp = corp;this.price = price;}
    <!--2.通过入参位置下标--><bean id="car2" class="com.vtstar.entity.Car"><constructor-arg index="0" value="400"/><constructor-arg index="1" value="大众辉腾"/><constructor-arg index="2" value="20000000"/></bean>

因为brand和corp都是String类型,所以Spring无法确定type为String的<constructor-arg/>到底对应的是brand还是corp。但是这种按索引匹配入参的方式能够消除这种不确定性

控制台输出的结果:

3. 联合使用类型和索引匹配入参

有时需要Type和index联合使用才能确定配置项和构造函数入参的对应关系,举个栗子

    public Car(String brand, String corp, double price) {System.out.println("brand :" + brand + " corp :" + corp + " price :"+price);this.brand = brand;this.corp = corp;this.price = price;}public Car(String brand, String corp, int maxSpeed) {System.out.println("brand :" + brand + " corp :" + corp + " maxSpeed :"+maxSpeed);this.brand = brand;this.corp = corp;this.maxSpeed = maxSpeed;}

在这里,Car拥有两个重载的构造函数,它们都有两个相同类型的入参,按照index的方式针对这样的情况又难以满足了这时就需要联合使用<constructor-arg/>中的type和index了。

    <!--3.通过参数类型和入参位置联合注入--><bean id="car3" class="com.vtstar.entity.Car"><constructor-arg index="0" type="java.lang.String" value="30000000.0"/><constructor-arg index="1" type="java.lang.String" value="卡迪拉克"/><constructor-arg index="2" type="int" value="400"/></bean>

对于上图的代码清单如果只根据index来进行匹配入参,那么Spring无法确认第三个参数是price还是maxSpeed了,所以解决这种有歧义的冲突,请将type和index结合使用,对于因参数数目相同而类型不同引起的潜在配置歧义问题,Spring容器可以正确的启动且不会给出报错信息,他将随机采用一个匹配的构造函数实例化Bean,而被选择的构造函数可能并不是用户所期望的那个。因此,必须要特别谨慎,以避免潜在的错误。

控制台输出的结果:

4.通过自身类型反射入参

如果Bean的构造函数入参类型是可辨别的,什么是可辨别的入参类型呢?(非基础数据类型且入参类型各异

我们再建一个Boss实体类,在Boss类中引用Car类

package com.vtstar.entity;import java.util.Date;/*** @ClassName Boss* @Description TODO* @Author XiaoWu* @Date 2018/9/6 11:22* @Version 1.0**/
public class Boss {private String name;private Car car;private Integer age;public Boss() {}public Boss(String name, Car car,Integer age) {System.out.println("The name of the boss " + name + " ,He has a  " + car.getBrand()+" age is "+age);this.name = name;this.car = car;this.age = age;}public String getName() {return name;}public Car getCar() {return car;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}
}

Spring配置Boss相关的Bean,由于name, car ,age入参都是可辨别的,所以无须在<constructor-arg/>中指定type和index。

    <!--4.通过自身类型反射入参--><bean id="boss" class="com.vtstar.entity.Boss"><constructor-arg value="Tom"/><constructor-arg ref="car1"/><constructor-arg value="20"/></bean>

但是为了避免潜在配置歧义引起的张冠李戴的情况,如果Bean存在多个构造函数,那么显式指定index和type属性是一种良好的配置习惯。

看控制台输出的结果:

5.循环依赖问题

Spring容器对构造函数配置Bean进行实例化有一个前提,即Bean构造函数入参引用的对象必须已经准备就绪。由于这个机制,如果两个Bean都相互引用,都采用构造函数注入方式,就会发生类似于线程死锁的循环依赖问题。

    <!--5.循环依赖注入--><bean id="boss1" class="com.vtstar.entity.Boss"><constructor-arg index="0" value="Tom"/><constructor-arg index="1" ref="car4"/><constructor-arg index="2" value="20"/></bean><bean id="car4" class="com.vtstar.entity.Car"><constructor-arg index="0" value="232.9"/><constructor-arg index="1" ref="boss1"/></bean>

控制台输出的结果:(这就是采用循环注入方式产生最大的问题)

如何解决这种问题?将相互依赖的两个Bean中的其中一个Bean采用Setter注入的方式即可。

    <!--5.循环依赖注入--><bean id="boss1" class="com.vtstar.entity.Boss"><constructor-arg index="0" value="Tom"/><constructor-arg index="1" ref="car"/></bean><bean id="car" class="com.vtstar.entity.Car"><property name="brand" value="保时捷 k3"/><property name="maxSpeed" value="100"/><property name="price" value="30.1"/></bean>

控制台输出结果:


构造函数注入方式:
优点:
       1.构造函数可以保证一些重要的属性在bean实例化的时候就设置好,避免因为一些重要的属性没有提供而导致一个无用的Bean 实例情况
       2.不需要为每个属性提供Setter方法,减少了类的方法个数
       3.可以更好的封装类变量,不需要为每个属性提供Setter方法,避免外部错误的调用

缺点:
      1.如果一个类属性太多,那么构造函数的参数将变成一个庞然大物,可读性较差
      2.灵活性不强,在有些属性是可选的情况下,如果通过构造函数注入,也需要为可选的参数提供一个null值
      3.如果有多个构造函数,则需要考虑配置文件和具体构造函数匹配歧义的问题,配置上相对复杂
      4.构造函数不利于类的继承和拓展,因为子类需要引用父类复杂的构造函数
      5.构造函数注入有时会造成循环依赖的问题

三. 工厂注入

既然需要一个工厂,那么我们需要创建一个CarFactory类

package com.vtstar.ioc;import com.vtstar.entity.Car;/*** @ClassName CarFactory* @Description TODO* @Author XiaoWu* @Date 2018/9/6 13:55* @Version 1.0**/
public class CarFactory {/** @methodName createHongQiCar* @Description 创建红旗轿车制造工厂* @Date 2018/9/6 13:58* @Param []* @return com.vtstar.entity.Car**/public Car createHongQiCar(){Car car = new Car();car.setBrand("红旗H1");System.out.println("这里是非静态工厂的创建..." + car.getBrand());return car;}/** @methodName createDaZhongCar* @Description 创建大众汽车制造工厂* @Date 2018/9/6 14:02* @Param []* @return com.vtstar.entity.Car**/public static Car createDaZhongCar(){Car car = new Car();car.setBrand("大众GoGoGo");System.out.println("这里是静态工厂的创建..." + car.getBrand());return car;}
}

工厂注入方式分为 静态工厂和非静态工厂,相关Spring配置如下:

    <!--非静态注入工厂方法--><bean id="carFactory" class="com.vtstar.ioc.CarFactory"/><bean id="car5" factory-bean="carFactory" factory-method="createHongQiCar"/><!--静态注入工厂方法--><bean id="car6" class="com.vtstar.ioc.CarFactory" factory-method="createDaZhongCar"/>

看控制台输出的结果:


Spring 提供给了我们多种注入方式,需要使用哪种方式各位同鞋可以根据自身的场景下考量,

这篇文章就分享到这啦,溜啦溜啦~~

Spring 依赖注入三种方式的实现,及循环依赖问题的解决(源码+XML配置)相关推荐

  1. spring依赖注入三种方式

    首先,要学习Spring中的Bean的注入方式,就要先了解什么是依赖注入. 依赖注入是指:让调用类对某一接口的实现类的依赖关系由第三方注入,以此来消除调用类对某一接口实现类的依赖. Spring容器中 ...

  2. Spring学习3 - Bean的属性、DI依赖注入三种方式

    文章目录 1. 属性设置 - XML配置文件.以及与其等价的注解形式 2. 依赖注入DI的方式 2.1 属性自动注入 - 不推荐 代码准备阶段 代码测试 2.2 子节点构造器注入 - 不推荐 2.3 ...

  3. 22 springboot依赖注入三种方式

    1 基于构造函数的依赖注入 Spring 基于构造函数的依赖注入_w3cschoolJ虽然当前有关Spring Framework(5.0.3)的文档仅定义了两种主要的注入类型,但实际上有三种 pub ...

  4. Spring IoC注入三种方式

    三种注入方式: setter & getter 注入 构造函数注入 p命名空间注入 有两个实体类 student: public class Student {String name;int ...

  5. Spring循环依赖的三种方式以及解决办法

    Spring循环依赖的三种方式以及解决办法 [转]https://www.cnblogs.com/liuqing576598117/p/11227007.html 示例 https://github. ...

  6. Spring集成Memcached三种方式(一)

    Spring集成Memcached三种方式(一) 转载:http://blog.csdn.net/u013725455/article/details/52102170 Memcached Clien ...

  7. IoC、Spring 环境搭建、Spring 创建对象的三种方式、DI

    二.IoC 中文名称:控制反转 英文名称:(Inversion of Control) 3.I oC 是什么? 3.1 IoC 完成的事情原先由程序员主动通过 new 实例化对象事情,转交给 Spri ...

  8. Spring 学习之 二----Spring创建对象的三种方式

    最近在系统的学习Spring,现在就Spring的一些知识进行总结. 我们知道Spring是一个开放源代码的设计层面的框架,他主要解决的是业务逻辑层与其他各层之间松耦合的问题. Spring 有三个核 ...

  9. Spring创建对象的三种方式以及创建时间

    创建对象的三种方式: 1.采用默认的构造函数创建 2.采用静态工厂方法 1.写一个静态工厂方法类 public class HelloWorldFactory { public static Hell ...

最新文章

  1. 深度学习 vs 机器学习 vs 模式识别
  2. JWT(JSON Web Token)的基本原理
  3. markdown生成html不出效果,mdeditor: 简单markdown编辑器,同步预览html效果。不依赖任何插件,使用简单,原创,造轮子中。。。更新中。。。...
  4. 关于多级导航如何实现 详细解释。
  5. python定义输入变量_Python 2 声明变量 输入输出 练习
  6. 新书出版 |《数据库程序员面试笔试宝典》
  7. 帅地鸽了大半年,一个专注于面试、基础知识、算法的小破站上线了
  8. PSpice应用B-2
  9. 北京时间校准 服务器 显示器 标准,纯干货~北京时间校准显示器实用分享
  10. WEB 安全之 SQL注入一 盲注
  11. android 动画-补间动画
  12. 用二次函数研究三次多项式函数的零点问题【中阶和高阶辅导】
  13. python 马赛克拼图_使用 python 做到马赛克拼图
  14. 都这样了!我还是没法关闭微信朋友圈广告
  15. 一元线性回归方程的参数估计
  16. android 高德地图设置不能旋转_高德地图行车记录仪AR导航怎么设置使用教程
  17. 使用php读取文件中的内容,以表格的形式进行输出。
  18. 编写一个留言簿程序,写入留言提交后显示留言内容
  19. 08_Python算法+数据结构笔记-二叉搜索树查询/删除-AVL树旋转/插入/应用-贪心算法
  20. 【algods】4.树和二叉树、完全二叉树、满二叉树、二叉查找树、平衡二叉树、堆、哈夫曼树、散列表...

热门文章

  1. QT编译报错:Error while building/deploying project xxx When executing step qmake
  2. T-SQL程序练习04
  3. debug没问题,release有问题的情况
  4. 美团技术年货:1300+页电子书,覆盖前端、后台、数据、算法、顶会论文……...
  5. (c++)请编写程序,输入正整数 n,计算平方和 s=1 平方 +2 平方+3平方+⋯+n 平方。
  6. 市场逆风中,海尔智家如何续写增长故事
  7. 有红、白、黑三种球若干个,其中红、白球共25个,白、黑球共31个,红、黑球共28个,使用for循环完成计算这三种球的个数
  8. elasticsearch报错: illegal argument exception, variable is not found
  9. GYP使用总结 ---- 从Makefile到GYP
  10. scrolltop 原生js_jQuery 中 scrollTop;原生的js怎么写