Есть ли способ @Autowire bean, который требует аргументов конструктора?

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

В XML я могу использовать как часть определения bean-компонента. Есть ли аналогичный механизм для аннотации @Autowire?

Ex:

@Component
public class MyConstructorClass{

  String var;
  public MyConstructorClass( String constrArg ){
    this.var = var;
  }
...
}


@Service
public class MyBeanService{
  @Autowired
  MyConstructorClass myConstructorClass;

  ....
}

Как в этом примере указать значение constrArg в MyBeanService с аннотацией @Autowire? Есть какой-либо способ сделать это?

Спасибо,

Эрик


person Eric B.    schedule 18.07.2011    source источник
comment
Может быть, я не совсем понял, или, возможно, я неправильно понял, но я не ищу autowire constrArg; Я ищу автонастройку MyConstructorClass, но для конструктора требуется String. Если бы я использовал конфигурацию XML, мой bean-компонент defn был бы таким:   -  person Eric B.    schedule 19.07.2011
comment
Если бы я использовал конфигурацию XML, мой bean-компонент defn выглядел бы примерно так: ‹bean id = myBeanService class = MyBeanService› ‹имя свойства = myConstructorClass› ‹bean class = MyConstructorClass› ‹значение аргумента конструктора = MyStringHere /› ‹/bean› ‹ / property ›‹/bean› Есть ли способ перевести это в аннотации?   -  person Eric B.    schedule 19.07.2011


Ответы (9)


Вам понадобится _ 1_ аннотация.

Типичным вариантом использования является присвоение значений полей по умолчанию с использованием выражений стиля "#{systemProperties.myProp}".

public class SimpleMovieLister {

  private MovieFinder movieFinder;
  private String defaultLocale;

  @Autowired
  public void configure(MovieFinder movieFinder, 
                        @Value("#{ systemProperties['user.region'] }") String defaultLocale) {
      this.movieFinder = movieFinder;
      this.defaultLocale = defaultLocale;
  }

  // ...
}

См. Язык выражений› Конфигурация аннотаций


Чтобы быть более ясным: в вашем сценарии вы должны подключить два класса, MybeanService и MyConstructorClass, примерно так:

@Component
public class MyBeanService implements BeanService{
    @Autowired
    public MybeanService(MyConstructorClass foo){
        // do something with foo
    }
}

@Component
public class MyConstructorClass{
    public MyConstructorClass(@Value("#{some expression here}") String value){
         // do something with value
    }
}

Обновление: если вам нужно несколько разных экземпляров MyConstructorClass с разными значениями, вы должны использовать аннотации квалификатора

person Sean Patrick Floyd    schedule 18.07.2011
comment
Спасибо за совет, но я не понимаю, как это связано с установкой аргумента конструктора для внедренного компонента. Из того, что я могу сказать из документации Spring, он хорош для установки значений по умолчанию, но не указывает, как передать конструктор arg. - person Eric B.; 19.07.2011
comment
@EricB. @Value похож на @Autowired для строковых аргументов. - person Bozho; 19.07.2011
comment
@Eric B (классное имя пользователя, кстати :-)), я добавлен пример кода для иллюстрации - person Sean Patrick Floyd; 19.07.2011
comment
Спасибо за совет. Уловка здесь заключается в том, что мне нужно иметь доступ для изменения кода MyConstructorClass. К сожалению, это не всегда так; У меня не обязательно есть возможность изменять код. Кроме того, в моем примере я использую String в качестве аргумента, но бывают случаи, когда конструктор требует других bean-компонентов в качестве аргументов. - person Eric B.; 19.07.2011
comment
@Eric ну, тогда вам нужен либо XML, либо Конфигурация на основе Java - person Sean Patrick Floyd; 19.07.2011
comment
Изменится ли этот ответ, если конструктор arg является настраиваемым java-объектом, т.е. не String? - person Kevin Meredith; 15.08.2014
comment
@KevinMeredith ну, это должен быть объект Spring, который может конвертировать, но тогда он работает. Однако, если ваше значение - Spring bean, вы должны использовать @Autowired, @Injectet al. - person Sean Patrick Floyd; 15.08.2014
comment
@SeanPatrickFloyd В вопросе экземпляр MyConstructorClass автоматически подключается к переменной myConstructorClass в MyBeanService. В вашем примере нет переменной myConstructorClass. Я смущен. Будет ли использоваться вместо него foo аргумент? - person Mikhail Batcer; 27.01.2016
comment
@MikhailBatcer говорит, что нужно что-то делать с foo. например: назначить поле foo :-) - person Sean Patrick Floyd; 27.01.2016
comment
@SeanPatrickFloyd Например, в поле private MyConstructorClass myConstructorClass;, верно? :-) - person Mikhail Batcer; 27.01.2016
comment
@SeanPatrickFloyd - я хочу передать значение - Value1 конструктору, как я могу это сделать, я пробовал ваш синтаксис, но он не компилируется, это правильно ..? Вы можете мне помочь? - person Ashish Shetkar; 24.02.2017
comment
@AshishShetkar Я думаю, что в ответе есть опечатка. Вместо @Value("#{ systemProperties['user.region'] }"} String defaultLocale это должно быть @Value("#{ systemProperties['user.region'] }") String defaultLocale. В моем проекте Spring-Boot 4 мне нужно было вместо этого использовать @Value("${user.region}") String defaultLocale. Хотя не знаю разницы. - person JackLeEmmerdeur; 11.07.2019

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

