-
2019-06-03 14:33:44
Spring通过DI(依赖注入)实现IOC(控制反转),常用的注入方式主要有三种:构造方法注入(Construct注入),setter注入,基于注解的注入(接口注入)。(参考https://blog.csdn.net/jinchaoh/article/details/80246577)
构造方法注入
如果只有一个有参数的构造方法并且参数类型与注入的bean的类型匹配,那就会注入到该构造方法中。
Setter注入
在XML中写入,然后在set方法中注入。示
例如下:
<!-- 注册userService --> <bean id="userService" class="com.lyu.spring.service.impl.UserService"> <!-- 写法一 --> <!-- <property name="UserDao" ref="userDaoMyBatis"></property> --> <!-- 写法二 大小写无所谓,因为Spring会将每个单词首字母自动改为大写--> <property name="userDao" ref="userDaoMyBatis"></property> </bean> <!-- 注册mybatis实现的dao --> <bean id="userDaoMyBatis" class="com.lyu.spring.dao.impl.UserDaoMyBatis"></bean>
然后在set中使用
public class UserService implements IUserService { private IUserDao userDao1; public void setUserDao(IUserDao userDao1) {//这里注意,name方法与类中成员变量名和方法的参数名都无关,只与set方法名有关 this.userDao1 = userDao1; } public void loginUser() { userDao1.loginUser(); } }
如果通过set方法注入属性,那么spring会通过默认的空参构造方法来实例化对象,所以如果在类中写了一个带有参数的构造方法,一定要把空参数的构造方法写上(必须要有空参构造方法),否则spring没有办法实例化对象,导致报错。
基于注解的注入@Autowired(自动注入)修饰符有三个属性:Constructor,byType,byName。默认按照byType注入。
constructor:通过构造方法进行自动注入,spring会匹配与构造方法参数类型一致的bean进行注入,如果有一个多参数的构造方法,一个只有一个参数的构造方法,在容器中查找到多个匹配多参数构造方法的bean,那么spring会优先将bean注入到多参数的构造方法中。
byName:被注入bean的id名必须与set方法后半截匹配,并且id名称的第一个单词首字母必须小写,这一点与手动set注入有点不同。
byType:查找所有的set方法,将符合符合参数类型的bean注入。主要有四种注解可以注册bean,每种注解可以任意使用,只是语义上有所差异:
- @Component:可以用于注册所有bean
- @Repository:主要用于注册dao层的bean
- @Controller:主要用于注册控制层的bean
- @Service:主要用于注册服务层的bean
在写DAO函数的时候,一般要写一个@Mapper,作用为:(参考https://blog.csdn.net/weixin_39666581/article/details/81057385)
1:为了把mapper这个DAO交给Spring管理
2:为了不再写mapper映射文件
3:为了给mapper接口 自动根据一个添加@Mapper注解的接口生成一个实现类
更多相关内容 -
Spring依赖注入的三种方式实例详解
2020-10-19 02:20:11主要介绍了Spring依赖注入的三种方式的相关资料,希望通过本文能帮助到大家,让大家理解掌握这部分内容,需要的朋友可以参考下 -
spring四种依赖注入方式的详细介绍
2020-08-31 12:03:53本篇文章主要介绍了spring四种依赖注入方式的详细介绍,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 -
Spring依赖注入的方式
2019-04-19 01:40:32NULL 博文链接:https://huangminwen.iteye.com/blog/1041743 -
spring依赖注入的三种方式以及优缺点
2021-04-13 14:45:03spring依赖注入的三种方式以及优缺点 一.依赖注入的三种方式 1.通过构造器注入。(spring4.3之后,推荐使用) 2.通过setter注入。(spring4.3之前,推荐使用) 3通过filed注入。 二.三种方式的代码示例: ...spring依赖注入的三种方式以及优缺点
一.依赖注入的三种方式
1.通过构造器注入。(spring4.3之后,推荐使用)
2.通过setter注入。(spring4.3之前,推荐使用)
3通过filed注入。
二.三种方式的代码示例:
Constructor注入
private AlarmContactService alarmContactService; private final AlarmService alarmService; private final SysUserService sysUserService; @Autowired public AlarmContactController(AlarmContactService alarmContactService, AlarmService alarmService, SysUserService sysUserService) { this.alarmContactService = alarmContactService; this.alarmService = alarmService; this.sysUserService = sysUserService; }
Setter注入
private AlarmContactService alarmContactService; private AlarmService alarmService; private SysUserService sysUserService; @Autowired public void setAlarmContactService(AlarmContactService alarmContactService) { this.alarmContactService = alarmContactService; } @Autowired public void setAlarmService(AlarmService alarmService) { this.alarmService = alarmService; } @Autowired public void setSysUserService(SysUserService sysUserService) { this.sysUserService = sysUserService; }
Field注入
@Autowired private AlarmContactService alarmContactService; @Autowired private AlarmService alarmService; @Autowired private SysUserService sysUserService;
三.3种方式的各优点和缺点
三种方式的优点分析
1.基于构造器注入,会固定依赖注入的顺序,不允许我们创建的bean对象之间存在循环依赖关系,这样Spring能解决循环依赖的问题。
2.基于setter注入,只有对象是需要被注入的时候,才会注入依赖,而不是在初始化的时候就注入。
3.在成员变量上写上注解来注入,这种方式,精短,可读性高,不需要多余的代码,也方便维护。
三种方式的缺点分析
1.使用构造器注入的缺点是,当我们构造器需要注入的对象比较多时,会显得我们的构造器,冗余,不美观,可读性差,也不易维护。
2.当我们选择setter方法来注入的时候,我们不能将对象设为final的;
3.当我们在field变量上来实现注入的时候
a.这样不符合JavaBean的规范,而且很有可能引起空指针;
b.同时也不能将对象标为final的;
c.类与DI容器高度耦合,我们不能在外部使用它;
d.类不通过反射不能被实例化(例如单元测试中),你需要用DI容器去实例化它,这更像集成测试;
来自Spring官方文档的建议:
在Spring 3.x 中,Spring团队建议我们使用setter来注入:而在Spring 4.x 中,Spring团队不再建议我们使用setter来注入,改为了constructor:
Spring团队通常建议使用构造器来注入,因为它允许一个应用程序组件实现为不可变对象,并确保所需的依赖项不是空。此外构造器注入组件总是返回一个完全初始化状态的client客户端(调用)。附注,大量的构造函数参数是一个糟糕的代码习惯,看起来也很坏,这意味着类可能有太多的责任,应该被重构,以更好地解决适当的关注点分离。
三.解释下什么是循环依赖:
1. 循环依赖是什么?
Bean A 依赖 B,Bean B 依赖 A这种情况下出现循环依赖。
Bean A → Bean B → Bean A 或者 Bean A → Bean B → BeanC → Bean A
2. 循环依赖会产生什么结果?
当Spring正在加载所有Bean时,Spring尝试以能正常创建Bean的顺序去创建Bean。
例如,有如下依赖:
Bean A → Bean B → Bean C
Spring先创建beanC,接着创建bean B(将C注入B中),最后创建bean A(将B注入A中)。
假如,有如下循环依赖:
Bean A → Bean B → Bean C → BeanD → Bean A
但当存在循环依赖时,Spring将无法决定先创建哪个bean。这种情况下,Spring将产生异常BeanCurrentlyInCreationException。
一条Spring 4.3 的新特征:
在Spring 4.3 以后,如果我们的类中只有单个构造函数,那么Spring就会实现一个隐式的自动注入:
就是我去掉了构造器上的@Autowired注解,经测试后发现,程序能正常运行。alarmContactService,alarmService,sysUserService的依赖也被成功注入了。
private AlarmContactService alarmContactService; private final AlarmService alarmService; private final SysUserService sysUserService; public AlarmContactController(AlarmContactService alarmContactService, AlarmService alarmService, SysUserService sysUserService) { this.alarmContactService = alarmContactService; this.alarmService = alarmService; this.sysUserService = sysUserService; }
使用构造注入允许加入final,这也表示以后不能再被更改了。
-
Spring系列之依赖注入的三种方式
2019-05-22 10:13:39一、依赖注入方式 1.使用属性的setXXX方法注入 2.构造函数注入 (1)按类型匹配入参type (2)按索引匹配入参index (3)联合使用类型和索引匹配入参[type和index一起使用] 有时需要联合使用type和index才能...目录
(3)联合使用类型和索引匹配入参[type和index一起使用] 有时需要联合使用type和index才能确定匹配项和构造函数入参的对应关系,看下面的代码。
2.2.1 装配分为四种:byName, byType, constructor, autodetect。
2.3.1 常用的自动装配注解有以下几种:@Autowired,@Qualifier,@Named,@Resource,@Inject。
2.4 自动检测配置,也是springmvc中最牛的一项功能:只要一个配置,base-package属性指定要自动检测扫描的包。
转载:傻傻分不清:Spring IoC注入,自动装配与循环依赖
一、依赖注入方式
对于spring配置一个bean时,如果需要给该bean提供一些初始化参数,则需要通过依赖注入方式,所谓的依赖注入就是通过spring将bean所需要的一些参数传递到bean实例对象的过程(将依赖关系注入到对象中),spring的依赖注入有3种方式:
·使用属性的setter方法注入 ,这是最常用的方式;
·使用构造器注入;最好的解决方案是用构造器参数实现强制依赖,setter 方法实现可选依赖。
1.使用属性的setXXX方法注入
属性注入即通过setXxx()方法注入Bean的属性值或依赖对象,由于属性注入方式具有可选择性和灵活性高的优点,因此属性注入是实际应用中最常采用的注入方式。
<bean id=”……” class=”……”> <property name=”属性1” value=”……”/> <property name=”属性2” ref=”……”/> …… </bean>
属性注入要求Bean提供一个默认的构造函数,并为需要注入的属性提供对应的Setter方法。Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用Setter方法注入属性值。来看一个简单的例子。
package com.spring.model; public class Car { private int maxSpeed; private String brand; private double price; public int getMaxSpeed() { return maxSpeed; } //一定要写被注入对象的set方法 public void setMaxSpeed(int maxSpeed) { this.maxSpeed = maxSpeed; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public void run(){ System.out.println("brand:"+brand+",maxSpeed:"+maxSpeed+",price:"+price); } }
Car类中定义了3个属性,并分别提供了对应的Setter方法。(注:默认构造函数是不带参的构造函数。Java语言规定如果类中没有定义任何构造函数,则JVM自动为其生成一个默认的构造函数。反之,如果类中显示定义了构造函数,则JVM不会为其生成默认的构造函数。所以假设Car类中显示定义了一个带参的构造函数,如public Car(String brand),则需要同时提供一个默认构造函数public Car(),否则使用属性注入时将抛出异常。)
下面在Spring配置文件中对Car进行属性注入:
<!-- 属性注入 --> <bean id="car" class="com.spring.model.Car"> <property name="maxSpeed" value="200"></property> <property name="brand" value="红旗CA72"></property> <property name="price" value="200000.00"></property> </bean>
在上述代码中配置了一个Bean,并为该Bean的3个属性提供了属性值。具体来说,Bean的每一个属性对应一个<property>标签,name为属性的名称,在Bean实现类中拥有与其对应的Setter方法:maxSpeed对应setMaxSpeed(),brand对应setBrand()。
需要指出的是:Spring只会检查Bean中是否有对应的Setter方法,至于Bean中是否有对应的属性变量则不做要求。例如配置文件中<property name="brand"/>的属性配置项仅要求Car类中拥有setBrand()方法,但Car类不一定要拥有brand成员变量。2.构造函数注入
构造函数注入是除属性注入之外的另一种常用的注入方式,它保证一些必要的属性在Bean实例化时就得到设置(construct是bean生命周期的第一步,实例化bean),并且确保了Bean实例在实例化后就可以使用。
使用方式:
第一,在类中,不用为属性设置setter方法(但是可以有),但是需要生成该类带参的构造方法。
第二,在配置文件中配置该类的bean,并配置构造器,在配置构造器中用到了<constructor-arg>节点,该节点有四个属性:
· index是索引,指定注入的属性位置,从0开始;
· type是指该属性所对应的类型;
· ref 是指引用的依赖对象;
· value 当注入的不是依赖对象,而是基本数据类型时,就用value;
(1)按类型匹配入参type
如果任何可用的Car对象都必须提供maxSpeed、brand和price的值,使用属性注入方式只能人为在配置时提供保证,而无法在语法级提供保证,这时通过构造函数注入就可以很好地满足这一要求。使用构造函数注入的前提是Bean必须提供带参的构造函数,下面为Car提供一个可设置maxSpeed、brand和price属性的构造函数。
package com.spring.model; public class Car { private int maxSpeed; private String brand; private double price; //带参构造方法 public Car(int maxSpeed,String brand, double price){ this.maxSpeed=maxSpeed; this.brand=brand; this.price=price; } public void run(){ System.out.println("brand:"+brand+",maxSpeed:"+maxSpeed+",price:"+price); } }
构造函数注入的配置方式和属性注入方式的配置有所不同,在spring配置文件中使用构造函数注入装配这个Car Bean。
<!-- 构造函数注入(按类型匹配) --> <bean id="car1" class="com.spring.model.Car"> <constructor-arg type="int" value="300"></constructor-arg> <constructor-arg type="java.lang.String" value="宝马"></constructor-arg> <constructor-arg type="double" value="300000.00"></constructor-arg> </bean>
在<constructor-arg>的元素中有一个type属性,它表示构造函数中参数的类型,为spring提供了判断配置项和构造函数入参对应关系的“信息”。
(2)按索引匹配入参index
我们知道,Java语言通过入参的类型及顺序区分不同的重载方法,对于上面代码中的Car类,Spring仅通过type属性指定的参数类型就可以知道“宝马”对应String类型的brand入参,而“300000.00”对应double类型的price入参。但是,如果Car构造函数3个入参的类型相同,仅通过type就无法确定对应关系了,这时需要通过入参索引的方式进行确定。
为了更好地演示按索引匹配入参的配置方式,特意对Car构造函数进行一下调整。public Car(String brand, String corp,double price){ this.brand=brand; this.corp=corp; this.price=price; }
brand和corp的入参类型都是String,所以String将无法确定type为String的<constructor-arg>到底对应的是brand还是corp。但是,通过显示指定参数的索引能够消除这种不确定性,如下所示。
<!-- 构造函数注入(按索引匹配) --> <bean id="car2" class="com.spring.model.Car"> <!-- 注意索引从0开始 --> <constructor-arg index="0" value="宝马"></constructor-arg> <constructor-arg index="1" value="中国一汽"></constructor-arg> <constructor-arg index="2" value="300000.00"></constructor-arg> </bean>
构造函数第一个参数索引为0,第二个为1,以此类推,因此很容易知道“宝马”对应brand入参,而“中国一汽”对应corp入参。
(3)联合使用类型和索引匹配入参[type和index一起使用]
有时需要联合使用type和index才能确定匹配项和构造函数入参的对应关系,看下面的代码。public Car(String brand, String corp,double price){ this.brand=brand; this.corp=corp; this.price=price; } public Car(String brand, String corp,int maxSpeed){ this.brand=brand; this.corp=corp; this.maxSpeed=maxSpeed; }
这里,Car拥有两个重载的构造函数,它们都有三个入参。针对这种情况,按照入参索引的配置方式又难以满足要求了,这时需要联合使用<constructor-arg>的type和index才能解决问题,看下面代码。
<!-- 构造函数注入(通过入参类型和位置索引确定对应关系) --> <!-- 对应public Car(String brand, String corp,int maxSpeed)构造函数 --> <bean id="car3" class="com.spring.model.Car"> <constructor-arg index="0" type="java.lang.String" value="奔驰"></constructor-arg> <constructor-arg index="1" type="java.lang.String" value="中国一汽"></constructor-arg> <constructor-arg index="2" type="int" value="200"></constructor-arg> </bean>
对于上面的两个构造函数,如果仅通过index进行配置,Spring将无法确定第3个入参配置项究竟是对应int的maxSpeed还是double的price,采用索引匹配时,真正引起歧义的地方在于第3个入参,因此仅需要明确指定第3个入参的类型就可以取消歧义了。所以在上面的代码中,第1个和第2个<constructor-arg>元素的type属性可以去除。
对于由于参数数目相同而类型不同所引起的潜在配置歧义问题,Spring容器可以正确启动且不会给出报错信息,它将随机采用一个匹配的构造函数实例化Bean,而被选择的构造函数可能并不是用户所希望的。因此,必须特别谨慎,以避免潜在的错误。二、依赖注入的本质就是装配——自动装配:spring可以使用xml和注解来进行自动装配。自动装配就是开发人员不必知道具体要装配哪个bean的引用,这个识别的工作会由spring来完成,自动装配就是为了将依赖注入“自动化”的一个简化配置的操作
2.1 自动装配的概念
大家可以看到用xml装配bean是一件很繁琐的事情,而且我们还要找到对应类型的bean才能装配。
创建应用对象之间协作关系的行为称为装配。也就是说当一个对象的属性是另一个对象时,实例化时,需要为这个对象属性进行实例化,这就是装配。
如果一个对象只通过接口来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行切换。但是这样会存在一个问题,在传统的依赖注入配置中,我们必须要明确要给属性装配哪一个bean的引用,一旦bean很多,就不好维护了。基于这样的场景,spring使用注解来进行自动装配,解决这个问题。自动装配就是开发人员不必知道具体要装配哪个bean的引用,这个识别的工作会由spring来完成。
与自动装配配合的还有“自动检测”,这 个动作会自动识别哪些类需要被配置成bean,进而来进行装配。这样我们就明白了,自动装配是为了将依赖注入“自动化”的一个简化配置的操作。
2.2 通过xml配置的方式实现自动装配
2.2.1 装配分为四种:byName, byType, constructor, autodetect。
具体选择哪一种装配方式,需要配置<bean>标签的autowire属性,如果没有配置,默认是byName类型,就是会根据属性的名字来进行自动装配。
1)byName就是会将与属性的名字一样的bean进行装配。
2)byType就是将同属性一样类型的bean进行装配。
3)constructor就是通过构造器来将类型与参数相同的bean进行装配。
4)autodetect是constructor与byType的组合,会先进行constructor,如果不成功,再进行byType。
上面最常用的还是byName和byType。自动装配时,装配的bean必须是唯一与属性进行吻合的,不能多也不能少,有且只有一个可以进行装配的bean,才能自动装配成功。否则会抛出异常。如果要统一所有bean的自动装配类型,可以在<beans>标签中配置default-autowire属性。当然如果配置了autowire属性,我们依然可以手动装配属性,手动装配会覆盖自动装配。
2.3 spring2.5之后提供了注解方式的自动装配。
但是要使用这些注解,需要在配置文件中配置<context:annotation-config />。只有加上这一配置,才可以使用注解进行自动装配,默认情况下基于注解的装配是被禁用的。
2.3.1 常用的自动装配注解有以下几种:@Autowired,@Qualifier,@Named,@Resource,@Inject。
1)@Autowired注解是byType类型的,这个注解可以用在属性上面,setter方面上面以及构造器上面。
- 使用这个注解时,就不需要在类中为属性添加setter方法了。但是这个属性是强制性的,也就是说必须得装配上,如果没有找到合适的bean能够装配上,就会抛出异常NoSuchBeanDefinitionException。这时可以使用required=false来允许可以不被装配上,如果required=false时,则不会抛出异常。另一种情况是同时有多个bean是一个类型的,也会抛出这个异常。此时需要进一步明确要装配哪一个Bean,这时可以组合使用@Qualifier注解,值为Bean的名字即可。
2)@Qualifier注解使用byName进行装配,这样可以在多个类型一样的bean中,明确使用哪一个名字的bean来进行装配。@Qualifier注解起到了缩小自动装配候选bean的范围的作用。
- 注意:@Autowired注解是spring提供的,所以会依赖spring的包。还有一个byType的注解@Inject,与@Autowired注解作用一样,也是byType类型,而且是java ee提供的,完全可以代替@Autowired注解,但是@Inject必须是强制装配的,没有required属性,也就是不能为null,如果不存在匹配的bean,会抛出异常。@Autowired与@Qualifier可以组合使用,@Inject也有一个组合的注解,就是@Named注解,与@Qualifier作用一样,也是byName,但是不是spring的,是java ee标准的。这样就出现了两套自动装配的注解组合,@Autowired与@Qualifier是spring提供的,@Inject与@Named是java ee的。但是@Qualifier注解在java ee中也有一样,作用与spring的@Qualifier注解一模一样,只是所在的包不一样。不过建议大家使用spring的。最后还有一个@Resouce注解, 这个注解也是java ee的,也是byName类型的,原理同@Qualifier和@Named是一样的。
2.4 自动检测配置,也是springmvc中最牛的一项功能:只要一个配置<context:component-scan base-package="">,base-package属性指定要自动检测扫描的包。
该配置会自动扫描指定的包及其子包下面被构造型注解标注的类,并将这些类注册为spring bean,这样就不用在配置文件一个一个地配置成bean标签。构造型注解包括:@Controller,@Component,@Service,@Repositoryt标注的自定义注解。
生成的bean的ID默认为类的非限定名,也就是把类的名字的首字母换成小写。可以在这些注解的值中写名bean id的值,如@Controller("helloworld")。如果你想细化包被扫描的范围,可以使用<context:include-filter>和<context:exclude-filter>。具体使用方法这里不再详说。注意,没有被扫描到的类是不能注册为bean,也就不能被用来装配其他类。所以这个配置的base-package的范围非常重要。
-
Spring 依赖注入三种方式的实现,及循环依赖问题的解决(源码+XML配置)
2018-09-08 22:13:39Spring支持两种依赖注入方式,分别是属性注入,构造函数注入。除此之外,Spring还支持工厂注入方式。 接下来,我们一起来了解一下Spring的几种注入方式。 一.属性注入 首先来了解一下定义:属性注入是指通过 ...搬砖啦,搬砖啦,这几天在看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 提供给了我们多种注入方式,需要使用哪种方式各位同鞋可以根据自身的场景下考量,
这篇文章就分享到这啦,溜啦溜啦~~
-
IOC与DI的讲解以及依赖注入的三种方式
2018-03-13 19:39:03spring的核心思想是IOC和AOP,IOC-控制反转,是一个重要的面向对象编程的法则来消减计算机程序的耦合问题,控制反转一般分为两种类型,依赖注入和依赖查找,依赖什么?为什么需要依赖?注入什么?控制什么?依赖注入... -
Spring依赖注入的三种方式
2021-10-26 21:10:16Spring的依赖注入,我们一般使用@Autowired注解来完成,关于依赖注入一般有三种方式: 变量注入、构造器注入、setter方法注入,下面我们仔细分析一下三种注入方式各自的特点以及使用场景。 一、变量注入(Field ... -
依赖注入(DI)的三种实现方式
2017-09-21 10:28:24本文来探讨一下依赖注入的三种实现方式是哪三种,Spring的支持又怎么样。 首先,依赖注入(DI)的实现方式有三种:设值注入、构造方法注入、接口注入,下面来看看这三种注入方式在Spring中的支持怎么样。 1、设值... -
Spring积累(3):依赖注入3种方式
2021-08-08 16:12:08目录 一、依赖注入方式 Spring的依赖注入有3种方式: 1、使用属性注入 2、构造函数注入 使用方式: (1)按类型匹配入参 (2)按索引匹配入参 (3)联合使用类型和索引匹配入参 3、使用字段(Filed)注入(用于注解... -
spring 依赖注入的三种方式
2020-12-07 12:46:01@Autowired:构造器,参数,...第一种、set方式注入 //Car类 package com.spring.bean; @Component public class Car { public Car() { System.out.println("car...constructor"); } } //Boss类 package com.sp -
php依赖注入的三种方式
2019-12-30 17:54:02控制反转(Inversion of Control,...下面我们就为大家介绍一下php依赖注入的三种方式。 一、构造器注入 将被依赖对象通过构造函数的参数注入给依赖对象,并且在初始化对象的时候注入。 优点: 对象初始化完... -
[Spring] IoC的理解及三种依赖注入方式
2022-04-11 16:58:04[Spring] IoC的理解及三种依赖注入方式Spring---IoC的理解及三种依赖注入方式IoC是什么意思依赖控制反转Spring提供的依赖注入的三种方式setter注入(属性注入)构造器注入p命名空间注入(工厂方法注入)@Autowired ... -
SpringBoot之三种常见的依赖注入方式
2021-07-13 09:54:56总结 Spring中Autowired注入的两种方式: byType:默认是按照类型注入,与bean的属性具有相同类型的其他bean自动装配到bean 的对应属性中。 byName:与bean的属性具有相同名字的其他bean,自动装配到对应的属性中。... -
Java依赖注入的三种方式
2019-09-10 09:33:46Spring通过DI(依赖注入)实现IOC(控制反转),常用的注入方式主要有三种:构造方法注入,setter注入,基于注解的注入。 构造方法注入 通过Spring配置文件,标签 <constructor-arg ref="" private UserDao ... -
Spring常用的三种依赖注入方式
2020-01-22 18:34:54资料来源: 1、https://blog.csdn.net/a745233700/article/details/80959716... ... 1 问题由来 1、传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象,这种开发方式存在的问... -
Spring依赖注入的三种实现方式
2020-03-27 06:57:58依赖注入(Dependency Injection,DI)和控制反转含义相同,它们是从两个角度描述的同一个概念。 当某个JAVA实例需要另一个 Java 实例时,传统的方法是由调用者创建被调用者的实例(例如,使用 new 关键字获得被调用... -
Spring 三种依赖注入方式.doc
2011-07-17 00:28:25Spring 三种依赖注入方式.doc Spring 三种依赖注入方式.doc -
Spring IOC(依赖注入的三种方式)
2018-07-09 16:38:51Spring IOC(依赖注入的三种方式):1、Setter方法注入。2、构造方法注入。3、P命名空间注入。Spring IOC(依赖注入的五种不同数据类型):1、注入直接量(基本数据类型、字符串)2、引用其他Bean组件。3、使用内部... -
依赖注入的两种常用的注入方式
2019-03-03 15:24:34在Spring框架中,主要有两种依赖注入方式:基于构造函数、基于setter方法 1.基于构造函数 基于构造函数的DI(依赖注入)是通过调用具有多个参数的的构造函数的额容器来完成的,每个参数表示依赖关系,下面演示一... -
spring IOC中三种依赖注入方式
2018-06-30 18:20:49spring的核心思想是IOC和AOP,IOC-控制反转,是一个重要的面向对象编程的法则来消减计算机程序的耦合问题,控制反转一般分为两种类型,依赖注入和依赖查找,依赖什么?为什么需要依赖?注入什么?控制什么?依赖注入... -
AngularJS ng依赖注入的三种方式
2017-07-29 17:48:12一.ng的四种注入方式 ...最基本的依赖注入方式 {{gameName}} //最基本的依赖注入方式 var MyModule = angular.module("MyModule",[]); MyModule.controller('MyCtrl',['$scope', function($scope){ -
Bean 的三种依赖注入方式介绍
2018-08-20 16:17:46接下来将详细的向大家介绍Spring容器支持的三种依赖注入的方式以及具体配置方法: • 属性注入方法 • 构造函数注入方法 • 工厂方法注入方法 一.属性注入 属性注入即通过setXXX()方法注入Bean的属性值... -
Spring中的三种依赖注入和三种Bean装配方式
2019-03-05 18:32:11Spring中的依赖注入的三种方法 基于构造方法 setter注入(常用) 接口注入(不常用) Bean如下 package spring; public class Role { private Long id; private String roleName; private String n... -
spring四种依赖注入方式
2019-05-07 10:49:18平常的java开发中,程序员在某个类中需要依赖其它类的方法,通常是new一个依赖类再调用类...依赖注入的另一种说法是“控制反转”,通俗的理解是:平常我们new一个实例,这个实例的控制权是我们程序员,而控制反转...