В любом случае, @Autowire bean, который требует аргументов конструктора?
Я использую Spring 3.0.5 и использую аннотацию @Autowire для моих членов класса как можно больше. Один из beans, который мне нужен для autwire, требует аргументов для его конструктора. Я просмотрел документы Spring, но, похоже, не нашел ссылки на то, как комментировать аргументы конструктора.
В XML я могу использовать как часть определения bean. Есть ли аналогичный механизм для аннотации @Autowire?
Пример:
@Component
public class MyConstructorClass{
String var;
public MyConstructorClass( String constrArg ){
this.var = var;
}
...
}
@Service
public class MyBeanService{
@Autowired
MyConstructorClass myConstructorClass;
....
}
В этом примере, как указать значение "constrArg" в MyBeanService с аннотацией @Autowire? Есть ли способ сделать это?
Спасибо,
Эрик
Ответы
Ответ 1
Вам нужна аннотация @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
с разными значениями, вы должны использовать аннотации классификатора
Ответ 2
В этом примере, как я могу указать значение "constrArg" в MyBeanService
с аннотацией @Autowire
? Есть ли способ сделать это?
Нет, не так, как вы. bean, представляющий MyConstructorClass
, должен быть конфигурируемым, не требуя какого-либо его клиента beans, поэтому MyBeanService
не может сказать, как настроено MyConstructorClass
.
Это не проблема с автосогласованием, проблема в том, как Spring создает экземпляр MyConstructorClass
, учитывая, что MyConstructorClass
является @Component
(и вы используете компонентное сканирование и, следовательно, не указываете MyConstructorClass
явно в вашей конфигурации).
Как сказал @Sean, один из ответов здесь заключается в использовании @Value
для параметра конструктора, так что Spring будет извлекать значение конструктора из системного свойства или файла свойств. Альтернативой является MyBeanService
для непосредственного создания экземпляра MyConstructorClass
, но если вы это сделаете, то MyConstructorClass
больше не является Spring bean.
Ответ 3
Ну, время от времени я сталкиваюсь с тем же вопросом. Насколько мне известно, этого нельзя сделать, когда нужно добавить динамические параметры к конструктору. Однако шаблон factory может помочь.
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 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 bean как таковой, но он достаточно близко. Dependency Injection работает, хотя я ввожу factory, а не сам bean - нужно добавить новый factory поверх своей новой реализации MyBean
, если захотите ее заменить.
Кроме того, MyBean
имеет доступ к другому beans - потому что он может иметь доступ к файлу factory.
И, по-видимому, возможно, потребуется добавить некоторую логику в функцию getMyBean
, что является дополнительным усилием, которое я допускаю, но, к сожалению, у меня нет лучшего решения. Поскольку обычно проблема заключается в том, что динамические параметры поступают из внешнего источника, например базы данных или взаимодействия с пользователем, поэтому я должен создать экземпляр bean только в середине запуска, только когда эта информация будет доступна, поэтому Factory
должно быть достаточно адекватным.
Ответ 4
Вы также можете настроить свой компонент следующим образом:
package mypackage;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConstructorClassConfig {
@Bean
public MyConstructorClass myConstructorClass(){
return new myConstructorClass("foobar");
}
}
}
С аннотацией Bean
вы говорите Spring зарегистрировать возвращенный бин в BeanFactory
.
Таким образом, вы можете подключить его по своему желанию.
Ответ 5
Вам нужно использовать @Autowired и @Value. Для получения дополнительной информации по этой теме обратитесь к post.
Ответ 6
Альтернативой может быть вместо передачи параметров конструктору, который может быть у вас в качестве getter и seters, а затем в @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;
....
}
Ответ 7
Большинство ответов довольно старые, поэтому в то время это было невозможно, но на самом деле существует решение, удовлетворяющее всем возможным вариантам использования.
Так что правильно знаете ответы:
- Не предоставление реального компонента Spring (дизайн factory)
- или не подходит для каждой ситуации (используя
@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 -managed (я использую его для создания нескольких асинхронных исполнителей "на лету", например)
Единственное, о чем следует помнить, это то, что у вас должен быть конструктор, который не принимает никаких аргументов (и может быть пустым) в классе, который вы хотите создать (или конструктор @Autowired
, если он вам нужен).
Ответ 8
Другой вариант, если у вас уже есть экземпляр созданного объекта и вы хотите добавить его как зависимость @autowired для инициализации всех внутренних переменных @autowired, может быть следующим:
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
public void doStuff() {
YourObject obj = new YourObject("Value X", "etc");
autowireCapableBeanFactory.autowireBean(obj);
}
Ответ 9
В
Вы нашли ответ на этот пост? Я ищу то же самое, дайте мне знать, если какое-либо из решений работает для вас.
Спасибо