CDI(Weld)基础<3>依赖关系注入DI和编程查找

一.Inject点(三种方式)

1.构造器依赖注入

public class SomeBean {
  
  private final Service service;

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

说明:每个Bean只能有一个@Inject方式的构造方法.

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方法

注意:
  • 需要注意的是,如果Bean的父类已经字段注入某个Bean.那之后这个Bean初始化方法参数注入方式会出问题.
  • 构造函数注入的一个主要优点是,它允许将bean不可变的。

CDI还支持参数注入一些其他方法调用的容器。例如,参数注入是支持生产方法:

@Produces Checkout createCheckout(ShoppingCart cart) 
{
	return new Checkout(cart);
}
这种情况,是不需要@inject的.

二:Qualifier

Qualifier注解的作用在第一章已经说过.
比如我们有多个bean实现特定的bean类型,注入某个bean就必须应该使用限定符注释注入.
下面是第一个PaymentProcessor的Qualifier

@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,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数量太多造成爆炸.

@Qualifier
@Retention(RUNTIME)
@Target({METHOD,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,TYPE})
public @interface Payment{
    EnumMoneyType value();
    //下面的comment将不起作用,被容器忽略
    @Nonbinding String comment() default "";
}

四:通过编程获取上下文实例

在某些情况下,注入不是最方便的方式来获取上下文引用.
例如,

  1. bean类型或限定符在运行时动态地变化
  2. qualifiers和type不满足@Inject.
  3. 我们想要遍历所有某个type和qualifiers的Bean.

这种情况下,应该这样使用:

@InjectInstance<PaymentProcessor>paymentProcessorSource;
实例的get() 方法创建的bean的上下文实例。这样也会有个延迟加载的意思在里面.
PaymentProcessorp=paymentProcessorSource.get();
当然,按照我们在本章上面说的,也可以这样:
@Inject@AsynchronousInstance<PaymentProcessor>paymentProcessorSource;
现在,返回的PaymentProcessor get()将是限定符为@Asynchronous的实例。

@Any+Instance select()

或者,我们可以动态地指定限定符。首先,我们将@Any限定符添加到注入点.

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

接下来,我们需要获得我们的限定符类型的一个实例。因为注释是接口,我们不能只写新的Asynchronous().这也是非常乏味的从头创建一个注释类型的具体实现。相反,CDI让我们过AnnotationLiteral helper类获得一个限定符实例通。

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());
   }
}

在其他的类中,可以如下代码这样注入

@InjectLoggerlog;
容器提供了一个内置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();
}

相关文章

什么是设计模式一套被反复使用、多数人知晓的、经过分类编目...
单一职责原则定义(Single Responsibility Principle,SRP)...
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强...
适配器模式将一个类的接口转换成客户期望的另一个接口,使得...
策略模式定义了一系列算法族,并封装在类中,它们之间可以互...
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,...