文档章节

再论 IoC 和 AOP - 驳 yong9981 对 "谈谈 ... (2019-12-25)" 一文的评论

开源老码农
 开源老码农
发布于 2019/12/26 07:54
字数 4245
阅读 1.7K
收藏 1

「深度学习福利」大神带你进阶工程师,立即查看>>>

谈谈我对 IoC 和 AOP 的理解 一文由 JFinal 作者波总对 IoC 与 AOP 的一句表述引起:

IOC 本质是为了实现 AOP

在文中我考察了 IoC, DIAOP 三个概念及其关系, 并得出以下结论:

  1. IoC 的本质不是为了实现 AOP.
  2. 波总的 JFinal 已经实现了 IoC 原则. 因为应用写的代码总是被 JFinal 的代码调用, 这就是控制反转.
  3. DI 也不是为了实现 AOP

@yong9981 对此文给出了以下评论:

我不赞同 yong9981 在评论中的观点, 所以在本文中一一回应.

1. "其实不能算错, 相反,只有对IOC/AOP有深切理解的人才能说出这句话"

yong9981 这句话应该是针对波总的表述 "IoC 的本质是为了实现 AOP" 而言. 我在 谈谈 一文中已经清楚地给出了维基百科中对 IoC 和 AOP 这两个概念的描述, 并由此得到结论: "IoC 的本质不是为了实现 AOP". 如果 yong9981 认为波总的表述没有错误, 那就是我的结论有误. 逻辑上讲有两个可能:

  1. 维基百科对 IoC 和 AOP 的概念描述有错误
  2. 我在文中依据概念得出结论的过程不正确, 依据维基百科的概念描述应该得到 "IoC 的本质是为了实现 AOP" 这个表述

@yong9981 回答, 你认为我上面结论的错误应该属于哪种情况? 不管是哪种情况, 请给出你的依据. 直接下断言的方式我不接受.

2. "ACT则更奇葩,框架只提供 IOC 功能 ... 从架构上来说是错误的"

依据维基百科对 IoC 的定义, 凡是提供控制反转的 (应用代码被框架调用的) 都是 IoC 的应用. 因此我大致可以说基本上 Web 框架 (包括 Servlet 在内)都是符合 IoC 原则的.

基于以上分析, 我姑且揣摩 yong9981 想说的是 "ACT 则更奇葩, 框架只提供 DI 功能".

先来看看除 Act (截止到 1.8.x) 以外还有那些框架支持 DI, 但不提供 通用 AOP:

由此看来只支持 DI 而不提供 AOP 的框架也不仅仅是 Act 了.

再来看看百度百科对 奇葩 这个词的定义是:

原意是指奇特而美丽的花朵,常用来比喻不同寻常的优秀文艺作品或非常出众的人物。比喻某人(或某事物)不落世俗,个性十足。这个词更多带有调侃,指美好出众的事物不同。也多指向一些正常人行为和思维以外的,让人难以想象的行为。

我相信 yong9981 用 奇葩 来修饰 Act, 不会是引用其原意, 而是指 Act 仅支持 DI 不支持 AOP 的设计不正常. 上面我已经列出有更多的框架采用同样的设计, 所以这个词我不接受.

3. "把AOP和声明式事务当作DAO插件一起捆绑,这从架构上来说是错误的"

这里解释一下 yong9981 上面这个论断的上下文.

ActFramework 不支持通用的 AOP, 而是采用下面的方式替代了 AOP 的部分常见用途:

  1. 拦截器机制 - 解决应用在请求处理流程中的切面编程问题
  2. Metric 机制 - 解决应用收集性能数据的切面编程问题
  3. 对声明式事务的处理
    • Ebean - 交给 Ebean 的声明式事务处理机制 (通过 javaagent 机制修改应用字节码)
    • EclipseLink 和 Hibernate - 交给 act-jpa-common 的声明式事务机制处理 (通过 Act 的类增强机制修改应用字节码)

