CDI(Weld)基礎<3>依賴關係注入(DI)和編程查找

一.Inject點(三種方式)

1. 構造器依賴注入

public class SomeBean {
  
  private final Service service;

  @Inject
  public SomeBean(Service service){
    this.service = service;
  }
}

說明:每一個Bean只能有一個@Inject方式的構造方法. java

2.字段注入(推薦)

public class SomeBean {
  
  @Inject
  private Service service;

}
這種推薦定義爲private.在這種狀況下,當容器初始化一個SomeBean類型的bean將注入正確的Service bean,不須要任何setter方法。

3.初始化方法參數注入

public class SomeBean {
  
  private Service service;
  
  @Inject
  public void setService(Service service) {
    this.service = service;
  }
}
通常我不喜歡這麼用. bean能夠有多個初始化方法。

4.注入說明,順序說明

依賴關係注入發生在容器第一次實例化bean 實例時。順序以下: 編程

  1. 容器調用bean構造函數(默認的構造函數或一個附加@Inject的構造函數),以獲取 bean 的實例。
  2. 容器初始化 bean 的全部字段。
  3. 接下來,容器調用bean的全部初始化方法(調用順序是不可移植的,不要依賴它)。
  4. 最後是標註了@PostConstruct方法.(若是有的話)

構造器----字段----初始化方法---@PostConstruct方法 async

注意:
  • 須要注意的是,若是Bean的父類已經字段注入某個Bean.那以後這個Bean初始化方法參數注入方式會出問題.
  • 構造函數注入的一個主要優勢是,它容許將bean不可變的。

CDI還支持參數注入一些其餘方法調用的容器。例如,參數注入是支持生產方法: 函數

@Produces Checkout createCheckout(ShoppingCart cart) 
{
	return new Checkout(cart);
}
這種狀況,是不須要@inject的.

二:Qualifier

Qualifier註解的做用在第一章已經說過.
好比咱們有多個bean實現特定的bean類型,注入某個bean就必須應該使用限定符註釋注入.
下面是第一個PaymentProcessor的Qualifier this

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Synchronous {}
@Synchronous
public class SynchronousPaymentProcessor implements PaymentProcessor {
   public void process(Payment payment) { ... }
}
第二個:
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Asynchronous {}
@Asynchronous
public class AsynchronousPaymentProcessor implements PaymentProcessor {
   public void process(Payment payment) { ... }
}

1.構造器注入

@Inject
public Checkout(@Synchronous PaymentProcessor syncPaymentProcessor,
		@Asynchronous PaymentProcessor asyncPaymentProcessor) {

   this.syncPaymentProcessor = syncPaymentProcessor;
   this.asyncPaymentProcessor = asyncPaymentProcessor;
}

2.字段注入(推薦)

@Inject @Synchronous PaymentProcessor syncPaymentProcessor;
@Inject @Asynchronous PaymentProcessor asyncPaymentProcessor;

3.初始化方法注入

public void setPaymentProcessors(@Synchronous PaymentProcessor syncPaymentProcessor,
				    @Asynchronous PaymentProcessor asyncPaymentProcessor) {

   this.syncPaymentProcessor = syncPaymentProcessor;
   this.asyncPaymentProcessor = asyncPaymentProcessor;
}

三:內置的Qualifiers==>@Default @Any

1.@Default

若是沒有指定具體的qualifiers,那麼系統默認加一個@Defaule.

2.@Any用法

若是咱們有多個Bean實現了某個接口,咱們要注入全部的這個接口類型的託管Bean,以下面這種方式獲取.
public class SomeBean {
  
  @Inject
  public void listServiceImplementations(
      @Any Instance<Service> serviceList) {

    for(Service service : serviceList){
      System.out.println(service.getClass().getCanonicalName());
    }
  }
}

四:Multiple qualifiers

Inject點能夠指定多個限定符:
@Synchronous @Reliable
public class SynchronousReliablePaymentProcessor implements PaymentProcessor {
   public void process(Payment payment) { ... }
}
使用:
@Inject @Synchronous @Reliable PaymentProcessor syncPaymentProcessor;

