使用@Alternative來選擇一個替代者
你可能還記得,我們在前面定義了幾個可作為替代選擇的傳輸器,分別是JsonRestAtmTransport和SoapRestAtmTransport。想象一下如果你是ATM的安裝者,那么需要配置傳輸器和地點。我們之前定義的注入點只是使用默認傳輸器StandardRestAtmTransport。
如果還需要不同的傳輸器,就需要更改/META-INF/beans.xml文件來正確的選擇傳輸器,如下:
<!-- {classpath}/META-INF/beans.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 http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<alternatives>
<class>org.cdi.advocacy.JsonRestAtmTransport</class>
</alternatives>
</beans>
你在輸出中將會看到選擇使用了JSON REST傳輸器。 Output
deposit called
communicating with bank via JSON REST transport
替代選擇在DI中是非常普遍的場景應用,也就是說,你有不同的注入對象依賴于不同的構建環(huán)境。很棒的是對象可以被替換。替代選擇特性允許你標記對象可以被其它對象替換。
如果DI容器有選擇替代功能,可以讓你標記對象能夠被替代。請考慮使用這種方式。因為我們不需要非得在文檔中說明替代選擇,他自身就是一種文檔,如果某些人知道CDI并且知道替代選擇,那么他們在看到它時不會感到陌生。替代選擇是讓你替代對象的標準方式。
你可以考慮使用CDI作為很多模式的標準,我們已經在很多純DI框架中使用過。簡單化和標準化是DI的發(fā)展方向的一部分。 2. 使用@Qualifier注入不同的類型 在CDI中所有的對象和生產者都是限定類型的。如果你沒有分配限定類型那么將會使用默認的@Default和@Any。就像一個罪犯在美國,如果沒有足夠的錢給律師,那么他將會被分配一個。 限定類型用來識別正確的對象被注入,你可以寫自己定制的限定類型。 限定類型能夠匹配注入目標和注入源,確保正確的類型被注入。 你可以決定在什么時候注入Soap、Json和Standard傳輸器。你不想把他們列出來選擇,實際上,你想在某些時候使用Json實現.
、、 Soap運行時限定類型注解
package org.cdi.advocacy;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import javax.inject.Qualifier;
@Qualifier @Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Soap { }
注意一個限定類型就是一個運行時注解,其標注了@Qualifier注解。@Qualifier注解把一個運行時注解聲明為限定類型。
下面我在SoapAtmTransport使用一個新的限定類型@Soap:
// SoapAtmTransport使用新的@Soap限定類型注解
package org.cdi.advocacy;
@Soap public class SoapAtmTransport implements ATMTransport {
@Override
public void communicateWithBank(byte[] datapacket) {
System.out.println("communicating with bank via Soap transport");
}
}
現在你已經準備好注入一個Soap傳輸器,你可以在構造器參數上標注限定類型注解,如下: [source,java]
public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
private ATMTransport transport;
@Inject public AutomatedTellerMachineImpl(@Soap ATMTransport transport) {
this.transport = transport;
}
你也可以使用setter方法參數來標注限定類型,如下: [source,java]
public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
private ATMTransport transport;
@Inject public void setTransport(@Soap ATMTransport transport) {
this.transport = transport;
}
最通用的方法就是使用變量域層注入,如下: [source,java]
public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
@Inject @Soap private ATMTransport transport; ---
到這里,我們下面將使用變量域層注入來簡化我們的例子。
使用@Qualfiers在同一個bean中注入多個類型
我們假設ATM機使用不同的傳輸器是基于一些不同的商業(yè)規(guī)則,比如是LDAP或者XML或者數據庫。
在這個例子中我們希望注入三個不同的傳輸器并且基于商業(yè)規(guī)則配置傳輸器。
你需要通知在注入完成時bean已經被CDI準備好了。要獲得這個通知你需要在init方法上標注@PostConstruct注解,這樣你就能區(qū)分使用哪種類型的傳輸器了。
//AutomatedTellerMachineImpl使用新的多個限定類型注解注入多個傳輸器
package org.cdi.advocacy;
import java.math.BigDecimal;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.inject.Named;
@Named("atm")
public class AutomatedTellerMachineImpl implements AutomatedTellerMachine {
private ATMTransport transport;
@Inject @Soap
private ATMTransport soapTransport;
@Inject @Json
private ATMTransport jsonTransport;
@Inject @Json
private ATMTransport standardTransport;
//These could be looked up in a DB, JNDI or a properties file.
private boolean useJSON = true;
private boolean behindFireWall = true;
@PostConstruct
protected void init() {
//Look up values for useJSON and behindFireWall
if (!behindFireWall) {
transport = standardTransport;
} else {
if (useJSON) {
transport = jsonTransport;
} else {
transport = soapTransport;
}
}
}
public void deposit(BigDecimal bd) {
System.out.println("deposit called");
transport.communicateWithBank(null);
}
...
}
嘗試執(zhí)行上面的例子,將會得到下面的輸出。 Output
deposit called communicating with bank via JSON REST transport