Как внедрить зависимости в самопроизвольный объект в Spring?

128

Допустим, у нас есть класс:

public class MyClass {
    @Autowired private AnotherBean anotherBean;
}

Затем мы создали объект этого класса (или какой-то другой фреймворк создал экземпляр этого класса).

MyClass obj = new MyClass();

Можно ли по-прежнему внедрять зависимости? Что-то вроде:

applicationContext.injectDependencies(obj);

(Думаю, у Google Guice есть что-то вроде этого)

Игорь Мухин
источник

Ответы:

194

Сделать это можно с помощью autowireBean()метода AutowireCapableBeanFactory. Вы передаете ему произвольный объект, и Spring будет относиться к нему как к чему-то, что он создал сам, и применит различные части и части автоподключения.

Чтобы получить AutowireCapableBeanFactoryдоступ, просто подключите следующее:

private @Autowired AutowireCapableBeanFactory beanFactory;

public void doStuff() {
   MyBean obj = new MyBean();
   beanFactory.autowireBean(obj);
   // obj will now have its dependencies autowired.
}
skaffman
источник
Хороший ответ (+1). Также есть второй метод, с помощью которого вы можете повлиять на то, как происходит автоматическое
Шон Патрик Флойд
Но что, если у меня есть два объекта, а второй - автопроволока. Как autowire bean factory справляется с зависимостями в кейсе?
Вадим Кирильчук
3
На самом деле это плохой образец. Если это то, как вы действительно используете MyBean, почему бы просто не иметь конструктор с параметром AnotherBean в качестве параметра. Что-то вроде: codeчастный компонент @Autowired AnotherBean; public void doStuff () {MyBean obj = новый MyBean (bean); } code. Похоже, что со всеми этими аннотациями люди действительно сбиваются с толку и просто не используют базовый шаблон, который был в java SDK с первого дня. :(
Денис
3
Я согласен - Spring полностью изменил определение языка. Теперь мы используем интерфейсы как конкретные классы, методы как экземпляры классов и всевозможные сложные и тяжелые методы выполнения того, что вы делали раньше, с новым и достойным дизайном.
Родни П. Барбати
@Denis, если MyBean имеет зависимости, которые фактическому классу не нужны, вы регистрируете зависимости, которых на самом деле не существует, просто для создания экземпляра класса, поэтому на самом деле нет никакой разницы.
Далтон
17

Вы также можете пометить свой MyClass аннотацией @Configurable:

@Configurable
public class MyClass {
   @Autowired private AnotherClass instance
}

Затем во время создания он автоматически внедрит свои зависимости. У вас также должен быть <context:spring-configured/>в контексте вашего приложения xml.

glaz666
источник
2
Galz666, ваш метод выглядит намного чище для того, что я хочу сделать. Однако я не могу заставить его работать. У меня нет XML-файла, и я использую полностью конфигурацию Java. Есть ли эквивалент <context:spring-configured/>?
masstroy
1
Это чистое решение, но требует немного больше усилий: вы должны либо использовать переплетение во время загрузки, как показано выше iimuhin, либо добавить компилятор AspectJ в проект. Время загрузки, как следует из названия, вызовет дополнительные затраты во время выполнения.
jsosnowski
4

Просто возникла такая же потребность, и в моем случае это уже была логика внутри управляемого java-класса, отличного от Spring, к которому был доступ ApplicationContext. В стиле эшафот. Решено:

AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
factory.autowireBean(manuallyCreatedInstance);
rand0m86
источник
3

Я хотел поделиться своим решением, которое следует @Configurableподходу, brieflyупомянутому в ответе @ glaz666, потому что

  • Ответ на @skaffman почти 10 лет, и это не означает , что не достаточно хорошо или не работает
  • Ответ @ glaz666 краток и на самом деле не помог мне решить мою проблему, но указал мне правильное направление

Моя установка

  1. Spring Boot 2.0.3 с Spring Neo4j & Aop starts(что в любом случае не имеет значения)
  2. Создайте экземпляр bean-компонента, когда он Spring Bootбудет готов, используя @Configurableподход (using ApplicationRunner)
  3. Gradle и Eclipse

меры

Мне нужно было выполнить следующие шаги, чтобы заставить его работать

  1. Объект @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE, dependencyCheck = false)должен быть помещен поверх вашего, Beanкоторый должен быть создан вручную. В моем случае Beanэкземпляр, который должен быть создан вручную, имеет @Autowiredслужбы, следовательно, реквизиты для указанной выше аннотации.
  2. Аннотируйте основную часть Spring Boot XXXApplicaiton.java(или файл, помеченный @SpringBootApplication) с помощью @EnableSpringConfiguredи@EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.ENABLED)
  3. Добавьте зависимости в свой файл сборки (например, build.gradle или pom.xml в зависимости от того, какой из них вы используете) compile('org.springframework.boot:spring-boot-starter-aop')иcompile('org.springframework:spring-aspects:5.0.7.RELEASE')
  4. Новый + ваш, Beanкоторый аннотирован @Configurableгде угодно, и его зависимости должны быть автоматически подключены.

* Что касается пункта 3 выше, я знаю, что org.springframework.boot:spring-boot-starter-aopтранзитивно тянет spring-aop(как показано здесь mavencentral ), но в моем случае Eclipse не смог разрешить @EnableSpringConfiguredаннотации, поэтому я явно добавил spring-aopзависимость в дополнение к стартеру. Если вы столкнетесь с той же проблемой, просто объявите о зависимости или отправляйтесь на поиски решения

  • Есть ли конфликт версий
  • Почему org.springframework.context.annotation.aspect.*недоступен
  • Правильно ли настроена ваша IDE?
  • И т. Д. И т. Д.
Raf
источник