CDI @Alternative方案通常用于以下目的,如:
1. 为了处理客户特定的业务逻辑在运行时确定。
2. 要指定Bean的有效期为一个特定的部署方案,例如,特定国家的销售税的法律时,需要针对具体国家的销售税业务逻辑。
3. 用于测试的模拟版本。
这个情况下本章的 @Alternative 和 @Specializes内容就是你要学习的.
1. Using alternative stereotypes
CDI允许在部署时使用替代的实现去覆盖一个bean类型。例如,下面bean提供了PaymentProcessor接口的默认实现:.
public class DefaultPaymentProcessor implements PaymentProcessor {
...
}
但在我们的环境中,我们不想使用这个默认的实现,所以我们重写了PaymentProcessor:
@Alternative
public class StagingPaymentProcessor implements PaymentProcessor {
...
}
或者
@Alternative
public class StagingPaymentProcessor extends DefaultPaymentProcessor {
...
}
在bean.xml中进行配置
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee">
<alternatives>
<class>org.credo.StagingPaymentProcessor</class>
</alternatives>
</beans>
这样就在项目启动的时候使用了指定的实现.
但不可能一个个的在bean.xml中配置.CDI提供了更方便的方法.如下先定义一个Stereotype,注意,其包含了@Alternative注解.
@Alternative
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
public @interface Staging {}
在我们这个环境要使用的Bean实现上加@Staging Stereotype注解.
@Staging
public class StagingPaymentProcessor implements PaymentProcessor {
...
}
最后我们在bean.xml中仅需如此配置
<beans
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
<alternatives>
<stereotype>org.mycompany.myapp.Staging</stereotype>
</alternatives>
</beans>
现在不管有多少Staging环境下使用的替换Bean,他们都将全部启动.
2.alternatives的一个非严重缺陷
当我们启用替代方案,这是否意味着默认的实现被禁用?不完全是!如果默认的实现有一个限定词,例如@LargeTransaction ,alternatives Bean没有,系统仍然会注入的默认实现.例如
@Inject @LargeTransaction PaymentProcessor paymentProcessor;
所以我们在系统中还没有完全取代该默认实现,一个alternatives Bean在所有注射点可以完全重写默认bean的唯一方法是alternatives Bean实现所有bean类型,所有限定符。比如上面PaymentProcessor 的alternatives Bean也加入了@LargeTransaction注解.
但是,如果默认的Bean申明了一个producer方法或者一个观察者方法(cdi event 事件),那么即时是如上述所说的那样做,也还是不够的.我们需要进行额外的一些处理.CDI提供了一个特殊的功能,叫做specialization(专业化),可以帮助开发者避免这些陷阱。specialization是通知系统的方式来完全替代和禁用一个bean的实现。
3.Using specialization
针对第二节的问题,使用@Specializes来处理,如下所示:
@Alternative @Specializes
public class MockCreditCardPaymentProcessor
extends CreditCardPaymentProcessor {
...
}
当启用了这个Alternative Bean,即时其他的Bean,包括默认的Bean,申明了Producer方法和观察者方法,依然使用这个Alternative Bean.
这个时候,我们通知容器,我们必须使用这个Alternative Bean,这个Alternative Bean也会自动继承默认实现的所有qualifiers.
比如默认Bean有 @Default @CreditCard,那么例子的MockCreditCardPaymentProcessor也会继承这2个限定符.
比如默认Bean是有@Named,那么MockCreditCardPaymentProcessor会使用默认的EL Name.相当于继承了@Named!