2015-11-07 5 views
4

В платформе Java EE с CDI есть возможность вводить экземпляры классов POJO. Очень простым способом нам нужно использовать аннотацию @Inject, чтобы ввести экземпляр по умолчанию для какого-либо интерфейса. Мы также можем использовать квалификаторы для ввода конкретного класса в наше поле. Но эти решения довольно статичны.Динамические инъекции экземпляров через CDI

Мне нужна еще одна динамическая модель инъекционного материала.

Позвольте мне представить мою проблему: Предположим, что у нас есть интерфейс Animal и три класса, которые реализуют этот интерфейс: Ant, Dog, Elephant. Я хотел бы динамически вставлять экземпляр одного из этих трех классов, и это зависит от некоторой переменной, такой как строка (имя животного). В Java SE Я хотел бы сделать это, как показано ниже:

Map<String, Animal> animalMap = new HashMap<>(); 
animalMap.put("ant", new Ant()); 
animalMap.put("dog", new Dog()); 
animalMap.put("elephant", new Elephant()); 
... 
String animalName = ...; 
Animal animal = animalMap.get(animalMap); 
animal.doSomething(); 

Так что мне нужно что-то вроде:

class AnimalManager { 
    @Inject // ? 
    private Animal animal; // ? 

    public void run(String animalName) { 
     // based on animalName get new instance of animal and run doSomething() 
     ... 
     animal.doSomething(); // if animalName is "ant" call the doSomething on Ant class 
    } 
} 

Во всех классов, которые реализуют интерфейс Animal Мне нужно использовать переменные с @EJB аннотациями.

Какой лучший и самый правильный способ сделать это в Java EE?

EDIT:
OK на основе ответа от Svetlin Зарев и hwellmann Я создал это (спасибо!):

В начале мы создадим модель животных:

@Qualifier 
@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.FIELD, ElementType.TYPE}) 
public @interface AnimalModel { 

    Type value(); 

    enum Type { ANT, DOG, ELEPHANT } 

} 

Давайте создадим животное :

public interface Animal { 
    public void eat(Object food); 
} 

Следующая, конкретные классы:

@AnimalModel(AnimalModel.Type.ANT) 
public class Ant implements Animal { 

    @Override 
    public void eat(Object food) { 
     ... 
    } 

} 

@AnimalModel(AnimalModel.Type.DOG) 
public class Dog implements Animal { 

    @Override 
    public void eat(Object food) { 
     ... 
    } 

} 

@AnimalModel(AnimalModel.Type.ELEPHANT) 
public class Elephant implements Animal { 

    @Override 
    public void eat(Object food) { 
     ... 
    } 

} 

Далее AnimalLiteral:

public class AnimalLiteral extends AnnotationLiteral<AnimalModel> implements AnimalModel { 

    private static final long serialVersionUID = 1L; 
    private Type type; 
    public AnimalLiteral(Type type) { 
     this.type = type; 
    } 

    public Type value() { 
     return type; 
    } 
} 

Основным компонентом является животное завода:

@Dependent 
public class AnimalFactory { 

    @Inject 
    @Any 
    private Instance<Animal> animals; 

    private static Map<String, AnimalModel.Type> animalMap; 

    public AnimalFactory() { 
     animalMap = new HashMap<>(); 
     animalMap.put("ant", AnimalModel.Type.ANT); 
     animalMap.put("dog", AnimalModel.Type.DOG); 
     animalMap.put("elephant", AnimalModel.Type.ELEPHANT); 
    } 

    public Animal getAnimal(String animalName) { 
     AnimalModel.Type type = animalMap.get(animalName); 
     AnimalLiteral literal = new AnimalLiteral(type); 
     Instance<Animal> animalInstance = animals.select(literal); 
     return animalInstance.get(); 
    } 
} 

И клиент:

public class Client { 

    @Inject 
    private AnimalFactory animalFactory; 

    public void run(String animalName) { 
     Animal animal = animalFactory.getAnimal(animalName); 
     animal.eat("some food..."); 
    } 

} 

Я не знаю, что положить карту animalMap в этом месте правильно ...?

+0

Использовать инъекцию конструктора вместо инъекции в поле. – chrylis

ответ

1

Я не думаю, что такой динамический впрыск с CDI можно достичь просто потому, что зависимости вводятся, когда контейнер создает экземпляр управляемого компонента. Другими словами, когда вы назовете run() на ваш AnimalManager, то зависимость Animal была бы уже введена.

Что вы можете сделать, это ввести AnimalFactory и на каждом звонке просто позвольте сказать AnimalFactory.createAnimal(animal); или использовать что-то вроде вашего подхода к карте.

+0

Как это не просто лишить моего ответа и повторить его? – BadZen

+0

Вы говорите об альтернативах HK2, CDI, SOAP, Spring, дизайне и других мамбо-джамбо, но вы не объяснили, почему простой CDI не работает и не упоминает конкретного механизма достижения желаемой цели. Просто сравните мой короткий, осознанный и ясный ответ, который состоит из 1 (одного) предложения с вашим «романом». –

+0

Это неверно. CDI просматривает экземпляр, когда вы пытаетесь его использовать. Когда вы просто вызываете «AnimalManager # run», не может существовать никаких экземпляров «Animal». Это когда вы вызываете 'animal.doSomething' - вот когда экземпляр« Animal »просматривается или создается. «AnimalManager # animal» не является прямой ссылкой, это контекстный прокси. –

5

Использование Instance<T> в сочетании с квалификаторами является стандартным способом в CDI для выполнения динамической инъекции.

Вам нужен квалификатор с аргументом привязки, например.@Species("ant"), чтобы отличить классы реализации.

public class AnimalSelector { 

    @Inject 
    @Any 
    private Instance<Animal> animals; 

    public Animal selectAnimalBySpecies(String speciesName) { 
     SpeciesLiteral qualifier = new SpeciesLiteral(speciesName); 
     return animals.select(qualifier).get(); 
    } 
} 

public class SpeciesLiteral extends AnnotationLiteral<Species> implements Species { 

    private String name; 

    public SpeciesLiteral(String name) { 
     this.name = name; 
    } 

    @Override 
    public String value() { 
     return name; 
    } 
} 

@Qualifier 
@Target({ TYPE, METHOD, PARAMETER, FIELD }) 
@Retention(RUNTIME) 
@Documented 
public @interface Species { 

    String value() default ""; 
} 
Смежные вопросы