CDI是否可以注入通用接口实现?

时间:2016-03-03 00:48:43

标签: java-ee cdi

我正在尝试创建类似于以下内容的类型转换器:

public interface ConvertToString<T> {
    String convert (T value);
}

public class ConvertLongToString implements ConvertToString<Long> {
    @Override
    public String convert (Long value) {
        return value.toString();
    }
}

public class Foo<T> {
    @Inject
    @Any
    private Instance<ConvertToString<T>> instance;

    private T value;

    public String getValue() {
        return instance.get().convert(value);
    }

    public void setValue(T value) {
        this.value = value;
    }
}

在此示例中,我希望Foo<Long>的实例知道如何通过注入正确的转换器来转换值。

这是否可以像我所展示的那样使用CDI,甚至可以通过迭代实例列表来实现?

我想要构建类似于Bean Validation框架的东西,你可以为许多类型创建相同注释的验证器(当然你有@ValidatedBy ...)

3 个答案:

答案 0 :(得分:2)

泛型只是编译时,但据说可以解析注入点并在运行时具有类似的行为。例如:

@Inject
@ConvertToString
ConvertToStringInterface<T> converter;

@Produces
@ConvertToString
public ConvertToStringInterface produceConverter(InjectionPoint injectionPoint) {
    Type type = injectionPoint.getType();
    ParameterizedType parameterizedType = (ParameterizedType) type;
    Type argType = parameterizedType.getActualTypeArguments()[0];

    Class<?> clazz = (Class<?>) argType;

    if (clazz == Long.class) {
        return new ConvertLongToString();
    }
}

这可能需要一些调整,但它应该有效。

答案 1 :(得分:1)

正如我上面的评论所述,我不相信认为正确的答案是正确的,即使它使用高级CDI apis来回答你。看起来正确,但实际上,我认为它不可能纠正。

另一方面,标记为减号的答案是正确的,但过于抽象而无法提供帮助。

他的制片人有一个问题。当CDI创建bean时,无法知道conrete实例返回注入点。 WELD实施者在编译时缺乏相同的信息来解决您的错误问题。 因为注入点没有指定编译类型所需的具体参数化类型。 而instance.get()api也没有任何帮助,因为它没有传递任何源类型。做完了。

所以答案看起来技术上很好,但实际上它不起作用。

我知道你可以通过多种方式解决问题。 我现在至少可以想到两个,我相信还有其他人。

你可以创建一个愚蠢的Facade转换器,它唯一知道如何为输入类型找出合适的转换器。 类似的东西:

pulblic class MyClassThatNeedsAConverter{

@Inject
MyAllMightyFacadeConverterThatKnowsAllConcreteConverters converter;

public void someMethodThatNeedsToConverSomethin(T someTypeThatCanBeConverted){
// now i need to convert this to string
String convertedString = converter.convert(someTypeThatCanBeConverted);
// contiue with my business logic
}
}

那就是程序员API。 然而,您的框架将实现所有强大的转换器外观。

public class MyAllMightyFacadeConverterThatKnowsAllConcreteConverters {

@Inject
ConvertToStringInterface<String> stringToStringConverter;
@Inject
ConvertToStringInterface<Long> longToStringConverter;
// and on you go with different concreted converters
// CDI can resolve these just fine, just make sure you do not have more than
// one candidate bean
...

public <T> String convert(T whateverTypeThatComesISwallow){
if(whateverTypeThatComesISwallow instaceof Long){
return longToStringConverter.convert((Long) whateverTypeThatComesISwallow ); 
} 

if(whateverTypeThatComesISwallow  instaceof SomeOtherType){
// same old story
}

throw new RuntimeException("You are out of luck, this type i do not know off an will not convert"  + whateverTypeThatComesISwallow.getClass().getCanonincalName() );
}
}

这是一条路线。

另一种方法是你可以拥有类似转换器缓存的东西。 所以你的框架可以提供像

这样的东西
@ApplicationScoped
public class MyConverterHolder{
@Inject
Instance<StringConverter<?>> allConverterInstancesKnownToTheSystem;

final Map<Class, StringConveter<?>> myCacheOfConveters = new HashMap<>();

@postConstruct
public postConstruct(){
 // you do not want to this initialization all the time
// so you just do it once during construction
    for(StringConveter<?> currentConverterInstance :     allConverterInstancesKnownToTheSystem) {
      myCacheOfConveters.put(currentConverterInstance .getSupportedInputType(), currentConverterInstance );
    }   
  } // post construct ends here

public <T>  String convert(T someTypedDataIcanSwallow){
Class<T> sourceType = someTypedDataIcanSwallow.getClass();
return myCacheOfConveters.get(sourceType).convert(someTypedDataIcanSwallow);
}

}

我倾向于更喜欢第二种方法。 当哈希映射可以为我完成工作而我不需要维护它时,我不喜欢用ifs和elses发送垃圾邮件代码。 但重点是。 除非你告诉它具体的类型,否则CDI不能给你一个具体的实现来解决你的问题。 如果您没有给出具体类型,CDI可以为您提供符合某些类型标准的所有实例。但是你必须解决挑选正确问题的问题。

因此,您需要在转换之间创建此Facade对象。

关于参数化tpye注射的良好读数当然是规范本身。 这描述了用于将bean解析为注入点的匹配规则,并在描述之后给出了示例。 例如。参见&#34; 5.2.4节。原始类型和参数化类型的可分配性&#34;。

 For example, Dao is eligible for injection to any injection point of
 type @Default Dao<Order>, @Default Dao<User>, @Default Dao<?>,
 @Default Dao<? extends Persistent> or @Default Dao<X extends
Persistent> where X is a type variable.

这一段也很重要:

  

Legal Bean类型中定义的任何合法bean类型都可以是   所需的注射点类型。此外,所需的类型   注入点可以包含通配符类型参数。但是,一个   type变量不是合法的注入点类型。

     

如果注入点类型是类型变量,则为容器   自动检测问题并将其视为定义错误。

亲切的问候。

答案 2 :(得分:0)

泛型是一个编译时构造,使其适用于运行时注入所需的信息不存在,类型擦除只是打败了整个事物....而且,我尝试使用Spring!