Есть ли способ @Autowire bean-компонента, который требует аргументов конструктора? - PullRequest
94 голосов
/ 19 июля 2011

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

В XML я могу использовать как часть определения компонента. Есть ли похожий механизм для аннотации @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? Есть ли способ сделать это?

Спасибо

Eric

Ответы [ 8 ]

53 голосов
/ 19 июля 2011

Вам нужна аннотация @Value.

Обычный вариант использования - назначение значений полей по умолчанию с использованием "#{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 с разными значениями, вы должны использовать аннотации Qualifier

10 голосов
/ 03 декабря 2015

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

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 не является бобом как таковым, но он достаточно близок. Внедрение зависимостей работает, хотя я внедряю фабрику, а не сам бин - нужно внедрять новую фабрику поверх собственной новой реализации MyBean, если кто-то хочет заменить ее.

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

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

9 голосов
/ 19 июля 2011

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

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

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

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

5 голосов
/ 22 января 2017

Вы также можете настроить свой компонент так:

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


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

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

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

1 голос
/ 22 ноября 2014

Вам нужно использовать @Autowired и @Value.Для получения дополнительной информации по этой теме см. сообщение .

0 голосов
/ 26 июня 2018

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

@Autowired 
private AutowireCapableBeanFactory autowireCapableBeanFactory;

public void doStuff() {
   YourObject obj = new YourObject("Value X", "etc");
   autowireCapableBeanFactory.autowireBean(obj);
}
0 голосов
/ 31 октября 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, если вам это нужно).

0 голосов
/ 06 марта 2016

В качестве альтернативы вместо передачи параметров конструктору вы можете использовать их в качестве методов получения и установки, а затем в @PostConstruct инициализировать значения, как вам нужно. В этом случае Spring создаст компонент с помощью конструктора по умолчанию. Пример ниже

@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;

  ....
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...