yong9981 认为 Act 在这方面的设计是错误的, 他的理由是:

因为事务本身是可以独立于DAO存在的

我不是很明白上面这条理由. 其中的 "事务" 是指一个运行时的事务实例, 还是只框架的事务处理机制? "DAO" 是指一个运行时的 Dao 实例, 还是数据库访问组件? 如果都是指实例和正在讨论的问题不相干, 我姑且认定 yong9981 说的是"事务处理机制和数据库访问组件无关", 并基于这个理解来讨论.

对上面的理由 yong9981 继续列出了一个作证:

例如spring事务模块可以捆绑在任意支持AOP联盟标准上的DAO工具上使用的,但前提是框架要支持AOP联盟标准。

先向 yong9981 提两个问题:

  1. 什么是 "支持AOP联盟标准上的DAO工具" ?
  2. 请列举出几个 "支持AOP联盟标准上的DAO工具" 出来

就 Java 生态, 我所知道的比较著名的数据库访问组件有:

请问 yong9981 上面列出的 Java 数据库访问组件哪个是支持 AOP Alliance 的? 请列出你引用的链接. 如果你给不出反例, 我可以得出以下结论:

j1. 没有支持 AOP 联盟标准上的 DAO 工具

既然 yong9981 使用 Spring 作为作证, 下面我们就看看 Spring 中 AOP 到底是如何参与声明式事务处理的

3.1 Spring 中对声明式事务的处理

Spring Transaction Management 文中这个插图描述了 Spring 对声明式事务的处理机制:

从图中, 我们可以看到 AOP 的参与到 TransactionAdvisor 为止, 而具体某个数据库访问机制如果需要支持 Spring 的事务处理过程, 需要讲自己适配到 Spring 的事务管理机制中 - 不是适配到 AOP alliance

拿 MyBatis 为例. 在 MyBatis Spring 插件 - 事务 文档中第一句话就是:

One of the primary reasons for using MyBatis-Spring is that it allows MyBatis to participate in Spring transactions

MyBatis Spring Github 项目 我们可以看到 MyBatis Spring 集成进 Spring 事务处理的代码:

到此我们可以得出下面的结论:

j2. "spring事务模块可以捆绑在任意支持AOP联盟标准上的DAO工具上使用的" 这个断言是错误的

4. "我说这个框架有问题... 只基于一个原则,... 能不能拿出来单独使用 ..."

yong9981 评论中的最后一段论述如下:

我说这个框架有问题,那个框架有问题,往往只基于一个原则,就是它这个功能能不能拿出来单独使用,能不能被其它软件替换掉, 基于这个原则,可以很简单地判断出以下问题:

  • ACT和JFinal不支持AOP联盟标准是一个缺陷
  • SpringBoot、SpringCloud的IOC/AOP内核不能被其它软件替换掉是一个问题,它导致Spring绑死在Spring内核上,不是说springioc内核不好,相反它非常好,依赖也少,查一查MAVEN就知道了。但它的问题是太大了,源码难看,又动不动更新扰民。
  • ACT和jFinal的MVC模块不能单独使用,这是一个架构问题,造成资源浪费,比方说,我想使用以下三个优秀功能的组合是做不到的: spring-ioc内核+ACT的MVC+JFinal的事务

依据 hotframeworks 给出的统计, 2019 最常用的 Java web 框架有:

  • Spring
  • JSF
  • GWT
  • Play!
  • Struts
  • Vaadin
  • Grails

请问 yong9981: 这些框架的组件都能互换吗? 是不是这些框架都有问题? 如果每个框架都有问题, 是不是你的问题有问题?

我认为:

  1. 在一定程度上, 框架组件的解耦 (相互替换) 可以采用遵循一些标准来达到, 比如 JSR 330 依赖注入, JSR 303 数据校验, JAX-RS (RESTful Service API) 等等
  2. 框架拥有选择自己生态结构的权利. 一个框架可以选择一个全封闭的生态, 也可以选择一个相对开放的生态. 没有理由因为框架做出的这种选择认为该框架有问题甚至有缺陷.

