首页 > 我是大名鼎鼎的IOC
头像
大黄奔跑
编辑于 2021-04-26 09:01
+ 关注

我是大名鼎鼎的IOC

微信公众号:大黄奔跑
关注我,可了解更多有趣的面试相关问题。

写在之前

IOC (控制反转)作为 Spring 中最核心的功能,之前学习一直不得要领,最近在看 《Spring 揭秘》忽有一种茅塞顿开的感觉。

觉得应该好好分享一下自己的理解和思考,供大家参考。

虽然 《Spring 揭秘》年代久远,但确实是一本好书,豆瓣评分高达9.1,所有的细节娓娓道来,不故弄悬殊,强烈推荐大家去看。

让我为你服务

抛开 IOC 的控制反转这个抽象的概念,我们先来看一个场景。

"让我为你服务",请记住这句话,这句话可以说解决了学习的根本问题,为什么需要 Spring IOC

首先说一个简单的场景

外卖服务出现以前,订餐是如何预订的呢?

"老板吗?给我来一份农家小炒肉,我稍后来取。"

"没有问题,要什么口味?超辣还是微辣、还是不辣?"

"微辣就行。"

好的,稍后你来取就行。

看,这就是我们以前点外卖的方式,想要吃什么菜,需要亲自给餐馆打电话,然后亲自上门取餐。

传统点餐

点一份餐,我需要直接跟张老板沟通,我依赖张老板;如果突然有一天张老板休假一天,岂不是吃不着小炒肉了,也就是说,我和张老板存在一个依赖关系

同时如果有时间还好。如果没有时间,没有时间给餐馆打电话点餐怎么办?点完了菜没有时间取,岂不是要饿肚子了?

可是回头一想,我们每次需要什么对象的时候都需要自己去主动取,真的有这个必要吗?

有没有人能够帮忙解决这个问题呢?

有需求,必然有市场。美团、饿了么应运而生。刚好满足我所有的诉求(帮忙点餐、帮忙拿外卖)。

被解放的大黄

其实我们的诉求只是想吃一顿农家小炒肉而已,至于是谁家的不要紧,张老板或者李老板都可以,是骑自行车还是骑摩托车给我送过来,都不要紧,只需要到12点给我来一份农家小炒肉即可。

我还需要为了一顿饭大费周章的自己打电话点餐、自己去店铺取餐吗?如此折腾。

真是有这样的诉求,才出现了如此蓬勃的外卖行业。

我和张老板之间不存在直接的依赖关系,张老板关门了,我还是可以吃到一份小炒肉。

这种依赖依赖关系交给了外卖商(容器)来进行管理,商家将自己有哪些东西放到外卖商平台(容器)。从被注入对象(小炒肉)的角度看,与之前直接寻求依赖对象相比,依赖对象的取得方式发生了反转,控制也从被注入对象转到了容器那里。

所谓的控制反转,甲乙双方不相互依赖,交易活动的进行不依赖于甲乙任何一方,整个活动的进行由第三方负责管理。

IOC角色

美团:让我为你服务!

重谈什么是IOC

外卖等服务商就类似于 IOC的容器概念,帮助我们大费周章的自己获取对象,而是提供了更加便捷的方式获取对象。与其去弄懂极度拗口的控制反转,不如去了解其本质。

外卖服务商就类似于一个大的中介,我想吃啥直接去里面点即可,总有一款小炒肉满足你的诉求。

其实 IOC 就是如此简单,原来需要什么餐自己去拿,现在呢?需要什么东西,点一下外卖即可,别人会准点给你送过来。

让我猜猜你的心思

当拿出外卖软件时,脑中想着"想吃一份汉堡",通过可以在软件中随意挑选汉堡。商家把汉堡作为对象注入到外卖软件这个大的容器中,而软件作为一个容器为客户提供服务。容器可以以多种方式将某个汉堡信息推送到用户的眼帘中。

1、如果你经常点肯德基的外卖,没准你一打开软件,自动给你推送一个肯德基奥尔良鸡肉套餐正在做活动;给你推荐一个实惠套餐,毕竟大数据下没得秘密。

2、如果你偶尔点击肯德基外卖,打开软件还需要搜索肯德基,系统会给你推荐经常点击的套餐;

