谈谈我对 IoC 和 AOP 的理解

原创
2019/12/25 08:00
阅读数 6.7K

@JFinal 波总在 JFinal 4.8 发布新闻的评论 中给出了下面的表述:

IOC 本质是为了实现 AOP

我有点吃惊, 没想到 Java 界的大佬对这两个概念有和我完全不一致的认识. 所以写下这篇博客, 并借此机会重新学习一下 IoC 和 AOP, 确保自己对这两个后端开发非常重要的概念不会有太过偏差的理解

1. IoC

IoC 是 Inversion of Control 的缩写, 中文意思是控制反转. 维基百科对 IoC 开宗明义的定位为:

In software engineeringinversion of control (IoC) is a programming principle

维基文中对此有详细的阐述, 大家可以自行前往维基百科 Inversion_of_control 词条查看, 我就不一一 Copy/Paste 了. 这里帮大家检出几个关键地方捋一捋:

  1. IoC 是编程原则 - 不是特定的产品, 不是具体实现方式, 当然也和具体编程语言无关
  2. 在传统编程范式中, 程序调用可重用的库
  3. 在 IoC 原则下, 程序接受通用框架的调用控制 - 框架调用程序代码
  4. 与 IoC 原则相关的概念包括:
  5. IoC 的设计目的包括:
    • 将执行任务和任务实现解耦
    • 让模块专注于设计任务
    • 模块仅依赖于设计契约而无需关注其他系统如何工作
    • 避免模块替换时的副作用

到这里我们可以比较清楚地得出下面的结论了:

J1. IoC 的本质不是为了实现 AOP.

J2. 波总的 JFinal 已经实现了 IoC 原则. 因为应用写的代码总是被 JFinal 的代码调用, 这就是控制反转.

那为什么波总会说 "IOC 本质是为了实现 AOP" 呢? 我姑且胡乱猜测一下, 波总想说的有可能是 "DI 本质是为了实现 AOP". 下面我们来探讨一下 DI, 这个和 IoC 以及 AOP 都有关系的概念.

2. DI

DI - Dependency Injection, 中文叫依赖注入, 在维基百科中的定义为:

In software engineering, dependency injection is a technique whereby one object supplies the dependencies of another object.

特别地, 维基百科中的 DI 词条给出了下面的描述:

Passing the service to the client, rather than allowing a client to build or find the service, is the fundamental requirement of the pattern.

The intent behind dependency injection is to achieve Separation of Concerns of construction and use of objects. This can increase readability and code reuse.

上面在对 DI 的描述中引入了另一个概念: SoC (Separation of Concern), 中文名关注点分离. 这是计算机科学中的一条设计原则, 简单地说就是将计算机程序划分为独立的单元, 每个单元解决一个可分离的关注点. 这个概念和封装 (Encapsulation) 非常接近, 可以说封装是对 SoC 设计原则的一种具体实现. 而 DI 则被描述为在构造和使用对象上实现 SoC 这个设计原则.

从上面的表述我们可以得到第三条结论:

J3. DI 也不是为了实现 AOP

那 DI 或者 IoC 到底和 AOP 有没有关系, 我们先来看看 AOP 的定义

3. AOP

AOP - Aspect Oriented Programming 在维基百科中的定义为:

In computing, aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns.

这里面有几个关键点:

  1. AOP 是一个编程范式. 听上去有点晦涩, 下面列举几个编程范式的例子可以帮助大家理解这个概念:
    • Imperative - 指令式, c, c++, Java
    • Declarative - 声明式, SQL, 各种 DSL, 比如 ANTLR 的语法文件
  2. AOP 的目的是通过分离横切关注点(Separation of cross-cutting concern) 来提高模块性.

这里的 Separation of cross-cutting conern 是不是有点耳熟? 回顾上面提到的 DI 描述中引入的 Separation of Concern, 两个概念字面相近, 但又不完全一致. AOP 关注的是切面, 而 DI 关注的是对象构造. 如果没有注意到这个异同处, 有可能将 DI (甚至 IoC) 和 AOP 的概念搅和到一起.

扩展讨论

无独有偶, 前段时间 drinkjava (@yong9981) 同学也和我就 AOP 以及 DI 的关系进行了比较深入的探讨, 话题包括:

  1. AOP 的实现是否必须有 DI 提供
  2. Web 框架是否必须提供通用 AOP 的实现
  3. 声明式事务是否必须采用 AOP 来提供

对这些话题感兴趣的朋友可以继续访问 谈谈 Act 的依赖注入 和 模板输出 - 回 drinkjava 同学的评论.

更新 - 2019-12-26

我不同意 @yong9981 对本文的评论, 并在这里做了反驳:

