[weld] 咱们工人有力量

wuhaixing 2010-04-19
让container创建对象实例会有局限性,毕竟不能像你自己写代码那么随心所欲。所以CDI提供了Producer方法的机制,这也是将非bean的对象集成到CDI环境中最简单的办法。
依照规范:
工人阶级的历史先进性体现在如下几个方面:
  • 团结一切可以团结的力量,要注入的对象可以不是bean的实例
  • 根据需要灵活多变,注入对象的类型在运行时可以变化
  • 乐于奉献,bean的构造函数不干的咱可以干

比如,工人(以下简称Producer)方法可以:
  • 将JPA entity包装成bean
  • 将任意一个JDK 类包装成bean
  • 将同一个类放到不同的scope、或赋予不同的初始化值,变成多个bean
  • 在运行时改变bean类型的实现


Producer方法给我们带来了运行时的多态性。前面已经见过了alternative bean带来的部署时多态性,但一旦部署完成,系统就固定下来了。可Producer方法不受这个限制:
@SessionScoped
public class Preferences implements Serializable {
   private PaymentStrategyType paymentStrategy;
   ...
   @Produces @Preferred 
   public PaymentStrategy getPaymentStrategy() {
       switch (paymentStrategy) {
           case CREDIT_CARD: return new CreditCardPaymentStrategy();
           case CHECK: return new CheckPaymentStrategy();
           case PAYPAL: return new PayPalPaymentStrategy();
           default: return null;
       } 
   }
}


来看看注入的时候:
@Inject @Preferred PaymentStrategy paymentStrategy;

它和Producer方法的返回类型、qualifier 标记相同,根据CDI的注入解析规则,他会找到Producer方法,container会调用这个方法来创建注入点所要求的实例。
wuhaixing 2010-04-19
8.1 Producer方法的空间
Producer方法的scope默认是@Dependent,所以container每次都会调用这个方法来创建符合注入要求的bean。因此,每个用户session中可能都会有多个PaymentStrategy对象。
要改变这种行为,可以给方法打上@SessionScoped标记。

@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy() {
   ...
}


现在,当调用Producer方法的时候,返回的PaymentStrategy会被放进session context中,因为session中已经有了这个实例,所以相同的session中再有需要时container也不会再调用方法了。
注意
Producer方法的scope和他所在的bean的scope是两码事。Producer方法的scope是指他所创建的bean的scope,决定的是调用方法的频率,以及返回对象的生命周期。而声明了这个方法的bean的scope决定的是这个bean本身的生命周期。就是说公司老板过的日子和打工的你过的日子不一样。
wuhaixing 2010-04-19
工薪族的价格,贵族式的享受

上面的代码有个问题,你亲自用了new!直接由程序创建的对象和由container创建的对象不是一个种,因此不能享受dependency injection,也没有interceptor。

如果你还想让Producer的返回值享受这些服务,可以把bean的实例注入到producer 方法中(哦,抱歉,这个世界上本来就没有公平可言!):

@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy(CreditCardPaymentStrategy ccps,CheckPaymentStrategy cps,PayPalPaymentStrategy ppps) {
   switch (paymentStrategy) {
      case CREDIT_CARD: return ccps;
      case CHEQUE: return cps;
      case PAYPAL: return ppps;
      default: return null;
   } 
}


但这又会产生新的问题,如果CreditCardPaymentStrategy是request-scoped bean,那producer 方法就会把这个本来应该呆在request session中的对象放到session scope中。request结束的时候,这个对象死了,可session中的bean们还不知道,以为还能找他解决问题。最麻烦的是container发现不了这个问题,你得自己多加小心,谨慎对待从producer方法返回的bean实例。

这个问题是可以解决的,而且方法不止一个。改变 CreditCardPaymentStrategy 实现的scope,或者改变producer方法的scope。

如果你还不满意,可以用@New 标记。
wuhaixing 2010-04-19
告诉container你要的是@New bean
先看代码:

@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy(@New CreditCardPaymentStrategy ccps,
                                          @New CheckPaymentStrategy cps,
                                          @New PayPalPaymentStrategy ppps) {
   switch (paymentStrategy) {
      case CREDIT_CARD: return ccps;
      case CHEQUE: return cps;
      case PAYPAL: return ppps;
      default: return null;
   } 
}


你说了,container就会给你创建一个新的CreditCardPaymentStrategy交给producer方法,这样producer方法返回的对象就会放进session context,也就会在session结束的时候才会被销毁。
wuhaixing 2010-04-19
管杀管埋
有时候你想亲手毁了producer方法产生的对象,总得有机会干干关闭jdbc连接之类的事吧。
@Produces @RequestScoped Connection connect(User user) {
   return createConnection(user.getId(), user.getPassword());
}

这时候可以在同一个类中定义一个与producer方法配对的disposer方法:
void close(@Disposes Connection connection) {
   connection.close();
}

disposer方法至少得有一个参数,标记为@Disposes,和producer方法返回类型及qualifiers一致。当context结束的时候,container会调用disposer方法,并且把producer方法返回的对象赋值给前面提到的那个参数。如果还有其他的参数,container会去找符合条件的bean进行赋值。
deeny 2010-04-19
站位有用吗?
qq905565585 2010-04-20
Produces @RequestScoped Connection connect(User user) {  
2.   return createConnection(user.getId(), user.getPassword());  
3.} 
Global site tag (gtag.js) - Google Analytics