展望CDI 2.0规范
本文译自 http://www.next-presso.com/2014/03/forward-cdi-2-0/
作者Antoine Sabot-Durand是红帽软件工程师,CDI规范专家组成员,本文提出了对未来的CDI 2.0规范想法,体现了作者对于组件运行的本质思考,是难得的好文。
不过估计写成此文时一气呵成,所以有些笔误和未达意之处。我翻译的也比较急,难免有错误之处,敬请谅解。
====== 以下为译文 =========
CDI可能是在JavaEE中最容易被忽略的规范之一。4年前1.0版本发布时,CDI是作为"JavaEE的扩展点“出现的。从技术来说它的确如此,但是并不是所有JavaEE的JSR都完全采用CDI,来提供一致的编程体验。今天IT世界快速发展,经过很多年使用巨大内存和多核的技术方案,我们又回归到原来需要优化资源使用的日子,面向移动或者嵌入式平台开发。由于CDI可以在JavaEE之外使用,所以它可以在新的方案里成为有趣的一个角色。但在此之前,它应该继续演进,并为这个新的挑战做好准备(并继续推进在JavaEE中越来越重要)。在这篇文章中我将尝试分享我的想法,CDI应该如何演变来满足未来的需求。我会探讨新的功能特性,使用一种更加模块化的体系结构,来使CDI可以容易扩展,从小型的树莓派设备到大型的集群方案。
声明:由于我会领导CDI 2.0规范(和Pete Muir一起),需要强调这篇文章都是我个人的观点,而不代表CDI 2.0的官方内容,或者是红帽软件CDI专家组的任何看法。
我想看到的新特性
一些特性已经被专家组讨论了,有些没有加入到1.1的推迟到2.0版本。有些是第三方开发的内容(主要是Apache的Deltaspike)规范标准化,有些是全新的。无论如何,大部分观点都很重要,可以帮助第三方项目或者JSR来促成最佳的CDI规范。
在JavaSE中启动
如今每一个CDI实现都提供一种方式来在JavaSE或者简单服务器环境(如仅包括Servlet)中启动CDI,Apache Deltaspike有一个通用的解决方案来做到这个。现在可以考虑把CDI规范集成到JavaSE中了。
容器热交换
强类型注入非常有用,但我认为CDI这方面太严格了。为了支持动态JVM语言,我们应该能够触发BeanManager重新启动来加载更多的bean,比如提供API来启动一个新的CDI容器来扩充内容。如果启动顺利,通过更多的步骤在新的容器中来复制状态和存在的实例,并保留旧容器的垃圾存在现状。这样在出错的情况下,我们可以保存当前的BeanManager。这是一个代价比较大,但是非常有用的特性,特别是对于一些高级工具或者特定功能的应用系统(比如CMS,电子商务平台等)
轻量级CDI
正如之前谈到的,业界希望能让CDI运行在嵌入式系统中,如树莓派,Arduino,乐高积木Mindstorms或者Android。如今大量使用Proxy阻碍了这个方向。
Java Proxy在2000年时非常有用,但现在这项技术太老了,带来很多问题(巨大的堆栈跟踪,JVM优化,高资源消耗等),现在CDI规范也想当然的未来代理用在实现之中。
这样看来,有一个好的方案是提供一个CDI规范子集,更少的限制,更加轻量。我们可以称之为“CDI Lite”(类似EE6中的EJB Lite)。这个子集会包括DI的所有内容,但可能去除所有和上下文(Context),Interceptor(拦截器),Decorator(装饰器)等内容。
也有讨论如何在不移除特性的情况下让CDI更轻量一些。
再见Proxy,欢迎Annotation和InvokeDynamic
另一个方法是在CDI实现中找到其他方案来替换Proxy,现在我们有两个选择:
- Annotation处理:编译时就对annotation进行处理,生成静态“magic"注入/装饰代码,类似于Dagger。
- InvokeDynamic:运行时进行链接,可以提供类似Proxy的行为,而较少的缺陷和可能有更好的性能。对于第二种方法,我开始做一些研究,希望很很快提供概念验证方案。
XML 配置文件
现在是提供这个功能的时候了。现存方案中使用扩展点来提供这个特性,但这个应该是核心功能,可以用于在部署时定义如何注册Bean和Annotation的重新载入,所以应该在规范中定义。如果CDI能够在其他规范中越来越适用,那么这个XML定义可能会成为通用的JavaEE配置文件格式的入口点!
如今,一些框架比如ApacheCamel不能使用CDI,就是因为没有配置方案,所以提供这个能力会扩展其应用范围。
异步的消息和事件
现在是Vert.x和Node.js异步处理的时代(我们想象一下:回调)。可以通过使用并发包在规范中进行定义。我们将提供一种方法来支持异步调用,无须用EJB的异步调用和异步事件方式,比如在@Observes加入一个异步boolean项,并且提供可选项来处理回调。
支持@Startup
加入一个简单功能:CDI bean在初始化阶段以后就自动完成实例化步骤,就和EJB目前的差不多。
这是一个开发者普遍的需求,而并不难做到。
可移植扩展API的推广和简化用法
我认为可移植扩展API是CDI中最好的特性。当然Ioc,事件和上下文管理都是很棒的,但它们在CDI 1.0引入时并不是最新的概念。
而可移植扩展API完全是CDI的创新,加入到JavaEE DNA中,可以无须使用私有方法就可以自然的扩展。
我认为这一点没有被推广很可惜(CDI的缩写中也没有体现其想法),内容还被放置在规范文本的结尾处,无视很多项目和规范可以从中收益。
我的分析是,缺乏和很多复杂概念项目进行沟通,难以深入到扩展的开发之中。我们也许可以提供更高的简化层来容易上手来开发扩展。不要误会我的意思,现有的机制是很好的,可以继续保留(可以增强),但我们可以提供辅助工具来更容易的创建扩展。可以是以下几种:
- Deltaspike AnnotationTypeBuilder和BeanBuilder标准化,使得创建Bean更容易。
- 针对类型和annotation的自省工具。
- 更简单的方法来创建新的和扩展已有的作用域。我们鼓励其他规范重用现有作用域(如@RequestScoped)的生命周期,但只能在实现层上做到这一点。
我们还应该特别注意初始化过程中的第一个事件(直到AfterTypeDiscovery)。因为CDI还不知如何处理类型和Annotation元数据的修改。这些未来会成为JavaEE配置系统的一部分。
事件执行顺序化
在CDI 1.1中,通过@Priority定义了装饰器和拦截器的优先顺序。是否也可以用在订购事件上?在@Observes上使用@Priority不是个好注意,因为这个annotation来自拦截器包中,我们可以在@Observes加入priority字段。
去除对Producer和Custom bean的偏见
为什么Producer和Custom bean是CDI的二等公民?我希望能够用装饰器或者拦截器在Producer上,或者至少有API让我能做到这一点。
扩展事件的作用域范围,从包到整个服务器
让CDI事件总线运行在JavaEE世界中更高的层次上,这样可以选择事件的传播范围,是当前应用程序,当前模块(EAR),当前包,或者是其他所有程序都侦听这个事件。
瞬态注入
当把一个依赖bean注入到一个长期存在的bean时,后者初始化时这个注入完成并只执行一次。如果我想每次访问时都再次初始化并且重新注入时(如我在Agorava项目遇到的情况),现在我必须这样写:
@Inject Instance<MyBean> myBeanInstances; public Mybean getMyBean() { return myBeanInstances.get(); }
我希望以后可以这样写:
@Inject @Transient MyBean myBean;
这是个语法糖,但这种写法易写易读。我们可以试着再找一些例子来说明代码如何简化。
更方便的流式查询语法
实现<T>接口的实例和编程时查询都是很有用的,但有时候非常繁琐,特别是处理Qualifiers时。
这个可以通过提供工具和用Java8的类型注解,来生成qualifier来简化,为什么不采用查询DSL?
myBeanInstance.restrictedTo(BeanImp.class).withQualifier(new @MyQualifier("Binding") AnnotationLiteral<>(), new @MyOtherQualifier AnnotationLiteral<>()).select();
是不是看上去友好些?
监控设施
还记得Seam2时优秀的调式页面么?我想有同样的东西或者工具来轻松构建相同的功能,监视bean和运行范围。CDI很神奇,可以有很好的工具来查看所有的内容,bean的成本,上下文和我们部署的拦截器。
更好的模块化支持:新的CDI架构(JavaEE)?
很多JSR都在抱怨CDI规范定义像铁板一块,和它们比较,实现很庞大(他们不想依赖更大的东西)。这个和缺乏JavaSE的引导支持,可能是CDI集成难以走远的2个主要原因。因此,我们可以提供一个更加模块化的方法,收集所有的模块,使用和JavaEE之外一致的代码栈。我理想中的JSR/模块是这样:
容器
容器模块存储所有应用程序用到的bean,作为独立模块提供以下功能:
- 提供最小化的api/实现给客户端程序,通过JNDI来或者bean
- 提供机制来加入插件到容器中,以支持新的组件(Servlet, JPA Entity, Guice, Spring bean)
- 准备成为未来JavaEE的通用容器
事件总线
事件和观察者模式是CDI规范的强大功能,但它们同样可以用于CDI以外。
我们可以设想一个新的规范或者是CDI的一个模块,基于CDI事件总线,来给JavaEE提供更广泛的事件模型。一个API只能依赖半打类(如果我们增加异步处理,排序和事件作用域会更多)来做工作。
组件扫描和扩展引擎
现在每个规范都在启动时进行类的扫描。一般来说应用服务器都是用专有的方式做扫描工作,这个过程可以标准化,通过扫描阶段性事件和元数据的操作,这样我们可以提供一致体验和一个标准的方法来扩展JavaEE。 CDI已经提供了大多数此功能与初始化机制,允许“观察”现有类的部署和修改这些类(即注解)的元数据。
如果ProcessAnnotatedType事件可以在服务器层级被捕获,并允许放置"veto"到给定的servlet或者一组JPA entity上面?这个特性让我们做到单一容器和单一配置文件,这个特性是很多开发者梦寐以求的。
基本的DI
这个模块包括所有的简单注入的API。所以有@Inject, @Qualifier, Instance<>, @Producer, InjectionPoint和反射相关的,就是我说过的"CDI Lite"
上下文管理
Context是CDI一个很好的功能,但大家都不需要,应该放在一个可选的API包中。处理所有正常的范围上下文和复杂的生命周期管理。
拦截器和装饰器
如今拦截器已经有了自己的JSR,加入装饰器到其中会让其更完备。
结论:CDI需要你!
以上是我对CDI的个人愿望清单,你也许有你自己的。我并不知道这些是否是好的想法,是否都可行,我们需要帮助来完成未来的CDI 2.0。 如果你想愿意,敬请关注CDI上的官方网站,@cdispec Twitter帐户(或者我的)和这个博客,并给我们反馈,通过CDI Maillist或CDI的IRC频道(freenode上jsr346)。未来几个月会决定CDI(和Java EE)的未来。
推荐阅读
-
用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识
-
用ASP.NET Core 2.0 建立规范的 REST API -- DELETE, UPDATE, PATCH 和 Log
-
CXL™联盟发布Compute Express Link ™ 2.0规范
-
用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST
-
用ASP.NET Core 2.0 建立规范的 REST API -- DELETE, UPDATE, PATCH 和 Log
-
[活动] Alluxio2018上海Meetup 展望Alluxio2.0
-
JSP2.0规范指令,JSP自动导入包,JSP四大作用域讲解
-
WAP 2.0介绍和使用规范
-
WAP 2.0介绍和使用规范
-
OpenGL 4.1规范发布 全面兼容OpenGL ES 2.0