3、可有可能你想换换口味,直接搜索汉堡,系统给你推荐一堆汉堡的店,自己慢慢挑选。

不管怎样,软件终究会有一种方式来向给你提供服务,以便得到你想要的美食。

IOC 模式中,被注入对象又是通过哪些方式来通知容器为其提供适当服务的呢?

最常见的三种注入方式:构造方法注入、Setter 方法注入、接口注入。

1. 构造方法注入

构造方法注入,就是被注入对象可以通过在其构造方法中声明依赖对象的参数列表, 让外部(通常是 IoC 容器)知道它需要哪些依赖对象。

比如以一个简单的商品服务为例,在 controller 层依赖 service 层的 GoodsService 服务,可以通过如下构造方法注入:

private GoodsService goodsService;

public GoodsController (GoodsService goodsService) {
   this.goodsService = goodsService;
}

容器 会检查被注入对象的构造方法,取得它所需要的依赖对象列表,进而为其注 入相应的对象。同一个对象是不可能被构造两次的,因此,被注入对象的构造乃至其整个生命周期, 应该是由容器来管理。

构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,可以马上使用。

就好比,你打开软件,美团就知道你想吃啥。

2. Setter注入

类似于类中某些属性设置的方式通过 setXX() 进行,这些方法统称为 setter 方法,通过setter方法,可以更改相应的对象属性,通 过getter方法,可以获得相应属性的状态。这也是面向对象编程中封装特性的表述方式。

当前对象只要为其依赖对象所对应的属性添加setter 方法,就可以通过setter方法将相应的依赖对象设置到被注入对象中。

还是以上面的 GoodService 为例

@Autowired
public void setGoodsService(GoodsService goodsService) {
    this.goodsService = goodsService;
}

同时需要利用传统的 xml 方式配置bean信息

<bean id="goodsService" class="com.jesper.seckill.service.GoodsService">
    <description>"用户商品"</description>
</bean>

<bean id="goodsController" class="com.jesper.seckill.controller.GoodsController">
    <property name="goodsService" ref="goodsService"/>
</bean>

setter方法注入虽不像构造方法注入那样,让对象构造完成后即可使用,但相对来说更宽松一些, 可以在对象构造完成后再注入。

这就好比点餐的时候搜索汉堡,然后自己慢慢挑选自己中意的品牌,是肯德基还是麦当劳都可以。

3. 接口注入

相对于前两种注入方式来说,接口注入没有那么简单明了。

被注入对象如果想要容器为其注入依赖对象,就必须实现某个接口。这个接口提供一个方法,用来为其注入依赖对象。容器最终通过这些接口来了解应该为被注入对象注入什么依赖对象。

GoodsController 为了让 容器 为其注入所依赖的 GoodsService ,首先需要实现 GoodsServiceCallable 接口,这个接口会声明一个 injectNewsGoods 方法(方法名随意), 该方法的参数,就是所依赖对象的类型。这样,容器就可以通过这个接口方法将依赖对象注入到被注入对象 GoodsController 当中。

通过接口注入

由于这种方式实现比较复杂,并且对于业务代码具有很强的侵入性,因此在实际开发和应用中不是很常见。

这就 好像你同样在酒吧点啤酒,为了让服务生理解你的意思,你就必须戴上一顶啤酒杯式的帽子,看起来有点多此一举。^1

来一杯这样的啤酒

三种方式比较

注入方式 优点 缺点
构造方法 对象在构造完成之后,即已进入就绪状态,可以马上使用 1、当依赖对象比较多的时候,构造方法的参数列表会比较长
2、构造方法无法被继承,无法设置默认值
3、容易造成注入方的构造函数泛滥
setter setter方法可以被继承,允许设置默认值 对象无法在构造完成后马上进入就绪状态
接口注入 极度不提倡,对代码侵入强、代码复杂

总结

主要和大家探讨什么是IOC,为什么需要IOC,同时无意中还引出容器的概念,其实可以概括一下 IOC 作用,让容器为你服务,避免任何事情都亲自出马,同时帮助我们解耦各业 务对象间依赖关系的对象绑定方式。

参考文章

全部评论

(1) 回帖
加载中...
话题 回帖

相关热帖

近期热帖

近期精华帖

热门推荐