谈谈 Act 的依赖注入 和 模板输出 - 回答 drinkjava 同学提问
1. 背景
依赖注入工具 jBeanBox 的作者 drinkjava 同学最近在 Actframework gitee 项目 的提出了如下评论:
你这个DI工具的出发点可能有问题,一个MVC工具为什么要引入DI依赖注入? 这个和Spring或Guice的功能重叠了。直接引入Spring或Guice的不好吗? 按我的观点,DI唯一比较经典的用法只是用来进行声明式事务才需要,从这个角度出发,就必须要DI支持AOP切面功能,而你却没有加入这个功能,这就很尴尬了,当需要声明式事务的时候,不得不引入一个支持AOP的DI工具,例如Spring/Guice/jFinal,这就造成了使用ACT的项目随时都具备了2套DI工具,也就是说你自带的DI工具实际上是多余的,尤其在流行的Boot环境下,各种配置都是建立在Spring-Core这个IOC/AOP工具基础上,别人不大可能移到Genie内核上。
另外考虑一下支持多种模板输出,如包括PDF输出,这才是MVC的V层要做的事,可以参见SpringMVC和Jfinal,必要时可抄它们的源码。jFinal的问题是DAO、IOC、MVC混成一团,是优点,更是一个大缺点,希望你将主要精力集中在MVC,将它做成一个精致、干净的后台表现层,不要介入任何DI、AOP、DAO、事务的工作,这方面优秀的、流行的工具太多,没必要重造轮子。你现在这个HTTP内核也是自已做的,为了一点点效率或钻牛角尖的小特性,放弃通用性,也是冒风险的,在项目MVC架构选型的第一步就可能被Pass,如果能将HTTP内核和MVC分开,比如说ACT的MVC即可以和自已的内核合用,也可以和Jetty或内嵌Tomcat内核合用,这才是一个比较优秀的架构。
首先感谢 drinkjava 同学的意见,看得出来是问题是认真思考之后提出来的. 下面就问题中的两段意见分别作答.
2. 问题一: 一个 MVC 工具为什么要引入 DI 依赖注入
这个问题有两个地方值得商榷:
-
上面这个问题隐含的一个前提假设是 Act 是一个 MVC 工具. 实际上这个前提有一点问题, 我启动 Act 项目的动机是希望弄一个符合自己想法的 PlayFramework V1.x 的后继者. Play 本身除了是一个开发框架,也是一个运行时平台, Act 也是. 单单用 "MVC 工具" 来描述 Act 并不符合我自己的想法. 用 "MVC 工具" 来描述 Act 的依赖 osgl-mvc 更能够贴切一点.
-
MVC 工具为什么不能引入 DI 依赖注入. 后面 drinkjava 同学也提到 "直接引入 Spring 或 Guice 的不好吗?", 说明 drinkjava 并不是认为 MVC 工具不能引入 DI 依赖注入, 而是认为 Act 引入的 DI 依赖注入 Genie 没有提供 AOP 功能, 而 AOP 功能在他看来是实现声明式事务必须的,所以才认为不适合.
2.1 Act 的依赖注入机制与应用
下面我们就 javadrink 同学上面的关切来谈谈 Act 的依赖注入机制与应用.
这里是来自 Wikipedia 对依赖注入的定义 "In software engineering, dependency injection is a technique whereby one object supplies the dependencies of another object". 简单地说就是对象的状态不由自己来创建,而是交给另外的对象来注入. 举个例子:
public class UserService { @Inject private Dao<User> userDao; @GetAction("/users/{userId}") public User get(String userId) { return userDao.findById(userId); } }
上面是一个简单的 UserService 端口. 其中需要使用对应与 User
实体类的 Dao
. 在上面的代码中我们没有看到 userDao
是如何初始化的, 因为 userDao
是 Act 框架在实例化 UserService
的时候注入的. 这就是一个典型的 Act 应用依赖注入的方式. 当然 Act 对于依赖注入的使用还有其他的扩展. 我们稍后会提到.
2.1.1 为什么不用 Guice 或者 Spring
现在我来回答 drinkjava 的这个问题: "直接引入 Spring 或 Guice 的不好吗?". 实际上在开发 Genie 之前, Act 尝试过另外两种依赖注入:
在 Act 正式发布之前, 这上面两种注入都曾经在 act 0.x 版本中进入过实际项目 (当然是老码农所在公司的 - 开坑自己先踩是老码农做开源的基本原则).最终我选择了自己开发 Genie 来提供 Act 的依赖注入, 主要原因有一下几点:
- Feather 的实现足够简单轻量; 但并不是 JSR 330 的完整实现, 比如不支持方法注入, 对 Scope 的支持不完善, 扩展性不够好
- Guice 提供了完整的 JSR 330 实现; 但同时也引入了一些额外的特性, 比如 Servlet 集成等. Guice 的代码实现也相对比较晦涩.
- Spring 的依赖注入至始至终都不是我的一个选项, 首先 Spring 的依赖注入不是 JSR 330 标准的实现, 另外 Spring 的依赖注入运行时效率太低 (参见依赖注入性能测试项目).
Feather 简洁的代码实现最终激励了我启动了 Genie 项目, 这个依赖注入库完整实现了 JSR 330, 同时提供了一些有趣且实用的扩展, 比如注入集合类型数据以及注入值数据 等, 这些扩展对实现 Act 里面的 @LoadResource
, @LoadConfig
还有 @Configuration
至关重要. 另外因为代码实现比较紧凑, 运行时效率也很不错, 在多项测试中都领先 Guice; 具体数据可以参考这个项目
2.1.2 依赖注入扩展 I - 请求处理方法参数的注入
接下来说说 Act 对传统依赖注入的第一个扩展扩展: 注入请求处理方法参数.
上面那个 UserService
的例子是经典的依赖注入使用方式, 除了将依赖注入字段, Act 还允许直接将服务注入请求处理方法, 例如:
@GetAction("/users/userId") public User get(String userId, Dao<User> userDao) { return userDao.findById(userId); }
上面代码中, get
方法有两个参数, userId
和 userDao
, 其中 userId
绑定到 URL 路径参数, 假如请求是 /users/abc123
, 那 userId
的值就是 abc123
; 而第二个参数 userDao
就是依赖注入了, 这个和前面注入到 userDao
字段是一样的. 但
又例如:
@PostAction("login") public void login(String email, char[] password, ActionContext context) { User user = userDao.findByEmail(email); badRequestIf(null == user, MSG_BAD_EMAIL_PASSWORD); badRequestIfNot(user.verifyPassword(password), MSG_BAD_EMAIL_PASSWORD); context.login(user); }
上面代码中的 ActionContext
也是注入的对象.
2.1.3 依赖注入的扩展 II - 资源和配置参数注入
得益于 Genie 的扩展机制, Act 中可以很轻易地注入加载资源和配置参数.
public static class Dao extends MorphiaDao<Contact> { @LoadResource("industry_type.mapping") private Map<String, String> industryTypeMapping; @Configuration("sql.url") private String jdbcUrl; ... }
上面的代码来自实际项目, 其中使用了两种扩展注入:
@LoadResource("industry_type.mapping)
将资源文件industry_type.mapping
的内容加载进Map<String, String> industryTypeMapping
字段@Configuration("sql.url")
将配置项sql.url
的值加载进String jdbcUrl
字段
资源文件 industry_type.mapping
的内容为:
AG1 - Agriculture=Agriculture AG2 - Agribusiness=Agribusiness AG3 - Hospitality=Tourism and Hospitality AG4 - Food Manufacturing/Processing=Food Manufacturing/ Processing ...
可以看出依赖注入在这种场景的使用减少了 boilerplate 代码的使用, 让应用代码变得更加简洁易懂.
2.1.4 依赖注入机制总结
通过上面关于依赖注入机制的介绍, 可以看出依赖注入在 Act 应用中是基本的机制, 而 drinkjava 同学在问题中表达的观点 "DI唯一比较经典的用法只是用来进行声明式事务才需要" 完全不能阐述依赖注入在 Act 框架的作用.
2.2 关于声明式事务和 AOP
drinkjava 同学提出 "DI唯一比较经典的用法只是用来进行声明式事务才需要,从这个角度出发,就必须要DI支持AOP切面功能,而你却没有加入这个功能". 这个我完全不能理解, DI 和 AOP 是完全不同的概念, 我从来不知道 DI 需要支持 AOP, DI 的 Java 标准 JSR 330 也完全没有提到这个特性. 而 Wikipedia 上 AOP 的页面 也根本没有谈到 Dependency Injection
的概念. 把这两个放在一起 Google 了一下, 发现这篇文章详细分解了一下这两个概念, 有兴趣的同学可以点击进去看看.
Act 目前不支持 AOP, 但 Act 提供的 SQL DB 插件, 包括 act-ebean, act-hibernate 以及 act-eclipselink 都支持声明式事务. 具体应用代码可以参考下面几个示例项目:
act-ebean
和 act-hibernate
, act-eclipselink
对声明式事务的实现机制是不同的.
- act-ebean 将声明式事务的实现交给 ebean 引擎. 而 Ebean 是采用了 java agent 对代码做增强来实现声明式事务
- act-hibernate 和 act-eclipselink 对声明式事务的实现机制都在 act-jpa-common 插件中, 是通过 ASM 对代码做增强来实现的.
我并不认为 AOP 对于应用开发来说是一个非常重要的特性, 也一直没有动手做 AOP 的支持. 我的理由如下:
- 我认为 AOP 的应用场合并不是非常普遍的, 可能的场景有:
- 声明式事务 - ACT 有相应的实现机制 (act-db)
- Auditing (审计) - ACT 有相应的实现机制 (act-aaa)
- 性能监控 - ACT 有相应的实现机制 (act-core, ASM based MetricEnhancer)
- 异常处理 - 我不赞同复杂而精巧的异常处理 - 更新的语言包括 .net C# 还有 groovy, kotlin 等都去掉了 CheckedException 这个概念. Web 应用程序的异常处理应该尽量轻量化.
- 通用的 AOP 的对于应用开发来说太过晦涩, 且容易导致难以调试的功能性以及性能方面的问题.
反过来讲, AOP 的适用场景都能采用专门的机制来实现, 对于应用来讲可以写出更加简洁容易的代码, 而且没有性能上的损耗. 这里我可以断言 drinkjava 同学评论中的说法 "当需要声明式事务的时候,不得不引入一个支持AOP的DI工具,例如Spring/Guice/jFinal,这就造成了使用ACT的项目随时都具备了2套DI工具,也就是说你自带的DI工具实际上是多余的" 是不成立的. 在 Act 中使用声明式事务以及我上面提到的另外两种 AOP 应用场景都不需要 AOP.
3. 问题二: 考虑一下支持多种模板输出,如包括PDF输出
这其实不是问题, 是一条建议. 看到这个建议我感觉 drinkjava 同学可能还不太熟悉 Act 的模板输出机制. views 示例项目展示了 Act 中同时使用多种不同的模板引擎的特性, 包括:
- beetl
- freemarker
- mustache
- rythm
- thymeleaf
- velocity
而 response-type 示例项目中展示了 Excel 模板的输出 (采用 JXLS 引擎). 可以说 Act 的模板输出框架是足够满足 (同时) 使用多种模板的. 当然到目前位置我还没有开发 PDF 的模板插件, 这个可以作为今后的一个工作.
4. 问题三: 集中精力在 MVC
这里回答 drinkjava 同学评论的最后一部分:
"jFinal的问题是DAO、IOC、MVC混成一团,是优点,更是一个大缺点,希望你将主要精力集中在MVC,将它做成一个精致、干净的后台表现层,不要介入任何DI、AOP、DAO、事务的工作,这方面优秀的、流行的工具太多,没必要重造轮子。你现在这个HTTP内核也是自已做的,为了一点点效率或钻牛角尖的小特性,放弃通用性,也是冒风险的,在项目MVC架构选型的第一步就可能被Pass,如果能将HTTP内核和MVC分开,比如说ACT的MVC即可以和自已的内核合用,也可以和Jetty或内嵌Tomcat内核合用,这才是一个比较优秀的架构。"
- "希望你将主要精力集中在MVC,将它做成一个精致、干净的后台表现层" - 在博客开头我也有讲, Act 并不是一个 MVC 工具, 而是 Web 应用框架以及运行时平台. 另外 "后台表现层" 是个什么鬼? 原谅我读书少, 理解不了这个术语 ^_^.
- "这方面优秀的、流行的工具太多,没必要重造轮子" 这句话我不太赞同, MVC 本身优秀的,流行的工具也很多,SpringMVC 就是一张大伞, 按照这个说法, Act 干脆就不要做了. 我的策略是, 首先看市面上有没有满足自己要求的, 有就用 (比如 Play, FastJSON, JXLS 等等), 没有就自己做 (比如 Rythm, Act, Genie 等等).
- "你现在这个HTTP内核也是自已做的,为了一点点效率或钻牛角尖的小特性,放弃通用性,也是冒风险的". 这里 drinkjava 同学高看我了, Act 的 HTTP 核心实现是 JBoss 的 Undertow. 我在悄悄打算迁移到 Netty 上去.
- "如果能将HTTP内核和MVC分开,比如说ACT的MVC即可以和自已的内核合用,也可以和Jetty或内嵌Tomcat内核合用,这才是一个比较优秀的架构。" - Jetty 和 Tomcat 是基于 Servlet 架构的 (不是 HTTP 网络层核心, 而是 API 层的架构), 我认为 Servlet 架构背负太多的历史报复, 对于现代 Web 框架来说并不是一个很好的选择. 至于 Act 是否是一个比较优秀的架构, 我并不是特别在意. 能在开发时提供友好的支持, 运行时提供足够的性能就行.
最后再次感谢 drinkjava 的评论, 很认真, 信息量很多, 所以我也很认真地使用一篇来回答你的评论.
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Mozilla 为开发者推出新的网页调试工具 Firefox Replay
Mozilla 在最近的 Firefox Nightly 更新版本中推出了一项名为Firefox Replay的实验性工具,它允许 Firefox 内容进程记录其行为,稍后重放并回退到早期状态。重放过程会保留所有相同的 JS 行为、DOM 结构,图形更新,以及其他行为。浏览器的 JS 调试器可用于检查和控制重放。目前这一功能仅支持 macOS。 MDN(Mozilla Developer Network,Mozilla 开发人员网站) 网页文档中将该功能称为Web Replay,由于还在测试中,它是默认禁用的,可以使用 devtools.recordreplay.enabled 首选项手动开启。通过“工具”->“ Web 开发者”菜单,查看“录制/回放”选项卡,启用的新调试器界面,可以访问功能。文档还提到,其中部分内容仍处于pre-alpha 阶段。 Firefox Replay 开发团队的成员jasonlaster11 也在网络发帖回复了大家的疑问。他表示该功能目前处于用户阶段,一旦被列入正式路线图,绝对会优先考虑跨平台支持。官网 firefox-replay.com 也正在构...
- 下一篇
如何设计一个安全的对外接口
前言最近有个项目需要对外提供一个接口,提供公网域名进行访问,而且接口和交易订单有关,所以安全性很重要;这里整理了一下常用的一些安全措施以及具体如何去实现。安全措施个人觉得安全措施大体来看主要在两个方面,一方面就是如何保证数据在传输过程中的安全性,另一个方面是数据已经到达服务器端,服务器端如何识别数据,如何不被攻击;下面具体看看都有哪些安全措施。1.数据加密我们知道数据在传输过程中是很容易被抓包的,如果直接传输比如通过http协议,那么用户传输的数据可以被任何人获取;所以必须对数据加密,常见的做法对关键字段加密比如用户密码直接通过md5加密;现在主流的做法是使用https协议,在http和tcp之间添加一层加密层(SSL层),这一层负责数据的加密和解密;2.数据加签数据加签就是由发送者产生一段无法伪造的一段数字串,来保证数据在传输过程中不被篡改;你可能会问数据如果已经通过https加密了,还有必要进行加签吗?数据在传输过程中经过加密,理论上就算被抓包,也无法对数据进行篡改;但是我们要知道加密的部分其实只是在外网,现在很多服务在内网中都需要经过很多服务跳转,所以这里的加签可以防止内网中数据...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Mario游戏-低调大师作品
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8编译安装MySQL8.0.19
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7