展开阅读全文
打赏
1
15 收藏
分享
加载中
学习了
2019/12/27 15:44
回复
举报
其实不能算是全错,相反,只有对IOC/AOP有深切理解的人才能说出这句话。技术要看它的使用环境,Spring的产生主要是为了取代EJB,而EJB除了无关痛痒的bean容器外,就是提供了声明式事务这个实打实有用的功能。 作为后端环境,就离不开与事务打交道,为了少写代码,又离不开和声明式事务打交道,至此有两个选择:1.每次用enhence之类的方法手工创建声明式事务对象(即代理对象) 2.由IOC工具(注意是IOC工具而不是框架,我下面要说)来负责生成这个代理对象。
你们两家都走过弯路,JFinal以前没有IOC功能,提倡用new来创建实例,这个我没意见,new就new吧。但在声明式事务这块,只好手工调用enhence方法创建proxy ,有点繁琐,估计JFinal作者感到了这个问题,所以最近又将IOC功能整合到框架中来了。而ACT则更奇葩,框架只提供IOC功能,不提供AOP功能, 把AOP和声明式事务当作DAO插件一起捆绑,这从架构上来说是错误的,因为事务本身是可以独立于DAO存在的。例如spring事务模块可以捆绑在任意支持AOP联盟标准上的DAO工具上使用的,但前提是框架要支持AOP联盟标准。
当然了,jFinal的AOP功能(貌似)也不支持AOP联盟标准,但好歹人家还提供了一个AOP功能和事务模块,虽然不能直接把Spring的事务模块拿来直接用,但好歹有了这个AOP功能后,这个事务模块是可以通用于所有DAO工具的。
谈到这里,我想就另一个问题,就是解偶的问题,我说这个框架有问题,那个框架有问题,往往只基于一个原则,就是它这个功能能不能拿出来单独使用,能不能被其它软件替换掉,基于这个原则,可以很简单地判断出以下问题:
* ACT和JFinal不支持AOP联盟标准是一个缺陷
* SpringBoot、SpringCloud的IOC/AOP内核不能被其它软件替换掉是一个问题,它导致Spring绑死在Spring内核上,不是说springioc内核不好,相反它非常好,依赖也少,查一查MAVEN就知道了。但它的问题是太大了,源码难看,又动不动更新扰民。
* ACT和jFinal的MVC模块不能单独使用,这是一个架构问题,造成资源浪费,比方说,我想使用以下三个优秀功能的组合是做不到的:
spring-ioc内核+ACT的MVC+JFinal的事务
2019/12/25 21:26
回复
举报
2019/12/26 08:13
回复
举报
这句话本身当然是错的。我这段话只是想说,在特点的语境下,尤其是站在JFinal的发展立场来看,能说出这句话已经很不错了,这里我代替JFinal作者来推想一下这名话的来源: 见这个贴:[jfinal为什么不实现类似ioc的容器功能](http://www.jfinal.com/feedback/1825)这是在2017年发表的,也就是说在那个时候,它的事务还是这样子的: public static MyService1 me1 = new MyService1(); 或 public static MyService2 me2 = Duang.duang(MyService2.class); 这就造成了用户必须手工分清这个serveice到底要不要调用一个duang方法生成代理,这是个问题。JFinal为了解决这个问题,才不得不将IOC功能公开给用户调用,只需要service = Aop.get()一个方法就行了,也就是说对于JFinal这个环境来说,它引入IOC的唯一目的就是为了实现AOP,这没毛病,但是如果跳开这个语境,断章取义,或是换成任一个非JFinal作者的人来说,就显得明显有问题了。 很高兴和你讨论架构设计,我的一些其它想法会在你刚开那个新贴里回。
2019/12/26 09:26
回复
举报
该评论暂时无法显示,详情咨询 QQ 群:912889742
即使从 JFinal 的发展角度来看, 波总也不应该说出 "IoC 的本质是为了 AOP". 他如果说 "在 JFinal 中, 引入 IoC (其实应该叫 DI) 的目的是为了 AOP" 就不会有大的毛病.
2019/12/26 09:36
回复
举报
你想当然的所谓组合,只是当下伸手党这些懒人的做法。 既然你有自己的想法,为什么自己不去实现? 每个产品都有自己的设计,不能以自己的一知半解来妄加评论,要虚心地学习和讨论。
2019/12/26 09:17
回复
举报
只是评论而已,评论可以帮助大家清晰思路。如果是缺点可以更正,如果说错了,相信作者自会取舍判断。另外抱歉用了"奇葩",老实说这真真确确是我脑子里当年面对JFinal和ACT两个框架浮现出来的词,一个只提供AOP,一个只提供IOC。但在回贴中用这个词并不合适,这反映了本人的个人素质有待提高,在此对老罗表示道歉,以后不再用此类与技术无关的词。 另外我也不是什么懒人党,只是在架构思路上和国内两位大伽的想法不同而已,我的基本思路是作功能模块(并已有项目在开发中),而不是做集成环境。各个功能模块组合在一起必然是最优的集成环境。
2019/12/26 10:08
回复
举报
共勉
2019/12/26 10:44
回复
举报
非常专业,都是干货,没有什么多余的文学修饰,什么大道至简呀,e=mc2呀,什么天下代码,简单不破呀,什么独创呀,什么极呀,什么最呀,看起来想广告词一样的广告
2019/12/25 17:34
回复
举报
👍
2019/12/27 13:12
回复
举报
写的很务实,也非常深入,完全是作者自己经验和开发ACT框架的体会,ACT框架是一个非常现代的WEB框架。另外。很吃惊居然还有开发WEB框架的大佬对IOC和AOP 不理解的..而且还出来误导其他人
2019/12/25 16:24
回复
举报
厉害
2019/12/25 13:10
回复
举报
该评论暂时无法显示,详情咨询 QQ 群:912889742
更多评论
打赏
14 评论
15 收藏
1
分享
返回顶部
顶部