5. 更新 - 2019-12-27

回答 @yong9981 2019-12-26 对本文的评论.

5.1 再议 AOP 与 DAO

先把术语定义一下, 因为 yong9981 使用了 DAO 工具, 我们就在这里沿用这个术语, 指各种数据库访问组件, 包括 Hibernate, MyBatis 等.

@yong9981 没有回答我在文中提出的问题, 而是举出了 Spring 的TransactionInterceptor.java:

TransactionInterceptor是一个符合AOP联盟的类,Spring为什么要引用这个接口?不是吃饱了没事干,而是设计给其它AOP工具来使用这个类的

并引用到了自己的 jBeanBox 来作证:

这体现了Spring的开放性,他把最有价值的事务模块从内核剥离出来了。具体一个配置事例可见https://gitee.com/xialinlin/jBeanBox。它没有使用spring的IOC/AOP内核,但是使用了它的事务.

yong9981 进而讲:

支持AOP联盟标准上的DAO工具 这是个笔误或简写,应该是支持AOP联盟标准的声明式事务的DAO工具。你列举的上述软件只要在Spring环境下,它们大多都是基于TransactionInterceptor进行声明式事务的

这里我需要指出几点:

  1. TransactionInterceptor 是 Spring 内部基于 Spring AOP 实现声明式事务的一种机制
  2. 这种机制和其他数据库访问组件 没有 关系 - 在本文第三节中列举的 DAO 工具中的代码, 包括其 spring 适配库 (例如 mybatis-spring) 中既看不到调用 TransactionInterceptor 的方法, 也看不到任何类继承 TransactionInterceptor. 因此没有所谓 "支持AOP联盟标准的声明式事务的DAO工具" - AOP联盟标准对于数据库访问组件来说是不可知的(agnostic).
  3. yong9981 的 jBeanBox 也根本谈不上 "使用了它 (Spring) 的事务". 除了 README, 代码中没有任何与 Transaction 相关的部分.

现在的问题是为什么 yong9981 一定要把 AOP 和 声明式事务以及 DAO 联系在一起呢? 我揣测其思维脉络可能是:

  1. Spring 支持 AOP Alliance
  2. Spring 在运行时基于 AOP 提供声明式事务的支持, 大致来说有一下几点:
    • 调用事务方法前建立事务上下文
    • 调用事务方法后提交事务
    • 调用出错时回滚事务 (依据异常类型可能有不同的处理)

到这里我们可以提个问题了, 是不是其他 DAO 工具能不做任何工作就使用 Spring 的声明式事务访问机制了呢? 通过研究 mybatis-springebean-spring-txn 这两个项目, 我们可以明确地给出否定回答: DAO 工具不会因为 Spring 采用了 AOP 来触发声明式事务机制而避免和 Spring 事务机制的集成工作.

另外 yong9981 在评论中谈到:

反之,jFinal、ACT中的事务并没有共享出来

jFinal 我不清楚, 下面我们来看看 ActFramework 的声明式事务机制, 并和 Spring 的声明式事务机制做一个比较.

5.1.1 ActFramework 的声明式事务机制

ActFramework 在 act-sql-common 插件项目 (act-1.8.9 之前是 act-jpa-common 项目) 中建立了事务处理机制. 该项目是 act-hibernateact-eclipselink 两个项目的祖父项目. 简单地说 act-hibernate 和 act-eclipselink 这两个 DAO 工具的 Act 插件无需建立自己的声明式事务处理机制. 如果今后有其他 hibernate 和 eclipselink 之外的其他支持 JPA 的 DAO 工具也可以很容易的集成进 Act-DB 框架. 这里给出两个数据, act-hibernate 的代码为 212 行, act-eclipselink 的代码为 105 行, 基本上都是为了适配 hibernate 和 eclipselink 本身的各种配置.

