Зачем использовать конструктор для инъекций setter в CDI?

Я не мог найти здесь разумного ответа, поэтому надеюсь, что это не дубликат. Итак, почему я должен предпочитать установку или конструкторную инъекцию над простыми

@Inject
MyBean bean;

Я получаю использование инъекции конструктора, если вам нужно что-то сделать с введенным bean во время инициализации вашего класса, например

public void MyBean(@Inject OtherBean bean) {
    doSomeInit(bean);
    //I don't need to use @PostConstruct now
}

но все же, это почти то же самое, что и метод @PostConstruct, и я вообще не получаю инъекцию setter, разве это не реликвия после Spring и других каркасов DI?

Ответ 1

Встраивание конструктора и свойств дает вам возможность легко инициализировать объект даже в среде без CDI, например unit test.

В среде, отличной от CDI, вы все равно можете просто использовать объект, просто передав конструктор arg.

OtherBean b = ....;
new MyBean(b);

Если вы просто используете полевую инъекцию, вы должны использовать отражение для доступа к полю, если оно является частным, например.

Если вы используете инъекцию свойств, вы также можете написать код в установщике. Таким образом, это зависит от ваших потребностей в реализации.

Сеттер против инъекции конструктора

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

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

Если у вас есть зависимость между существованием объекта и свойством, свойство должно быть либо аргументом конструктора. Это также сделает код более чистым, потому что, если вы используете параметр конструктора, вы документируете, что зависимость необходима.

Итак, вместо написания такого класса

public class CustomerDaoImpl implements CustomerDao {

  private DataSource dataSource;

  public Customer findById(String id){
     // Is the dataSource set?!
     Connection con = dataSource.getConnection();
     ...
     return customer;
  }

  public void setDataSource(DataSource dataSource){
     this.dataSource = dataSource;
  }

}

вам следует либо использовать конструкторскую инъекцию

public class CustomerDaoImpl implements CustomerDao {

  private DataSource dataSource;

  public CustomerDaoImpl(DataSource dataSource){
      if(dataSource == null){
        throw new IllegalArgumentException("Parameter dataSource must not be null");
     }
     this.dataSource = dataSource;
  }

  public Customer findById(String id) {    
      Customer customer = null;
     // We can be sure that the dataSource is not null
     Connection con = dataSource.getConnection();
     ...
     return customer;
  }
}

Мое заключение

  • Используйте свойства для каждой дополнительной зависимости.
  • Используйте конструктор args для каждой обязательной зависимости.

PS: Мой блог Разница между pojos и java beans объясняет мое заключение более подробно.

Ответ 2

При использовании CDI нет никакой причины использовать инсталляцию конструктора или сеттера. Как отмечено в вопросе, вы добавляете метод @PostConstruct для того, что в противном случае было бы сделано в конструкторе.

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

Наконец, инъекция конструктора позволяет полям быть final, но это не является недостатком @Inject -незапущенных полей (которые не могут быть final). Наличие аннотации в сочетании с отсутствием какого-либо кода, явно устанавливающего поле, должно четко указывать, что он должен быть установлен только контейнером (или инструментом тестирования). На практике никто не будет переназначать введенное поле.

Инъекторы конструктора и сеттера имели смысл в прошлом, когда разработчикам обычно приходилось вручную создавать экземпляры и вводить зависимости в тестируемый объект. В настоящее время технология развивается, и полевая инъекция - намного лучший вариант.