摘要
Spring 为何如此流行?我们用 Spring 的原因是什么?想想你会发现原来 Spring 解决了一个非常关键的问题:他可以让你把对象之间的依赖关系转而用配置文件来管理,也就是他的依赖注入机制。
依赖注入(dependency injection)的过程指类被实例化后,由容器负责将它的依赖(一起工作的其他类)准备好,提供给被实例化的bean使用。本节学习笔记主要关于两种常用的的依赖注入的方式:基于构造函数参数和基于属性setter方法注入,及让开发更轻松的自动装配技术(Autowired)说明。
1 基于构造函数参数注入
相似的注入方法是用定义静态工厂方法创建对象,将依赖作为工厂方法的参数。
优点是清楚的告诉调用者使用对象时必须注入的依赖,实例化后可得到完全可用的实例。适用于注入必须存在的依赖对象。
// 类定义 public class SimpleMovieLister {
// SimpleMovieLister类依赖了movieFinder对象 private MovieFinder movieFinder; // 带参数的构造函数,通过它注入movieFinder public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // 业务代码使用movieFinder
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
对应的配置代码
<beans> <bean id="simpleMovieLister " class="x.y.SimpleMovieLister "> <constructor-arg ref="movieFinder"/> </bean>
<bean id="movieFinder" class="x.y.MovieFinder "/>
</beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
1.1 多个构造函数参数
如果构造函数参数有多个,需要避免解析参数时发生歧义。
- 当多个参数都是引用类,且互相间无继承关系,则构造函数通过参数类型就可以区分不同参数,不需要给constructor-arg加其他属性;
- 当参数中有简单类型(非引用类型)如int,构造函数无法通过类型区分,如下所示类
//java类
package examples;
public class ExampleBean {
private int years;
private String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
方法1 通过指定constructor-arg标签的type属性区分
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
- 1
- 2
- 3
- 4
方法2 如果构造函数多个参数存在类型相同,则可以通过index属性代表构造函数签名中参数顺序(index从0开始)
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
- 1
- 2
- 3
- 4
方法3 构造函数定义时用@ConstructorProperties
注解给参数命名,在定义bean时可以指定constructor-arg标签的name属性
package examples;
public class ExampleBean {
// 省略属性定义
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
- 1
- 2
- 3
- 4
1.2 循环依赖问题(circle dependencies)
使用构造函数参数注入依赖,需要注意避免循环依赖问题。
当A对象的构造函数里依赖B对象,且B同时在构造函数里依赖A,就出现循环依赖,因为A和B都在等对方先创建
Spring容器启动时检测bean之间如果存在循环依赖会报BeanCurrentlyInCreationException
。
解决循环依赖的方法是,改用基于属性setter方法注入依赖,下面我们来一起学习这种依赖注入方式。
2 基于属性setter方法注入
基于Setter方法的依赖注入是由Spring容器在调用类的无参构造函数后调用属性的setter方法。它提供了运行时再次注入的机会,适用于选择依赖的对象。
// 在1中出现的类,这次使用基于属性Setter方法注入方式定义 public class SimpleMovieLister {
private MovieFinder movieFinder; public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; }
//…
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
对应bean配置
<beans> <bean id="simpleMovieLister " class="x.y.SimpleMovieLister "> <property name="movieFinder"> <ref bean="movieFinder"/> </property> </bean>
<bean id="movieFinder" class="x.y.MovieFinder "/>
</beans>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
上面配置中,property标签就是告诉Spring容器实例化MovieFinder对象并通过setMovieFinder方法给SimpleMovieLister对象注入依赖
3 自动装配(Autowired)
开启自动装配后,Spring 会自动从ApplicatonContext中寻找要依赖的bean并完成装配。
如何开启自动装配
通过配置文件bean标签的autowired属性开启自动装配并指定自动装配类型
4种自动装配模式
- autowired = “no”
默认设置,代表不需要装配 - autowired=“byName”
按属性name匹配,Spring检查容器中与属性有相同名称的bean - autowired=“byType”
按属性类型匹配, Spring检查容器中与属性规定的类型相同的bean,如果存在多个抛出异常 - autowired=“constructor”
按构造函数参数匹配,Spring检查容器中与构造函数参数类型相同的bean,如果不存在唯一一个则抛出异常
优点
1,在项目中统一使用自动装配可以大量减少通过属性Setter方法或构造函数参数注入依赖的配置
2,对象定义改变时自动装配产生的配置会自动随之更新
局限性
1, 如果同时定义了基于构造函数参数和属性Setter方法的依赖,自动装配会被覆盖而无法生效
2, 不能用于注入基本数据类型,String类型和Class类型,及它们的Array
3, 不如显示的指定依赖准确,通过属性和构造函数装配可以使自己对对象结构更清晰
参考资料
</div>
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-60ecaf1f42.css" rel="stylesheet">
<div class="more-toolbox">
<div class="left-toolbox">
<ul class="toolbox-list">
<li class="tool-item tool-active is-like "><a href="javascript:;"><svg class="icon" aria-hidden="true">
<use xlink:href="#csdnc-thumbsup"></use>
</svg><span class="name">点赞</span>
<span class="count"></span>
</a></li>
<li class="tool-item tool-active is-collection "><a href="javascript:;" data-report-click="{"mod":"popu_824"}"><svg class="icon" aria-hidden="true">
<use xlink:href="#icon-csdnc-Collection-G"></use>
</svg><span class="name">收藏</span></a></li>
<li class="tool-item tool-active is-share"><a href="javascript:;" data-report-click="{"mod":"1582594662_002"}"><svg class="icon" aria-hidden="true">
<use xlink:href="#icon-csdnc-fenxiang"></use>
</svg>分享</a></li>
<!--打赏开始-->
<!--打赏结束-->
<li class="tool-item tool-more">
<a>
<svg t="1575545411852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M179.176 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5718"></path><path d="M509.684 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5719"></path><path d="M846.175 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5720"></path></svg>
</a>
<ul class="more-box">
<li class="item"><a class="article-report">文章举报</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div class="person-messagebox">
<div class="left-message"><a href="https://blog.youkuaiyun.com/Lamb_IT">
<img src="https://profile.csdnimg.cn/7/5/5/3_lamb_it" class="avatar_pic" username="Lamb_IT">
<img src="https://g.csdnimg.cn/static/user-reg-year/1x/2.png" class="user-years">
</a></div>
<div class="middle-message">
<div class="title"><span class="tit"><a href="https://blog.youkuaiyun.com/Lamb_IT" data-report-click="{"mod":"popu_379"}" target="_blank">Lamb_IT</a></span>
</div>
<div class="text"><span>发布了25 篇原创文章</span> · <span>获赞 25</span> · <span>访问量 6万+</span></div>
</div>
<div class="right-message">
<a href="https://im.youkuaiyun.com/im/main.html?userName=Lamb_IT" target="_blank" class="btn btn-sm btn-red-hollow bt-button personal-letter">私信
</a>
<a class="btn btn-sm bt-button personal-watch" data-report-click="{"mod":"popu_379"}">关注</a>
</div>
</div>
</div>