下面来看看具体 Act 的声明式事务机制的处理过程.

  • TxScopeEnhancer 在应用类加载的时候如果检测到公共方法上有 @Transactional 注解, 对该方法的字节码进行增强:
    1. 方法调用之前建立事务环境
    2. 方法调用之后提交事务
    3. 异常发生时回滚事务
  • 以上事务环境的操作封装在 TxContext 类中, 该类通过 ThreadLocal 来封装 TxInfo 事务状态.
  • TxInfo 的事务状态通过 TxScopeEventListener 传递给 SqlDbService
  • SqlDbService 是各个 DAO 插件必须继承并实现的类. 比如 act-ebean 的 EbeanService, act-hibernate 的 HibernateService 以及 act-eclipselink 的 EclipseLinkService.
  • SqlDbService 提供以下抽象方法用于适配 DAO 工具集成:
    protected abstract void doStartTx(Object delegate, boolean readOnly);
    protected abstract void doRollbackTx(Object delegate, Throwable cause);
    protected abstract void doEndTxIfActive(Object delegate);
    

这个处理机制和 Spring 基于 AOP 的处理机制相比:

  1. TxScopeEnhancer 替代了 基于 AOP 的 TransactionInterceptor, 运行时效率上更有优势
  2. 有明确的机制将 Act 的事务处理适配到 DAO 工具集成项目中.
  3. 该机制没有采用 Spring 的 TransactionManagerDataSource 层面上耦合的方式, 而是通过明确的 API (参见上面 SqlDbService 的三个抽象方法) 和 DAO 工具集成耦合.
  4. 目前的实现比 Spring 对事务的支持要粗糙, 比如不支持 nested transaction. 这个地方是今后版本工作的重点区域.

值得一提的是从 act-1.8.9 开始, 事务机制的实现从 act-jpa-common 提升至 act-sql-common, 也就是说对 act-ebean 的事务支持理论上可以不需要通过 ebean 自己的事务处理机制了. 我今天做了一个小试验, 将 transaction-ebean 示例项目的 Account.java 文件中的事务注解从 io.ebean.annotation.Transactional 切换到了 act.db.sql.tx.Transactional, 运行结果证明了我前面的猜测, 运行过程中事务处理从 Ebean 内置的切换到了 Act 的提供的, 没有任何问题. 有兴趣的看官可以自行 checkout 这个项目代码研究一下:

在下一个版本中事务处理机制会直接提升到 Act 核心框架中, 这样允许 Act 提供对非 SQL 数据库 DAO 工具的事务支持, 比如 MongoDB (v4+).

5.2 关于开放和封闭的选择

我认为一个框架是否开放应该考察这个框架对现有业界标准的支持. 一个框架对标准的支持越好, 这个框架就越开放. 下面列出一些 Act 支持的标准:

  1. HTTP/RESTful, - 如果有哪个框架包括 Spring 在内在这方面的支持比 Act 更好, 我很愿意了解学习.
  2. JSR 330 - 依赖注入
  3. JSR 303 - 数据校验
  4. JAX-RS - 通过 act-jax-rs 插件
  5. JPA - 通过 act-jpa-common 插件

对于 yong9981 提到的

jFinal、ACT、NutzBoot,这三者都有IOC模块,难道三个作者就不能合力统一成一个短小精悍的可以干掉Guice的IOC/AOP模块?

我只能呵呵一下了. 以下是对 yong9981 其他几个表述的回答:

集成式环境的另一个问题是增加学习难度

如果一个框架能尽可能符合标准规范行事, 可以降低学习负担. 举个例子, 但凡用习惯了 Guice (支持 JSR 330 依赖注标准), 切换到 Act 毫无违和感.

你列了一堆Java web框架,但正常人有几个一个个全去了解的

列出一堆 Java Web 框架的目的是为了佐证你这个说法不正确: "我认为一个框架是否有问题只基于一个原则: 其组件能否单独使用, 能否被其他框架组件替换". 你没有回答我的质询: "这些框架的组件都能互换吗? 是不是这些框架都有问题? 如果每个框架都有问题, 是不是你的问题有问题?"