五:Qualifiers的會員概念,用法

你會看到一種用法@Qualifier數符能夠有成員。
這種方法將避免在單個應用程序中編寫的@Qualifier數量太多形成爆炸. spa

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Payment{
    EnumMoneyType value();
}
//USD--美圓   CNY--人民幣
public enum EnumMoneyType{
    USD,CNY;
}
咱們在Qualifiers中加入了一個屬性,爲一個枚舉,上面這個就是枚舉.固然你可使用默認值.這裏我沒寫.下面就是使用這個Qualifier的2個示例:
@Payment(EnumMoneyType.USD)
public class PRacquet implements RacquetType{
   .....
}
@Payment(EnumMoneyType.CNY)
public class FRacquet implements RacquetType{
   .....
}
咱們也可讓容器忽視成員,用@Nonbinding限定符註釋類型的成員容器就是忽略。
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Payment{
    EnumMoneyType value();
    //下面的comment將不起做用,被容器忽略
    @Nonbinding String comment() default "";
}

四:經過編程獲取上下文實例

在某些狀況下,注入不是最方便的方式來獲取上下文引用.
例如, code

  1. bean類型或限定符在運行時動態地變化
  2. qualifiers和type不知足@Inject.
  3. 咱們想要遍歷全部某個type和qualifiers的Bean.

這種狀況下,應該這樣使用: 對象

@Inject Instance<PaymentProcessor> paymentProcessorSource;
實例的get() 方法建立的bean的上下文實例。這樣也會有個延遲加載的意思在裏面.
PaymentProcessor p = paymentProcessorSource.get();
固然, 按照咱們在本章上面說的,也能夠這樣:
@Inject @Asynchronous Instance<PaymentProcessor> paymentProcessorSource;
如今,返回的PaymentProcessor get()將是限定符爲@Asynchronous的實例。

@Any+ Instance select()

或者,咱們能夠動態地指定限定符。首先,咱們將@Any限定符添加到注入點. 接口

import javax.enterprise.inject.Instance;
...
@Inject @Any Instance<PaymentProcessor> paymentProcessorSource;

接下來,咱們須要得到咱們的限定符類型的一個實例。由於註釋是接口,咱們不能只寫新的Asynchronous().這也是很是乏味的從頭建立一個註釋類型的具體實現。相反,CDI讓咱們過AnnotationLiteral helper類得到一個限定符實例通。 ip

class AsynchronousQualifier extends AnnotationLiteral<Asynchronous> implements Asynchronous {}
在某些狀況下,咱們可使用一個匿名類:
PaymentProcessor p = paymentProcessorSource.select(new AnnotationLiteral<Asynchronous>() {});
然而,咱們不能用一個匿名類實現一個限定符類型和成員。
因此咱們能夠經過qualifier of Instance select()方法。
Annotation qualifier = synchronously ? new SynchronousQualifier() : new AsynchronousQualifier();
PaymentProcessor p = anyPaymentProcessor.select(qualifier).get().process(payment);

五:InjectionPoint對象用法

好比說咱們使用logger,是這樣寫

Logger log = Logger.getLogger(MyClass.class.getName());

但經過CDI InjectionPoint對象

import javax.enterprise.inject.spi.InjectionPoint;
import javax.enterprise.inject.Produces;

class LogFactory {

   @Produces 
   Logger createLogger(InjectionPoint injectionPoint) {
      return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
   }
}

在其餘的類中,能夠以下代碼這樣注入

@Inject Logger log;
容器提供了一個內置bean實現InjectionPoint接口:
public interface InjectionPoint {

   public Type getType();
   public Set<Annotation> getQualifiers();
   public Bean<?> getBean();
   public Member getMember();
   public Annotated getAnnotated();
   public boolean isDelegate();
   public boolean isTransient();
}
相關文章
相關標籤/搜索