Заменить фабричное создание объектов механизмом CDI - PullRequest
2 голосов
/ 08 июля 2011

Я хотел ввести CDI (Weld) в наш проект, и теперь у меня возникли некоторые проблемы с объектами, которые создаются вручную.

Итак, у нас есть несколько классов, реализующих интерфейс IReport, которые имеют поле, которое должнобыть введеннымЭто нулевое значение во время выполнения, потому что все эти классы генерируются ReportFactory в классе ReportController.

private Map<String,Object> generateReport(ReportInfo ri, ...) {
// some input validation
    IReport report = ReportControllerFactory.getReportInstance( ri.getClassName() );
// ...
}

. Я знаю, что могу использовать аннотацию @Produces вместе с другим пользовательскиманнотации в ReportControllerFactory, но как использовать @Inject для переменной, которая может быть создана только после некоторой проверки, внутри метода?И как бы я передал параметр ri.getClassName()?Объект ri неизвестен, когда строится * 1016. *.

Большое спасибо!

С уважением, Себастьян

Редактировать в 08 июля 2011 г. (10:00):

Класс ReportFactory:

public static IReport getReportInstance( String className ) throws ReportException {

    IReport report = null;

    try {
        Class<?> clazz = Class.forName( className );
        report = (IReport) clazz.newInstance();
    }
    catch ( Exception e ) { … }        

    return report;
}

Редактировать 2 (Выбор правильной реализации отчета)

Экземпляр отчета выбраннекоторыми путями, идущими от внешнего интерфейса JSF к контроллеру отчетов.ManagedBean вызывает сессионный компонент, который имеет несколько методов, в зависимости от того, какая кнопка была нажата и где.Все эти методы устанавливают имя отчета и вызывают более общий метод sendOrGetReport.Этот метод выбирает уникальный ключ для указанного отчета из базы данных и решает, отправить электронное письмо или немедленно доставить отчет.Давайте предположим, что это должно быть доставлено.

Тогда ReportController вступает в игру.Он выбирает объект ReportInfo на основе уникального ключа и другой информации, предоставленной описанными выше методами, и вызывает ReportFactory, чтобы создать отчет типа ri.getClassName().

Необычно, а?Я думаю, что весь этот раздел может нуждаться в рефакторинге.Если вы не видите легкого места, я пропускаю @Inject в реализации отчета и выполняю поиск JDNI для этого ресурса.

Ответы [ 3 ]

12 голосов
/ 13 сентября 2011

Чтобы динамически создать правильный класс отчета, небольшое изменение в принятом ответе решит проблему. Решение заключается в том, что Instance <...> предоставляет вам список всех bean-компонентов определенного типа. А аннотация производит не нужно.

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

public class ReportFactory {

@Inject Instance<IReport> availableReports;

public IReport createReport(String type) {

   for (IReport report: availableReports) {
      if (report.getType().equals(type)) { //or whatever test you need
         return report;
      }
   }
   return null;
}

Теперь класс, которому нужен динамически выбранный отчет, может использовать эту фабрику.

public class ReportCreator {

    @Inject
    private ReportFactory reportFactory;

    public void createReport(String type) {
        IReport report = reportFactory.createReport(type);
        report.execute();
    }
 }
8 голосов
/ 08 июля 2011

Идея, лежащая в основе CDI (и других DI-платформ) для управления зависимостями, заключается в том, чтобы взять под контроль жизненный цикл управляемых bean-компонентов. Это подразумевает, среди прочего, что вы не можете вмешиваться в создание управляемых bean-компонентов, если вы хотите, чтобы ими управлял контейнер.

Конечно, это не значит, что ваш сценарий неразрешим, вам просто нужно немного изменить свою точку зрения; -)

Идея состоит в том, чтобы работать с управляемыми bean-компонентами (очевидно), но пусть ваша собственная логика решит, какой из всех доступных экземпляров является правильным.

...
@Inject Instance<IReport> availableReports;
...
@Produces
public IReport createReport() {
   IReport result;
   for (IReport report: availableReports) {
      // choose correct instance, you might want to query the injection
      // point or any attached qualifier a bit more in order to 
      // determine which is the correct instance
      if ...
         result = report;
      ...
   }
   return result;
}
...

С таким количеством бобов beantype IReport, которое вам нравится

public class AReport implements IReport {
...
@Inject
...
}

public class BReport implements IReport {
...
@Inject
...
}

И автоматическое использование, подобное этому

public class MyStuff {
...
@Inject
IReport myReport;
...
}

См. здесь и здесь для получения дополнительной информации.

Если я не понял вашу проблему неправильно, это должно помочь вам двигаться вперед - не стесняйтесь размещать дополнительные вопросы / комментарии.

UPDATE

Все может быть просто невероятно просто, если что-то вроде этого соответствует вашим требованиям:

@AReport
public class AReport implements IReport {
...
@Inject
...
}

@BReport
public class BReport implements IReport {
...
@Inject
...
}

с использованием как это

public class MyStuff {
...
@Inject
@AReport
IReport myAReport;
...
@Inject
@BReport
IReport myBReport;
...
}
1 голос
/ 08 июля 2011

Хорошо, так что, если я не слишком ошибаюсь, основываясь на документации (это правильная структура? http://docs.jboss.org/seam/3/latest/reference/en-US/html/solder-beanmanagerprovider.html), ваша фабрика должна получить дескриптор синглтона BeanManager (либо ввести его инъекцией)или вызовите какой-нибудь метод доступа из инфраструктуры) и выполните что-то вроде

Class<?> clazz = Class.forName( className );
report = beanManager.getBean(clazz);

Предполагая, что ваш CDI настроен для обработки каждого из возможных имен классов, вы должны получить правильный компонент.Теперь это всегда может быть один и тот же экземпляр;Я не знаю, если это то, что вам нужно, извините.

Извините, если я ошибаюсь;надеясь, что это поможет.

...