Як ввести залежності навесні в об'єкт, що самоаналізується, навесні?


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.
}

Гарна відповідь (+1). Існує також другий метод, за допомогою якого ви можете впливати на те, як відбувається автопроводка
— Шон Патрік Флойд

Але що робити, якщо у мене два об’єкти, а перший автопровідний другий. Як фабрика автопровідних бобів справляється із залежностями у випадку?
— Вадим Кирильчук

3
Це насправді погана закономірність. Якщо ви справді використовуєте MyBean, чому б просто не створити конструктор з AnotherBean як параметр. Щось на кшталт: codeприватний @Autowired AnotherBean боб; public void doStuff () {MyBean obj = новий MyBean (боб); } code. Схоже, що з усіма цими примітками люди заплутуються і просто не використовують основний зразок, який був у java SDK з першого дня :(
— Денис

3
Я згоден - Весна переробила всю мову. Зараз ми використовуємо інтерфейси як конкретні класи, методи як екземпляри класів і всілякі складні та кодові важкі методи робити те, що ви раніше робили з новим та гідним дизайном.
— Родні П. Барбаті

@Denis, якщо MyBean має залежності, які фактичному класу не потрібні, ти реєструєш залежності, які насправді не існують, просто для інстанції класу, так що різниці немає.
— Далтон

17

Ви також можете позначити свій MyClass за допомогою @Configurable анотації:

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

Тоді під час створення він автоматично вводить свої залежності. Ви також повинні мати <context:spring-configured/>у своєму контексті програми xml.


2
Galz666, ваш метод виглядає набагато чистішим, що я хочу зробити. Однак я не можу змусити його працювати. У мене немає файлу xml і я використовую повністю конфігурацію Java. Чи є еквівалент <context:spring-configured/>?
— masstroy

1
Це чисте рішення, але вимагає трохи більше зусиль: вам слід використовувати плетіння часу навантаження, як показано в iimuhin вище, або додати компілятор AspectJ до проекту. Час завантаження, як випливає з назви, спричинить додаткові витрати під час виконання.
— jsosnowski

4

Щойно я отримав таку саму потребу, і в моєму випадку до неї вже була логіка всередині не керованого ява класу Spring ApplicationContext. Натхненний скафманом. Вирішено:

AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
factory.autowireBean(manuallyCreatedInstance);

3

Я хотів поділитися своїм рішенням, яке відповідає @Configurableпідходу, як brieflyзгадується у відповіді @ glaz666, оскільки

  • відповідь на @skaffman майже 10 років, і це не означає , що не досить добре або не працює
  • Відповідь @ glaz666 коротка і не дуже допомогла мені вирішити мою проблему, але вказувала мені в правильному напрямку

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

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

Кроки

Мені потрібно було виконати наведені нижче кроки, щоб змусити його працювати

  1. Поміщення, @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE, dependencyCheck = false)яке слід розміщувати поверх того, Beanщо повинно бути встановлено вручну. У моєму випадку те, Beanщо має бути встановлено вручну, має @Autowiredпослуги, отже, реквізити вищевказаних анотацій.
  2. Анотація головного весняного завантаження 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(як показано тут мавенцентральне ), але, у моєму випадку, Затемнення не вдалося вирішити @EnableSpringConfiguredанотації, отже, чому я явно додав spring-aopзалежність на додаток до стартера. Якщо ви зіткнулися з одним і тим же питанням, просто оголосьте про залежність або вирушайте у пригоду з'ясування

  • Чи є конфлікт версій
  • Чому org.springframework.context.annotation.aspect.* не доступний
  • Чи правильно налаштовано IDE
  • І т.д.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.