Spring вводит репозиторий, смешанный с обычным свойством в конструкторе

У меня есть класс Service и интерфейс Repository (Spring Data). У меня также есть один абстрактный класс:

public abstract class TestingMethod {
    public TestingMethod() {
        timeSum = 0;
    }
    protected long timeSum;
}

И класс, который его расширяет:

@Component
public class LimitTestingMethod extends TestingMethod {

    @Autowired
    private GeoTestDataRepository geoTestDataRepository;

    private final int limitSize;

    public LimitTestingMethod(int limitSize) {
        super();
        this.limitSize = limitSize;
    }
}

В моем Service я хочу создать экземпляр LimitTestingMethod и установить его аргумент limitSize. Что-то вроде:

LimitTestingMethod ltm3 = new LimitTestingMethod(3);
LimitTestingMethod ltm10 = new LimitTestingMethod(10);

Но я получил ошибку:

Описание: Параметр 0 конструктора в Для com.exence.postgiscma.testingMethod.LimitTestingMethod требуется bean-компонент типа 'int', который не может быть найден. Действие: рассмотрите возможность определения bean типа «int» в вашей конфигурации.

Возможно ли добиться того, чего я хочу?

Всего найлучшего!

//РЕДАКТИРОВАТЬ

Как я вижу в комментариях, это плохой подход. Так что, может быть, кто-то даст мне совет, как это лучше спроецировать? Является ли это хорошим решением для передачи репо в качестве аргумента в конструкторе (я думаю, что нет, но я не могу понять, как это сделать лучше)?

LimitTestingMethod ltm3 = new LimitTestingMethod(3, geoTestDataRepository);

Есть ли хорошее и элегантное решение?

Основной концепцией Spring является инверсия управления, где контейнер отвечает за создание каждого компонента. Аннотируя класс как @Component, вы указываете Spring создать экземпляр вашего класса. Однако вы не предоставили каких-либо инструкций по значению, которое должно быть предоставлено для построения bean-компонента. Отсюда и ошибка, какое значение Spring вводить в конструктор.

andrew.b.10 23.12.2020 12:06

Почему вы хотите создать LimitTestingMethod с новым? Может быть, вы можете получить значение limitesSize из файла свойств?

burm87 23.12.2020 12:17
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
2
647
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поскольку вы создаете экземпляры за пределами Spring, ваше текущее решение не будет работать. Ошибка возникает из-за того, что вы аннотировали ее с помощью @Component, она обнаружит ее при запуске и попытается создать bean-компонент, но потерпит неудачу.

Чтобы решить эту проблему, вы можете сделать 1 из 2 вещей.

  1. Пусть Spring обрабатывает создание bean-компонентов, используя ApplicationContext в качестве фабрики, предоставляя дополнительные аргументы и делая прототип bean-компонента ограниченным.
  2. Позвольте Spring обработать инъекцию после того, как вы вручную создали экземпляр с помощью ApplicationContext.

Используйте ApplicationContext как фабрику

Сначала сделайте свой bean-компонент прототипом, чтобы он мог быть сконструирован при необходимости.

@Component
@Scope(
ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class LimitTestingMethod extends TestingMethod { ... }

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

public class Service {

  @Autowired
  private ApplicationContext ctx;


  public void yourMethod() {

    LimitTestingMethod ltm3 = ctx.getBean(LimitTestingMethod.class, 3);
    LimitTestingMethod ltm10 = ctx.getBean(LimitTestingMethod.class, 10);

  }
}

Это позволит Spring создать экземпляр, используя значение, переданное для конструктора, и выполнить автосвязывание.

Инъекция после создания

Другое решение состоит в том, чтобы вручную создать экземпляры, а затем позволить Spring обрабатывать автоматическую проводку. При этом вы потеряете способности АОП и получите только автосвязь.

Сначала удалите аннотацию @Component из LimitTestingMethod, чтобы она не была обнаружена во время запуска.

public class LimitTestingMethod extends TestingMethod { ... }

Теперь в вашем сервисе автоматически подключите ApplicationContext и после создания вашего bean-компонента используйте его для внедрения зависимостей.

public class Service {

  @Autowired
  private ApplicationContext ctx;


  public void yourMethod() {

    LimitTestingMethod ltm3 = new LimitTestingMethod(3);
    LimitTestingMethod ltm10 = new LimitTestingMethod(10);

    ctx.getAutowireCapableBeanFactory().autowireBean(lmt3);
    ctx.getAutowireCapableBeanFactory().autowireBean(lmt10);

  }
}

Оба добьются того, чего вы хотите, однако теперь ваш код напрямую зависит от Spring API. Поэтому вместо этого вам, вероятно, лучше выбрать другой вариант, а именно внедрить все для LimitTestingMethod через конструктор и передать репозиторий самостоятельно.

Используйте конструктор для создания экземпляра

public class LimitTestingMethod extends TestingMethod {

    private final GeoTestDataRepository geoTestDataRepository;
    private final int limitSize;

    public LimitTestingMethod(int limitSize, GeoTestDataRepository geoTestDataRepository) {
      this.limitSize=limitSize;
      this.geoTestDataRepository=geoTestDataRepository;
    }
}

Затем вы можете просто автоматически подключить репозиторий в своем классе обслуживания и создать экземпляры по мере необходимости (или создать фабрику, которая содержит сложность создания этого объекта).

public class Service {

  @Autowired
  private GeoTestDataRepository repo;


  public void yourMethod() {

    LimitTestingMethod ltm3 = new LimitTestingMethod(3, repo);
    LimitTestingMethod ltm10 = new LimitTestingMethod(10, repo);

  }
}

Спасибо за вашу помощь! Я считаю, что вариант 3 не будет элегантным и лучшим решением. Я также думаю, что не должно быть аннотации @Autowired, но это ошибка, которая, вероятно, произошла при копировании кода.

Wojtek 23.12.2020 13:29

Почему не должно быть autowired? Как еще вы собираетесь заводить репозиторий или ApplicationContext в свой сервис (хотя для последнего можно было бы реализовать ApplicationContextAware, если бы очень хотелось).

M. Deinum 23.12.2020 13:40

Я имею в виду аннотацию @Autowired в методе LimitTestingMethod в последнем примере. Репозиторий передается туда через конструктор.

Wojtek 24.12.2020 22:40

Другие вопросы по теме