2010-11-29 5 views
4

У меня есть метод производителя CDI, который - в зависимости от некоторых условий, не относящихся к этому примеру - создает объекты различных типов:метод производителя Generic CDI не работает, как ожидалось

public class TestProducer { 

    @Produces @TestQualifier 
    public Object create(InjectionPoint ip) { 
    if(something) { 
     return "a String"; 
    } else { 
     return Integer.valueOf(42); 
    } 
    } 

, но при использовании этого производителя, я всегда получить ошибку в followin ситуации:

@Named("test") 
public class TestComponent { 
    ... 
    @Inject public void setA(@TestQualifier String stringValue) { 
    ... 
    @Inject public void setB(@TestQualifier Integer integerValue) { 

Он работает только тогда, когда метод создания производителя имеет ожидаемый тип в сигнатуре метода:

public class TestProducer { 

    @Produces @SpringBean 
    public String create(InjectionPoint ip) { 

Теперь строка string вводится правильно, но у меня нет способа генерировать целое число из метода-производителя. Но это именно то, чего я хочу избежать, поскольку сам производитель должен быть полностью общим.

Я делаю что-то неправильно или нет способа добиться желаемого поведения?

+0

Какая ошибка? – Bozho 2010-11-29 12:13:29

+0

@Bozho: У меня нет кода здесь прямо сейчас, но это что-то вроде «невозможно разрешить соответствующий объект». При использовании отладчика я также могу убедиться, что сам метод производителя вообще не вызван. – perdian 2010-11-29 14:58:54

ответ

4

Вся документация CDI дает понять, что CDI делает typesafe инъекции зависимостей - и это возвышенное свойство CDI. ИМХО, то, что вы пытаетесь сделать, это именно то, чего пытается избежать CDI. Вы хотите, чтобы контейнер выдавал Object для каждого типа, и CDI не работает таким образом.

Инъекции указывает stringValue и integerValue могут получить только боб, который имеет java.lang.String и java.lang.Integer в своем списке bean types соответственно. java.lang.Object не удовлетворяет этому критерию.

У меня есть два предложения. Во-первых, поскольку у вас есть два или более точек впрыска различных типов, создавать два или более производителей методов для этого типа:

public class TestProducer { 

    @Produces @TestQualifier 
    public String createString(InjectionPoint ip) { 
    if(something) { 
     return "a String"; 
    } else { 
     // Some other value 
    } 
    } 

    @Produces @TestQualifier 
    public int createInt(InjectionPoint ip) { 
    if(something) { 
     return 42; 
    } else { 
     // Some other value 
    } 
    } 
// ... 

Это работает, если условие something просто проверить тип точки впрыска (то, что я ставка делает ставку).

Однако, если условие something определяет тип, используя другие критерии, отличные от типа точки впрыска, я предлагаю выполнить «грязную работу» самостоятельно: введите возвращаемое значение в Object, делает бросок вручную:

@Named("test") 
public class TestComponent { 
    ... 
    @Inject public void setA(@TestQualifier Object value) { 
     String stringValue = (String) value; 

    ... 
    @Inject public void setB(@TestQualifier Object value) { 
     int intValue = (Integer) value; 

Главное в том, что, в отличие от некоторых других структур DI, CDI не работает против системы типа Java - наоборот, она в значительной степени использует его. Не пытайтесь бороться с ним, но использовать этот аспект КДИ в вашу пользу :)

4

Производитель для Object странно в любом случае. Я не уверен, если это запрещено спецификацией, или это ошибка, но я думаю, что вы можете сделать некоторые умные обходной путь:

public class ValueHolder<T> { 
    private T value; 

    public T getValue() { 
     return value; 
    } 
} 

А затем впрыснуть и ValueHolder<Integer>

+0

Это на самом деле следующий шаг к вашему предложению от http://stackoverflow.com/questions/4144039/injecting-a-spring-bean-using-cdi-inject. Я хотел бы добавить компоненты из Spring ApplicationContext и потребовать очень общий поиск. – perdian 2010-11-29 15:05:05

1

Ваши методы инициализатора будет найдите управляемый компонент с типами API String и Integer, но ваш компонент компонента производителя имеет только тип API (в случае метода производителя, тип возвращаемого значения) Object.

Таким образом, вы можете использовать Object только в своих инициализационных методах, а затем различать типы внутри тела получателя или просто обернуть их и метод производителя в реальном типе, который может возвращать строки или Int (но я 'd избегать генериков)

2

Его можно создавать общие объекты с CDI производит так:

// the wrapper class 
    public class Wrapper<T> { 
     public final T bean; 
     public Wrapper(T bean){ 
     this.bean = bean; 
     } 
    } 

    // the producer inside some class 
    @Produces 
    public <T> Wrapper<T> create(InjectionPoint p){ 
     // with parameter 'p', it is possible retrieve the class type of <T>, at runtime 
    } 


    // the bean example 1 
    public class BeanA { 
     public void doFoo(){ 
     // ... 
     } 
    } 
    // the bean example 2 
    public class BeanB { 
     public void doBar(){ 
     // ... 
     } 
    } 


    // the class that uses the produced beans 
    public class SomeBean{ 

//// There on producer method, do you can retrieve the Class object of BeanA and BeanB, from type parameters of Wrapper. 

     @Inject 
     private Wrapper<BeanA> containerA; 
     @Inject 
     private Wrapper<BeanB> containerB; 

     public void doSomeThing(){ 
     containerA.doFoo(); 
     containerB.doBar(); 
     } 

    } 

Работы на шве 2.2.0. Я думаю, что это работает и в некоторых предыдущих версиях.