public interface MyBean {
    // here be my fancy stuff
}

public interface MyBeanFactory {
    public MyBean getMyBean(/* bean parameters */);
}

@Component
public class MyBeanFactoryImpl implements MyBeanFactory {
    @Autowired
    WhateverIWantToInject somethingInjected;

    public MyBean getMyBean(/* params */) {
        return new MyBeanImpl(/* params */);
    }

    private class MyBeanImpl implements MyBean {
        public MyBeanImpl(/* params */) {
            // let's do whatever one has to
        }
    }
}

@Component
public class MyConsumerClass {
    @Autowired
    private MyBeanFactory beanFactory;

    public void myMethod() {
        // here one has to prepare the parameters
        MyBean bean = beanFactory.getMyBean(/* params */);
    }
}

Итак, MyBean не является компонентом Spring как таковым, но он достаточно близок. Внедрение зависимостей работает, хотя я внедряю фабрику, а не сам компонент - нужно внедрить новую фабрику поверх своей собственной новой реализации MyBean, если кто-то хочет ее заменить.

Кроме того, MyBean имеет доступ к другим bean-компонентам - потому что он может иметь доступ к автоматически подключенным устройствам фабрики.

И, по-видимому, кто-то может захотеть добавить некоторую логику к функции getMyBean, что является дополнительным усилием, которое я допускаю, но, к сожалению, у меня нет лучшего решения. Поскольку проблема обычно заключается в том, что динамические параметры поступают из внешнего источника, такого как база данных или взаимодействия с пользователем, поэтому я должен создать экземпляр этого bean-компонента только в середине выполнения, только когда эта информация легко доступна, поэтому Factory должно быть вполне адекватным .

person Ivan Ketler    schedule 03.12.2015

Как в этом примере указать значение constrArg в MyBeanService с аннотацией @Autowire? Есть какой-либо способ сделать это?

Нет, не так, как ты имеешь в виду. Компонент, представляющий MyConstructorClass, должен быть настраиваемым, не требуя каких-либо его клиентских компонентов, поэтому MyBeanService не имеет права голоса при настройке MyConstructorClass.

Это не проблема с автоматическим подключением, проблема здесь в том, как Spring создает экземпляр MyConstructorClass, учитывая, что MyConstructorClass является @Component (и вы используете сканирование компонентов и, следовательно, не указываете MyConstructorClass явно в вашей конфигурации).

Как сказал @Sean, один из ответов здесь - использовать @Value в параметре конструктора, чтобы Spring извлекал значение конструктора из системного свойства или файла свойств. Альтернативой является MyBeanService непосредственное создание экземпляра MyConstructorClass, но если вы это сделаете, то MyConstructorClass больше не будет bean-компонентом Spring.

person skaffman    schedule 19.07.2011
comment
Можно ли установить @Value в полностью квалифицированный компонент? И их через Autowiring найдут? - person Kevin Meredith; 15.08.2014

Вы также можете настроить свой компонент следующим образом:

package mypackage;
import org.springframework.context.annotation.Configuration;
   @Configuration
   public class MyConstructorClassConfig {


   @Bean
   public MyConstructorClass myConstructorClass(){
      return new myConstructorClass("foobar");
   }
  }
}

С помощью аннотации Bean вы говорите Spring зарегистрировать возвращенный bean-компонент в BeanFactory.

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

person Zakaria    schedule 22.01.2017
comment
Не могли бы вы рассказать, как я могу выполнить автоматическое подключение, например клиент, который я создал в MyConstructorClassConfig, как показано выше, например. У меня есть метод createClient с аннотацией Bean, как мне использовать этот метод createClient в классе обслуживания? - person shapan dashore; 22.04.2021

Вам нужно использовать @Autowired и @Value. Дополнительную информацию о Эта тема.

person Fahim Farook    schedule 22.11.2014
comment
Ответ там короткий. По сути, ответ вам «@Autowired» и «@Value». Я мог бы добавить это как комментарий, но это было давно, и я не уверен, что у меня были права комментировать ... - person Fahim Farook; 14.05.2016

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

@Component
public class MyConstructorClass{

  String var;

  public void setVar(String var){
     this.var = var;
  }

  public void getVar(){
    return var;
  }

  @PostConstruct
  public void init(){
     setVar("var");
  }
...
}


@Service
public class MyBeanService{
  //field autowiring
  @Autowired
  MyConstructorClass myConstructorClass;

  ....
}
person george    schedule 06.03.2016

