CDI和Spring的主要差别

继上一篇文章《Java元注解meta-annotation与依赖注入》,我又探索了两大依赖注入技术体系CDI和Spring的关系。Spring实现了CDI规范吗?相信你们也会有这种问题。html

事实上有两个规范,一个JSR-299 CDI规范,一个JSR-330 DI规范,绕吧,是挺绕。Spring好像只实现了DI规范,而没有实现CDI规范。那么CDI和DI、CDI和Spring都有什么差别呢?java

CDI和DI的差别,主要表如今DI主要定义了5个注解(@Inject, @Named, @Qualifier, @Scope, @Singleton)及语义,而CDI则主要定义了scope, interceptor, bean lifecycle callback, EL integration, Java EE integration等API、SPI和语义,DI不特定于Java EE,而CDI特定于Java EE。编程

CDI和Spring的差别,例如,CDI定义了几个标准scope(request, session, application, conversation),容许自定义其余scope但只能看成“pseudo-scope”,而没有Spring的prototype和singleton。而Spring不仅有prototype和singleton,又在Web MVC模块里定义了几个scope(request, session, globalSession, application),所以双方在scope方面是不一样的。session

此次主要讲一个我认为很大的差别,即CDI的Client Proxy机制,见文档https://docs.jboss.org/cdi/sp...app

Client Proxy机制的特色是,若是为一个UserController注入了一个UserService bean,这个UserService实际上不是真正的UserService对象,而是一个代理对象,示例代码以下:框架

@Named
public class UserController {
    // 这个不是真正的UserService对象,而是一个代理
    @Inject
    private UserService userService;
}

@Named
public class UserService {
}

为何要这么代理一下呢?代理的做用是动态化,当UserController被实例化时,它只持有一个UserService代理的引用,此时UserService无需从上下文查找,甚至无需实例化(CDI的bean都是lazy creation的),只有当UserController在其方法中真的要使用UserService时,才到上下文中查找UserService,若没有就实例化一个。这一层抽象容许scope较大的bean引用scope较小的bean,例如UserController能够是application scope,而UserService能够是request scope,只要UserController在真的使用UserService时刚好进入了request scope就能够。函数式编程

Seam框架(CDI规范的起源之一)甚至还支持双向注入注出,即若是UserController的userService field被修改为另外一个值(指向一个新的UserService实例),那么当UserController的方法执行结束后,这个新值会被写回上下文,使得上下文中的UserService实例被替换。这一机制也是利用了代理,Seam给全部的client proxy都加了AOP interceptor来实现以上机制。函数

作个有条理的对比,CDI是如此:单元测试

  1. 对于几种标准scope,注入的是client proxy而非真实的bean,注入后仍会动态跟踪上下文的变更
  2. lazy creation
  3. 不管UserController是哪种标准scope,都只有在真的用到某个依赖项时才知道其是否有效
  4. scope较大的bean容许依赖scope较小的bean
  5. 标准scope的bean都容许循环依赖(利用代理来作实时解环处理)

Spring显然并不是如此:测试

  1. 默认注入的是真实的bean而非client proxy,注入后就再也不跟踪上下文的变更
  2. 默认eager creation,可用@Lazy注解配置为lazy creation
  3. 若是UserController是singleton scope(默认值),那么在应用启动时就会校验它所要注入的依赖是否有效
  4. scope较大的bean不容许依赖scope较小的bean
  5. 只有singleton scope的bean容许循环依赖(应用启动时用两阶段初始化来作解环处理)

Spring的风格很有函数式编程的不可变性,而CDI自动启用的可变性代理则显得有些多余。事实上市场也选择了Spring。(其实如今的CDI比Spring还要轻量级,new一个就能用,包括单元测试环境,而Spring却须要@SpringBootTest注解,那么究竟差距在哪里呢?)

相关文章
相关标签/搜索