前言
CDI(Contexts and Dependency Injection 上下文依赖注入),是JAVA官方提供的依赖注入实现,可用于Dynamic Web Module中,将依赖注入IOC/DI上升到容器级别, 它提供了Java EE平台上服务注入的组件管理核心,简化是CDI的目标,让一切都可以被注解被注入。CDI是为解耦而生.如Spring主要用途是AOP.IOC(DI),而CDI除了DI外,AOP功能也是有的.从实际使用上来看,CDI比Spring功能更丰富,更灵活,其代价也是有的,学习成本相对spring较高.
1.CDI致力于松耦合,强类型.
实现松散耦合的三种方式:
- 部署时候的多态选择,@alternatives
- producer methods在运行时的多态.
- 上下文相关的生命周期管理与bean生命周期解耦。
这些技术使客户端和服务器的松散耦合服务。客户端不再是紧密地绑定到一个接口的一个实现,也不需要管理生命周期实现。这种方法允许有状态的对象当作服务交互。松散耦合使系统更具活力。在以前,框架总是牺牲了类型安全(尤其是通过使用XML描述符,Spring2.5)。
CDI提供了三个额外的重要措施,进一步松耦合:
- 在业务逻辑层用拦截器技术解耦.
- 修饰符(注解)可以用来分离一些业务问题
- 用CDI EVENT技术进行解耦事件生产者与消费者.
第二个CDI是强类型的.无论是依赖关系的信息,拦截器,修饰符的Bean,以及CDI event的生产者,消费者等的信息全部都是类型安全的.由编译器进行验证.
CDI是确确实实没String标识符,如xml配置什么的.比如Spring2.5用XML配置,其实都是字符串,以及"约定大于配置"的概念.在CDI里是没有的.CDI框架不是隐藏,而是没有.
这种方法的最明显好处就是任何IDE都可以提供自动完成,验证以及最重要的重构!(了解JPA的,可以对比安全类型的查询和JPQL.如果重构代码JPQL是非常麻烦的).
还有个好处就是你在识别不同的对象,事件,拦截器可以通过注解而不是字符串名字,这样你可以提升代码质量.
CDI鼓励开发使用注解.如
@Asynchronous,
@Secure,
@Updated,
而不是使用复合名称,
asyncPaymentProcessor,
SecurityInterceptor
DocumentUpdatedEvent.
这也符合代码大全里的一些概念.只不过不用费尽心思考虑命名了,这样更简洁高效.
注释是可重用的。他们帮助描述系统的不同部分的共同特质。他们帮助我们分类和理解代码。他们帮助我们应对常见问题的常用方法。他们使我们的代码更简洁高效.
2.高级功能Producer methods
先贴一段代码,下面都用到
1 import javax.enterprise.inject.Produces; 2 3 @SessionScoped 4 public class Preferences implements Serializable { 5 6 private PaymentStrategyType paymentStrategy; 7 8 ... 9 10 @Produces @Preferred 11 public PaymentStrategy getPaymentStrategy() { 12 switch (paymentStrategy) { 13 case CREDIT_CARD: return new CreditCardPaymentStrategy(); 14 case CHECK: return new CheckPaymentStrategy(); 15 case PAYPAL: return new PayPalPaymentStrategy(); 16 default: return null; 17 } 18 } 19 } 20 //注入一个Producer methods 21 @Inject @Preferred PaymentStrategy paymentStrategy;
A:Producer methods的Scope
Producer methods的默认范围是@Dependent.
从上面代码我们可以思考一种场景,那就是一个用户会话中有多个PaymentStrategy对象的实例.如果想改变,我们可以在Producer方法上添加一个@SessionSciped注解.
现在,如果一个用户调用了这个Producer methods,那么返回的这个PaymentStrategy对象的实例将绑定到会话的上下文.Producer methods不会再实例化另一个出来.
1 @Produces @Preferred @SessionScoped 2 public PaymentStrategy getPaymentStrategy() { 3 ... 4 }
注意:Producer methods不继承声明此Producer methods的Bean的Scope.
其实这里有2个不同的Bean:Producer methods(相当于一个Bean)以及声明这个生产方法的Bean.
B: Injection into producer methods
在Producer methods一开始的实例有一个潜在的问题
CreditCardPaymentStrategy 的实现使用 Java new 运算符来实例化。
private PaymentStrategyType paymentStrategy;
而producer methods应该理解为一个独立的Bean,而paymentStrategy是从Preferences 中用new实例化的.所以我们应该使用下面这种方式来使用producer methods方法.
1 @Produces @Preferred @SessionScoped 2 public PaymentStrategy getPaymentStrategy(CreditCardPaymentStrategy ccps, 3 CheckPaymentStrategy cps, 4 PayPalPaymentStrategy ppps) { 5 6 switch (paymentStrategy) { 7 case CREDIT_CARD: return ccps; 8 case CHEQUE: return cps; 9 case PAYPAL: return ppps; 10 default: return null; 11 } 12 }
这里会有问题,如果CreditCardPaymentStrategy 是一个@RequestScope,那这里必然是要发生错误的.因为注入的CreditCardPaymentStrategy Bean实例是request,在@SessionScoped使用前容器就会进行销毁.那么就出错了.
- producer method to @Dependent or @RequestScoped.<最好的方式>
- CreditCardPaymentStrategy 更改Scope,但这可能会影响其他的地方,不是很好.
使用@New限定符,但在CDI 1.1 @New限定符被弃用。CDI鼓励应用程序注入@Dependent范围bean。
C:Use of @New with producer methods<不推荐>
Consider the following producer method:
1 @Produces @Preferred @SessionScoped 2 public PaymentStrategy getPaymentStrategy(@New CreditCardPaymentStrategy ccps, 3 @New CheckPaymentStrategy cps, 4 @New PayPalPaymentStrategy ppps) { 5 switch (paymentStrategy) { 6 case CREDIT_CARD: return ccps; 7 case CHEQUE: return cps; 8 case PAYPAL: return ppps; 9 default: return null; 10 } 11 }
这将会创建一个新的CreditCardPaymentStrategy依赖实例,传递到生产方法,依赖对象不会被摧毁,直到会话结束。
在CDI 1.1 @New限定符被弃用。CDI鼓励应用程序注入@Dependent范围bean。
D:Disposer methods
一些Procucer methods返回的对象需要显式的破坏。例如,有人需要关闭这个JDBC连接:
1 @Produces @RequestScoped 2 Connection connect(User user) { 3 4 return createConnection(user.getId(), user.getPassword()); 5 }
而在一个相同的类中,disposer method可以进行匹配.
1 void close(@Disposes Connection connection) { 2 3 connection.close(); 4 }
说明:在同一个类中,disposer method可以进行匹配类型为Connection 的Procucer methods,从而在 Procucer methods周期结束后进行jdbc的链接关闭.
如下面这个:
1 import javax.enterprise.context.RequestScoped; 2 import javax.enterprise.inject.Disposes; 3 import javax.enterprise.inject.Produces; 4 import javax.enterprise.inject.spi.InjectionPoint; 5 import javax.persistence.EntityManager; 6 import javax.persistence.EntityManagerFactory; 7 import javax.persistence.PersistenceUnit; 8 9 import org.slf4j.Logger; 10 import org.slf4j.LoggerFactory; 11 12 public class Resources { 13 14 @PersistenceUnit 15 private EntityManagerFactory entityManagerFactory; 16 17 @Produces 18 @RequestScoped 19 protected EntityManager createEntityManager() { 20 21 return entityManagerFactory.createEntityManager(); 22 } 23 24 //参数必须对应上面方法的返回值 25 protected void closeEntityManager(@Disposes EntityManager entityManager) { 26 27 if ( entityManager.isOpen() ) 28 { 29 entityManager.close(); 30 } 31 } 32 }
参考链接:https://my.oschina.net/zhaoqian/blog/263422#h1_1