Большинство ответов довольно старые, поэтому тогда это могло быть невозможно, но на самом деле есть решение, которое удовлетворяет всем возможным вариантам использования.

Итак, знайте, что ответы таковы:

  • Отсутствие реального компонента Spring (заводской дизайн)
  • или не подходит для каждой ситуации (при использовании @Value значение должно быть где-то в файле конфигурации)

Решение этих проблем - создать объект вручную с помощью ApplicationContext:

@Component
public class MyConstructorClass
{
    String var;

    public MyConstructorClass() {}
    public MyConstructorClass(String constrArg) {
        this.var = var;
    }
}

@Service
public class MyBeanService implements ApplicationContextAware
{
    private static ApplicationContext applicationContext;

    MyConstructorClass myConstructorClass;

    public MyBeanService()
    {
        // Creating the object manually
        MyConstructorClass myObject = new MyConstructorClass("hello world");
        // Initializing the object as a Spring component
        AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
        factory.autowireBean(myObject);
        factory.initializeBean(myObject, myObject.getClass().getSimpleName());
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        applicationContext = context;
    }
}

Это отличное решение, потому что:

  • Он дает вам доступ ко всем функциям Spring на вашем объекте (очевидно, @Autowired, но также @Async, например),
  • Вы можете использовать любой источник для аргументов конструктора (файл конфигурации, вычисленное значение, жестко запрограммированное значение, ...),
  • Вам нужно всего лишь добавить несколько строк кода, ничего не меняя.
  • Его также можно использовать для динамического создания неизвестного количества экземпляров класса, управляемого Spring (например, я использую его для создания нескольких асинхронных исполнителей на лету)

Единственное, что следует иметь в виду, это то, что у вас должен быть конструктор, который не принимает аргументов (и может быть пустым) в классе, который вы хотите создать (или конструктор @Autowired, если он вам нужен).

person AntoineB    schedule 31.10.2016
comment
в чем смысл MyConstructorClass myConstructorClass;? Вы никогда этим не пользуетесь? Вы использовали myConstructorClass вместо myObject? - person Dan; 16.08.2017
comment
@Dan Я, честно говоря, не помню, как это было некоторое время назад, но да, это может быть просто ошибка, которую я оставил, или переменная, которую я использовал раньше, но в конечном итоге в ней не было необходимости. - person AntoineB; 16.08.2017
comment
Только что наткнулся на этот поток, и он довольно старый, но люди все еще смотрят на него, поэтому прокомментируем: этот подход не может работать правильно, поскольку объект applicationContext используется в контроллере, а он устанавливается только после объекта построен (не позволяйте static ввести вас в заблуждение). Значит, этот процессор должен выйти из строя на NPE во второй строке, не так ли? - person GullerYA; 22.03.2020

Другой альтернативой, если у вас уже есть экземпляр созданного объекта и вы хотите добавить его как зависимость @autowired для инициализации всех внутренних переменных @autowired, может быть следующее:

@Autowired 
private AutowireCapableBeanFactory autowireCapableBeanFactory;

public void doStuff() {
   YourObject obj = new YourObject("Value X", "etc");
   autowireCapableBeanFactory.autowireBean(obj);
}
person Jason Glez    schedule 25.06.2018

Мне нравится ответ Закарии, но если вы находитесь в проекте, где ваша команда не хочет использовать этот подход, и вы застряли, пытаясь построить что-то с типом String, integer, float или примитивным типом из файла свойств в конструктор, то вы можете использовать аннотацию Spring @Value для параметра в конструкторе.

Например, у меня возникла проблема, когда я пытался вставить строковое свойство в свой конструктор для класса, помеченного @Service. Мой подход работает для @Service, но я думаю, что этот подход должен работать с любым Java-классом Spring, если он имеет аннотацию (например, @Service, @Component и т. Д.), Которая указывает, что Spring будет одним из экземпляров класса, создающим.

Скажем, в каком-то yaml-файле (или любой другой конфигурации, которую вы используете) у вас есть что-то вроде этого:

some:
    custom:
        envProperty: "property-for-dev-environment"

и у вас есть конструктор:

@Service // I think this should work for @Component, or any annotation saying Spring is the one calling the constructor.
class MyClass {
...
    MyClass(String property){
    ...
    }
...
}

Это не будет работать, поскольку Spring не сможет найти строку envProperty. Итак, это один из способов получить это значение:

class MyDynamoTable
import org.springframework.beans.factory.annotation.Value;
...
    MyDynamoTable(@Value("${some.custom.envProperty}) String property){
    ...
    }
...

В приведенном выше конструкторе Spring вызовет класс и знает, что при его вызове нужно использовать String "property-for-dev-environment", извлеченный из моей конфигурации yaml.

ПРИМЕЧАНИЕ: я считаю, что аннотация @Value предназначена для строк, промежуточных чисел и я считаю, что примитивные типы. Если вы пытаетесь передать пользовательские классы (beans), то подходы в ответах, определенных выше, работают.

person SomeGuy    schedule 11.12.2020