你的ACT也许是性能最好,但依赖太多(Maven上30个依赖,还是compiled类型的,发布包2M多),这对于普通人来说是个巨大的学习负担

首先性能是 Act 关注点, 但不是最重要的地方, 功能和易用性才是. 这个地方我会在以后的博客系列中继续讨论.

依赖的数量从来不是 Act 的关注点. 如果一个框架功能丰富, 具有很好的开发时支持和相当的运行时性能, 我不在乎依赖的数量.

依赖数量和学习负担没有正比关系. 学习的范围在于应用需要用到的功能. 应用开发的复杂性是一定的, 如果框架提供了支持, 你需要学习, 如果不提供, 你需要自行研发.

Yong9981 理想中的后台环境

这个属于个人喜好, 我在这里不做评论.

开源老码农

开源老码农

粉丝 363
博文 63
码字总数 72894
作品 4
其它
架构师
私信 提问
加载中
此博客有 15 条评论,请先登录后再查看。
访问安全控制解决方案

本文是《轻量级 Java Web 框架架构设计》的系列博文。 今天想和大家简单的分享一下,在 Smart 中是如何做到访问安全控制的。也就是说,当没有登录或 Session 过期时所做的操作,会自动退回到...

黄勇
2013/11/03
3.5K
6
opm-server-mirror

代码更新 2009-11-25: 加入反爬虫功能。直接Web访问服务器将跳转到Google。 使用方法 下载index.zip 解压index.zip得到index.php 将index.php传到支持php和cURL的国外服务器上 打开 http:/...

luosheng86
2013/01/29
1K
0
Promises/A 和 when() 实现--When.js

When.js 是 cujojs 的轻量级的 Promises/A 和 when() 实现,从 wire.js 的异步核心和 cujojs 的 IOC 容器派生而来。包含很多其他有用的 Promiss 相关概念,例如联合多个 promiss、mapping 和...

匿名
2013/02/15
7.4K
0
基于 ThinkPHP 的内容管理系统--歪酷CMS

歪酷网站管理系统(歪酷CMS)是一款基于THINKPHP框架开发的PHP+MYSQL网站建站程序,本程序实现了文章和栏目的批量动态管理,支持栏目无限分类,实现多管理员管理,程序辅助功能也基本实现了常见的文...

鲁大在线
2013/02/19
7K
2
PHP框架--XiunoPHP

XiunoPHP 是一款面向高负载应用的 PHP 开发框架,PHPer 通过它可以快速的简单的开发出高负载项目。 XiunoPHP 前身名为 Xiuno Framework,更名后版本号从 v1.0 开始计算。已经经过了多年的实际...

匿名
2013/03/20
2.5K
0

没有更多内容

加载失败,请刷新页面

加载更多

如何在Git历史记录中grep(搜索)已提交的代码 - How to grep (search) committed code in the Git history

问题: I have deleted a file or some code in a file sometime in the past. 我过去某个时候已经删除了文件或文件中的某些代码。 Can I grep in the content (not in the commit messages)......

技术盛宴
37分钟前
9
0
二进制安装安装mysql 8.0.20

MySQL最新版本8.0.20正式发布。与之前8.0的系列版本一样,这次的发行版除了包含缺陷修复,也同样包括新功能。下面快速浏览一下。关键字:hash join、InnoDB双写缓冲、二进制日志事务压缩。 ...

程序员面试吧
41分钟前
18
0
关于python3.8+ pyside2 pyinstaller打包的一些坑

环境: python 3.8 pyinstaller 3.6 pyside2 5.14 打包过程中出现错误(1):   7607 WARNING: lib not found: pywintypes38.dll dependency of c:\users\have_\appdata\local\programs\pyth......

齐勇cn
41分钟前
11
0
备战秋招!静电的UI设计教室全能课程开始招生~系统进阶!提升核心竞争力

。 本文分享自微信公众号 - 静Design(JingDesign91)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。...

静电1983
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部