Как можно связать бины spring
Как можно связать бины spring
Spring. Первые шаги
Данный фреймворк имеет высокую модульность, каждый модуль способен запускаться и выполнять свои задачи как самостоятельное приложение, либо же может использоваться в группе с другими модулями (фреймворки в фреймворке). Функционал спринга постоянно обновляется и дополняется, наиболее популярные модули реализуют функционал MVC, работы с базой данных, секьюрности приложений, обмена сообщений, шаблонизаторов и многого другого.
И в обратную сторону: Контейнер + метаданные (настройки) позволяют запустить образуют ваше приложение с контейнером, который управляет бинами.
Первая задача. HelloWorld.
Всего 3 класса и одна зависимость.
Класс App содержит метод мейн, с которого запускается спринг при создании контекста. В контекст мы передаем класс AppConfig, который помечен аннотацией @Configuration, так спринг понимает, что это настроечный класс.
В классе AppConfig содержится метод, помеченный аннотацией как бин, этот метод будет выполнен при запуске и его результат станет объектом управляемым спрингом.
Класс HelloWorld является обычным джава-классом, мы его используем без каких-либо сложностей.
В методе мейн мы можем получить бин ХеллоВорлд по имени и использовать в любом месте программы.
Спринг позволяет использовать различные типы бинов, одни будут жить от старта до завершения программы, другие будут создаваться при каждом реквесте или открытии новой сессии, третьи при каждом вызове будут создаваться новые. За это отвечает аннотация @Scope.
Задача 2. Spring Beans
Теперь поработаем со связыванием бинов. Вызывать бины из контекста хорошо, но не так удобно, как связывать их автоматически.
Для работы аннотаций связывания, я добавил в мавен ядро спринга, так мы имеем совершенно рабочее спринг приложение, хоть и без дополнительных модулей.
В компоненте AnimalCage есть поле типа Animal, которое связано с помощью аннотации @Autowired. При выполнении программы DI-контейнер потянет сюда бин, подходящий по типу.
Задача 3. Виды связывания бинов.
Чтобы спринг видел все бины, в классе AppConfig была добавлена аннотация @ComponentScan.
В спринге связать бины можно различными способами, рассмотрим их на примере:
Собрать цепочку до 8го элемента, использовав все вышеперечисленные методы связывания. После выполнения вы должны получить полную фразу.
Вопросы для самопроверки:
Подготовка к Spring Professional Certification. Контейнер, IoC, бины
Доброго времени суток, Хабр.
Сегодня я решил представить вам перевод цикла статей для подготовки к Spring Professional Certification.
Это перевод только первой статьи, если он зайдет аудитории, я продолжу выпуск переводов.
Внедрение зависимостей — это специальный паттерн, который уменьшает связь между Spring компонентами. Таким образом, при применении DI, ваш код становится чище, проще, его становится легче понять и тестировать.
Согласно паттерну DI, создание объектов для зависимостей переходит на фабрику или отдается третьей стороне. Это означает, что мы можем сосредоточиться на использовании этих объектов вместо их создания.
В Spring Framework интерфейс org.springframework.factory.BeanFactory предоставляет фабрику для бинов, которая в то же время является IoC контейнером приложения. Управление бинами основано на конфигурации(java или xml).
Интерфейс org.springframework.context.ApplicationContext — это обертка над bean factory, предоставляющая некоторые дополнительные возможности, например AOP, транзакции, безопасность, i18n, и т.п.
Основа Spring Framework — контейнер, и наши объекты «живут» в этом контейнере.
Контейнер обычно создает множество объектов на основе их конфигураций и управляет их жизненным циклом от создания объекта до уничтожения.
Контейнер — это объект, реализующий интерфейс ApplicationContext.
Spring обеспечивает несколько разновидностей контекста.
Есть несколько основных реализаций интерфейса ApplicationContext:
Примеры создания контекста:
Если вы используете JUnit 5, то вам нужно указать 2 аннотации:
Если это не веб-приложение, то есть 2 способа:
В Spring Boot приложении:
Этот класс поместит в контейнер экземпляр класса DataSource. Позднее его можно будет использовать при доступе к базе данных.
Component scanning(сканирование компонентов) — Spring автоматически обнаруживает бины, которые будут находиться в контейнере. Это бины с аннотациями-стереотипами.
Component | Корневая аннотация, которая помечает класс как кандидат для автовнедрения |
Controller | Указывает, что класс является контроллером для отправления данных на фронт. |
@RestController | Указывает, что класс является контроллером для REST. Содержит аннотации Controller и @ResponseBody |
Service | Указывает, что класс является сервисом для выполнения бизнес-логики |
Repository | Указывает, что класс является репозиторием для работы с бд |
@Configuration | Указывает, что класс содержит Java-конфигурацию(@Bean-методы) |
Область видимости — scope, скоуп. Существует 2 области видимости по умолчанию.
Singleton | Область видимости по умолчанию. В контейнере находится всего 1 экземпляр бина |
Prototype | В контейнере может находится любое количество экземпляров бина |
И 4 области видимости в веб-приложении.
Request | Область видимости — 1 HTTP запрос. На каждый запрос создается новый бин |
Session | Область видимости — 1 сессия. На каждую сессию создается новый бин |
Application | Область видимости — жизненный цикл ServletContext |
WebSocket | Область видимости — жизненный цикл WebSocket |
Область видимости указывается с помощью аннотации @Scope на @Bean методах.
Prototype Scope не потокбезопасный, т.к. он не гарантирует что один и тот же экземпляр будет вызываться только в 1 потоке.
Singleton Scope же наоборот потокобезопасный.
Singleton-бины обычно создаются сразу при сканировании.
Prototype-бины обычно создаются только после запроса.
Singleton bean можно внедрять в любой другой бин.
Prototype может быть зависимостью для любого бина.
Внедрять можно только singleton или prototype.
Для того чтобы использовать кастомный BFPP. Вы можете переопределить механизм получения данных из метафайлов.
Есть 3 варианта для создания таких методов:
Ниже перечислены типы DI, которые могут быть использованы в вашем приложении:
DI через конструктор считается самым лучшим способом, т.к. для него не надо использовать рефлексию, а также он не имеет недостатков DI через сеттер.
DI через поле не рекомендуется использовать, т.к. для этого применяется рефлексия, снижающая производительность.
DI через конструктор может приводить к циклическим зависимостям. Чтобы этого избежать, можно использовать ленивую инициализацию бинов или DI через сеттер.
Контейнер обрабатывает DI с помощью AutowiredAnnotationBeanPostProcessor. В связи с этим, аннотация не может быть использована ни в одном BeanFactoryPP или BeanPP.
Если внедряемый объект массив, коллекция, или map с дженериком, то Spring внедрит все бины подходящие по типу в этот массив(или другую структуру данных). В случае с map ключом будет имя бина.
Вы можете использовать разные типы внедрения:
Spring предоставляет аннотацию Qualifier, чтобы преодолеть проблему неоднозначности при DI.
Если в контейнере есть несколько бинов одного типа(SomeClass), то контейнер внедрит именно тот бин, над @Bean-методом которого стоит соответствующий квалификатор. Также можно не ставить квалификатор на метод, а использовать имя бина в качестве параметра квалификатора.
Имя бина можно можно указать через параметр аннотации Bean, а по умолчанию это имя фабричного метода.
Прокси это специальный объект, который имеет такие же публичные методы как и бин, но у которого есть дополнительная функциональность.
Два вида прокси:
Если в контейнере нет экземпляра бина, то вызывается @Bean-метод. Если экземпляр бина есть, то возвращается уже созданный бин.
В эту переменную будет внедрена строка, например из property или из view.
Как обычно, просьба присылать правки или найденные ошибки в личку.
Кастомизация резолвинга зависимостей в Spring
Привет! Меня зовут Андрей Неведомский и я главный инженер в СберТехе. Я работаю в команде, которая занимается разработкой одного из системных сервисов ЕФС (Единой Фронтальной Системы). В своей работе мы активно используем Spring Framework, в частности его DI, и время от времени сталкиваемся с тем, что резолвинг зависимостей в спринге оказывается недостаточно «умным» для нас. Эта статья – результат моих попыток сделать его умнее и в целом разобраться с тем, как он работает. Надеюсь, и вы сможете узнать из неё что-то новое об устройстве спринга.
Перед прочтением статьи настоятельно рекомендую ознакомиться с докладами Евгения EvgenyBorisov Борисова: Spring-потрошитель, часть 1; Spring-потрошитель, часть 2. Ещё есть плейлист из них.
Введение
Давайте представим, что нас попросили разработать сервис для предсказания судьбы и гороскопов. В нашем сервисе есть несколько компонентов, но основными для нас будут два:
Также в нашем сервисе будет несколько эндпойнтов (контроллеров) для, собственно, получения предсказаний судьбы и гороскопов. И ещё мы будем осуществлять контроль доступа к нашему приложению по IP с помощью аспекта, который будет применяться к методам контроллеров и выглядеть примерно так:
Итак, приложение мы разработали и у нас все здорово работает. Но давайте теперь поговорим о тестировании.
Как это тестировать?
Давайте поговорим о том, как нам протестировать корректность применения аспектов. У нас есть несколько способов сделать это.
Можно написать отдельные тесты на аспект и на контроллеры, без поднятия спрингового контекста (который как раз создаст прокси с аспектом для контроллера, подробнее об этом можно почитать в официальной документации), но в этом случае мы не протестируем именно то, что аспекты корректно применяются к контроллерам и работают именно так, как мы этого ожидаем;
Можно написать тесты, в которых мы будем поднимать полный контекст нашего приложения, но в этом случае:
Как создавать бины-моки?
Java Config с зависимостями в каждом тесте
Мы можем для каждого теста написать Java Config, в которым опишем как бины контроллера и аспекта, так и бины с моками зависимостей контроллера. Такой способ описания бинов будет императивным, поскольку мы будем явно говорить спрингу, каким образом нам нужно создать бины.
В этом случае тест для нашего контроллера будет выглядеть так:
Выглядит такой тест достаточно громоздко. В этом случае нам придётся написать Java Config для каждого из контроллеров. Хотя он будет разным по содержанию, смысл у него будет один и тот же: создать бин контроллера и моки для его зависимостей. Так что по сути он будет одинаковым для всех контроллеров. Я, как и любой программист, человек ленивый, поэтому от такого варианта отказался сразу же.
Аннотация @MockBean над каждым полем с зависимостью
В этом случае тест будет выглядеть так:
В этом варианте по-прежнему есть Java Config, но он значительно компактнее. Из недостатков – мне пришлось объявить поля с зависимостями контроллера (поля с аннотацией @MockBean ), даже несмотря на то, что дальше в тесте они никак не используются. Ну и в случае, если вы используете по какой-то причине Spring Boot версии ниже чем 1.4.0, то воспользоваться этой аннотацией вы не сможете.
Поэтому у меня родилась идея для ещё одного варианта мокирования. Мне хотелось бы, чтобы это работало так…
Аннотация @Automocked над зависимым компонентом
Тест в этом случае мог бы выглядеть так:
Как это работает?
Давайте разберемся, как это работает и что нам для этого нужно.
TestExecutionListener
В спринге есть такой интерфейс – TestExecutionListener. Он предоставляет API для встраивания в процесс выполнения теста на разных его этапах, например при создании инстанса тестового класса, перед вызовом тестового метода или после него и т.д. У него есть несколько имплементаций «из коробки». Например DirtiesContextTestExecutionListener, которая выполняет очистку контекста в случае, если вы поставили соответствующую аннотацию; DependencyInjectionTestExecutionListener – выполняет инъекцию зависимостей в тестах и т.д. Чтобы применить ваш кастомный Listener к тесту, нужно поставить над ним аннотацию @TestExecutionListeners и указать вашу имплементацию.
Ordered
Также в спринге есть интерфейс Ordered. Он используется для указания на то, что объекты должны быть некоторым образом упорядочены. Например, когда у вас есть несколько имплементаций одного и того же интерфейса и вы хотите заинжектить их в коллекцию, то в этой коллекции они будут упорядочены в соответствии с Ordered. В случае с TestExecutionListener’ами эта аннотация указывает на то, в каком порядке они должны применяться.
Итак, наш Listener будет имплементировать 2 интерфейса: TestExecutionListener и Ordered. Назовём мы его AutomockTestExecutionListener и выглядеть он будет так:
Что здесь происходит? Для начала в методе prepareTestInstance() он находит все поля с аннотацией @Automocked :
Затем делает эти поля доступными для записи:
Затем в методе findConstructorToAutomock() находит подходящий конструктор:
Подходящим в нашем случае будет либо конструктор с аннотацией @Autowired, либо конструктор с наибольшим количеством аргументов.
После создания моков для всех аргументов и регистрации их в контексте мы возвращаемся к созданию бина зависимого класса (т.е. контроллера в нашем случае):
Также следует обратить внимание на то, что мы использовали Order 1900. Это нужно потому, что наш Listener должен вызываться после очистки контекста DirtiesContextBeforeModesTestExecutionListener’ом (order=1500) и до инъекции зависимостей DependencyInjectionTestExecutionListener’ом (order=2000), потому что наш Listener создаёт новые бины.
AutowireCandidateResolver
AutowireCandidateResolver используется для определения того, соответствует ли BeanDefinition описанию зависимости. У него есть несколько имплементаций «из коробки», среди них:
Работает резолвер следующим образом:
Зачем понадобился свой резолвер?
Теперь давайте разберемся, зачем вообще понадобился свой резолвер на примере нашего контроллера.
Как видите, у него есть две зависимости одинакового типа – Function, но с разными дженериками. В одном случае – String и ZodiacSign, в другом – String и String. И проблема с этим в том, что Mockito не умеет создавать моки с учётом дженериков. Т.е. если мы создадим моки для этих зависимостей и поместим их в контекст, то Spring не сможет заинжектить их в этот класс, так как они не будут содержать информации о дженериках. И мы увидим исключение о том, что в контексте есть более одного бина класса Function. Как раз эту проблемы мы и решим с помощью своего резолвера. Ведь, как вы помните, в нашей имплементации Listener’а мы использовали в качестве имени бина тип с дженериками, а значит всё, что нам нужно сделать – это научить спринг сравнивать тип зависимости с именем бина.
AutomockedBeanByNameAutowireCandidateResolver
Итак, наш резолвер будет делать именно то, о чем я писал выше, а имплементация метода isAutowireCandidate() будет выглядеть так:
Здесь он получает строковое представление типа зависимости из описания зависимости, получает имя бина из BeanDefinition’а (которое уже содержит строковое представление типа бина), затем сравнивает их и если совпали – возвращает true. Если не совпали – делегирует внутреннему резолверу.
Варианты мокирования бинов в тестах
Итого, в тестах мы можем использовать следующие варианты мокирования бинов:
Добавим декораторов
Мы в нашей команде очень любим шаблон проектирования «Декоратор» за его гибкость. По сути аспекты реализуют именно этот шаблон. Но в случае, если вы конфигурируете контекст спринга аннотациями и используете package scan, вы столкнетесь с проблемой. Если у вас в контексте окажется несколько имплементаций одного и того же интерфейса, то при старте приложения вывалится NoUniqueBeanDefinitionException, т.е. спринг не сможет разобраться какой из бинов куда должен инжектиться. У этой проблемы есть несколько вариантов решения и дальше мы посмотрим на них, но сначала давайте разберемся, как изменится наше приложение.
Сейчас у интерфейсов FortuneTeller и HoroscopeTeller есть по одной имплементации, мы добавим еще по 2 имплементации для каждого из интерфейсов:
Java Config с верхнеуровневым декоратором
Можно снова воспользоваться Java Config’ом. В этом случае мы будем описывать бины в виде методов класса-конфига, причем аргументы, необходимые для вызова конструктора бина, нам придётся указать как аргументы метода. Из чего следует, что в случае изменения конструктора бина нам придётся менять и конфиг, что не очень круто. Из достоинств такого варианта:
Как видите, для каждого из интерфейсов здесь объявлен только один бин, а методы содержат в аргументах зависимости всех объектов, создаваемых внутри. В этом случае логика создания бинов достаточно очевидна.
Qualifier
Можно использовать аннотацию @Qualifier. Это будет более декларативно, чем Java Config, но в этом случае нужно будет явно указать имя бина, от которого зависит текущий бин. Из чего следует недостаток: повышается связность между бинами. А поскольку повышается связность, то и в случае изменения порядка декораторов изменения будут размазаны ровным слоем по коду. То есть в случае добавления нового декоратора, например, в середину цепочки, изменения затронут минимум 2 класса.
На примере логирующего декоратора для предсказателя судьбы видно, что, поскольку он у нас является верхнеуровневым (именно он должен инжектиться во все классы, использующие FortuneTeller, например в контроллеры), то над ним стоит аннотация @Primary. А над аргументом конструктора internal стоит аннотация @Qualifier с указанием имени бина, от которого он зависит — cachingFortuneTeller. В нашем случае в него должен инжектиться кэширующий декоратор.
Custom qualifier
Начиная с версии 2.5 спринг предоставляет возможность для объявления собственных Qualifier’ов, чем мы можем воспользоваться. Выглядеть это может следующим образом.
Для начала мы объявим enum с типами декораторов:
Затем объявим свою аннотацию, которая будет являться qualifier’ом:
Ну а сами декораторы в случае использования кастомного Qualifier’а будут выглядеть так:
Кэширующий декоратор – средний в нашей цепочке, поэтому над ним стоит аннотация @Decorator с указанием того, что он кэширующий, а в его конструкторе – та же аннотация с указанием того, что в него должен инжектиться не декоратор, то есть дефолтная имплементация FortuneTeller’а, в нашем случае – Globa.
Выглядит такой вариант по сравнению с Qualifier’ами чем-то лучше, чем-то хуже. Хуже, потому что теперь аннотацию нужно ставить не только в конструкторе, но и над самим классом. Лучше, потому что связность между бинами всё-таки стала чуть ниже – бины теперь не знают имя декоратора, который должен инжектиться в них, они знают только тип этого декоратора.
DecoratorAutowireCandidateResolver
Последний вариант – написать свой резолвер! Ведь мы здесь именно для этого! 🙂 Мне бы хотелось, чтобы у нас был какой-то способ явно объявить порядок декораторов, как в Java Config’е, при этом не объявляя все зависимости этих декораторов. Например, с помощью какого-то кастомного бина в конфиге, который бы содержал порядок декораторов. Выглядеть это могло бы так:
Выглядит вроде бы неплохо – мы получаем преимущества Java Config’а в виде более явного объявления и локализованности, при этом избавляемся от его недостатка – громоздкости. Посмотрим, что нам для этого понадобится!
Для начала нам понадобится какой-то способ объявления порядка. Им может быть, например, интерфейс с одним методом, который бы возвращал упорядоченный список классов. Выглядеть он может так:
BeanDefinitionRegistryPostProcessor
Также нам понадобится BeanDefinitionRegistryPostProcessor, который расширяет BeanFactoryPostProcessor, вызывается до него, и, согласно документации, может использоваться для регистрации новых BeanDefinition’ов. Не то, чтобы для этого нельзя использовать BeanFactoryPostProcessor, просто так кажется правильней.
Делать он будет следующее:
BeanFactoryPostProcessor
Не обойдём мы стороной и BeanFactoryPostProcessor, который используется для манипуляции с BeanDefinition’ами до того, как начнется инициализация бинов. Пожалуй, наиболее известен этот класс как «жертва Spring-потрошителя».
Всё, что он будет делать для нас, – это регистрировать в фабрике нашу имплементацию AutowireCandidateResolver’а:
DecoratorAutowireCandidateResolver
Он получает из descriptor’а тип зависимости (dependencyType) и тип зависимого класса (dependentType):
Затем получает из bdHolder’а BeanDefinition:
Сравнивает тип зависимости и тип зависимого класса. Таким образом мы проверяем, что имеем дело с декоратором:
Если они не совпали, то делегируем дальнейшую проверку внутреннему резолверу, т.к. мы имеем дело не с декоратором.
Получает из BeanDefinition’а аннотацию с указанием на класс родительского декоратора:
И если аннотация есть, то сравнивает указанный в ней класс с зависимым классом:
Если они совпали – мы нашли подходящий бин (декоратор), если нет – возвращаем false.
Другие варианты реализации
Варианты определения порядка инъекции
Итак, для определения порядка инъекции в спринге можно использовать следующие варианты:
Выводы
В любом случае, пользоваться этим или не пользоваться – решать вам, но я надеюсь, что из этой статьи вам удалось узнать что-то новое. Спасибо за чтение!
Способы внедрения зависимостей (Dependency Injection) в Spring
Dependency Injection (внедрение зависимостей) – ключевой шаблон проектирования в Spring. Мы говорим фреймворку создать за нас бины (иначе говоря — объекты) и внедрить их в другие бины. И фреймворк это делает.
Но как объяснить фреймворку Spring, что такой-то бин должен стать зависимостью для другого бина? Вариантов немного, а самых частых всего два: бин внедряется либо через конструктор класса, либо с помощью сеттера. Первое называется constructor-based injection, а второе — setter-based injection.
В этой статье мы создадим бин Engine и будем внедрять его в два других бина: в бин CarWithConstructor с помощью конструктора и в CarWithSetter с помощью сеттера.
Конфигурация Maven
Чтобы начать работу с бинами, необходимо добавить в pom.xml зависимость:
Определим классы. Итак, сначала у нас есть три класса. Класс Engine:
Класс CarWithConstructor с конструктором:
И класс CarWithSetter с сеттером:
Чтобы внедрить бин, классов нам недостаточно, Spring имеет дело с бинами, а не классами. Поэтому нужно сконфигурировать эти классы так, чтобы Spring контейнер создал на их основе бины. В конфигурации заодно будут заданы и pзависимости. Конфигурировать бины можно либо с помощью аннотаций, либо с помощью XML. (Но учтите, что XML-конфигурация немного устарела.)
Конфигурация бинов с помощью аннотаций
До того как внедрять бин engine, давайте его определим:
Аннотация @Component говорит фреймворку превратить класс в бин. При запуске Spring создаст экземпляр класса Engine. Этот экземпляр будет синглтоном в нашем случае. Мы сможем его впоследствии получить из контекста приложения с помощью команды:
И он будет внедрен во все бины, где мы зададим его в качестве зависимости. Неважно каким способом – через конструктор или сеттер.
Давайте зададим пакет, в котором хранятся бины, чтобы Spring знал, где их искать. Это делается с помощью аннотации @ComponentScan:
Обычно в классе Config прописываются конфигурации, но в нашем простом приложении он пуст.
В пакете «ru.javalang.injection» Spring будет искать аннотированные с помощью @Component классы, чтобы превратить их в бины при запуске приложения и инициализации контейнера Spring.
Итак, мы определили один бин engine. Теперь можно его внедрять в другие бины. Конечно, эти другие бины тоже надо сконфигурировать. И внутри конфигурации задать зависимости (dependency injection).
Constructor Based Injection
Если в классе есть конструктор, то можно внедрить зависимость через конструктор. При создании класса контейнер Spring вызовет конструктор и передаст зависимость в качестве аргумента конструктора.
Давайте определим бин CarWithConstructor и внедрим в него бин Engine с помощью конструктора:
Аннотация @Component означает, что класс CarWithConstructor надо зарегистрировать в качестве бина.
А аннотация @Autowired перед конструктором говорит фреймворку внедрить бин engine в качестве зависимости в бин CarWithConstructor.
Обратите внимание, что начиная с версии Spring 4.3 аннотацию @Autowired можно опустить, если у класса всего один конструктор. О том, что в конструкторе надо внедрить бин, фреймворк догадается сам.
Setter Based Injection
Если в классе задан сеттер, то зависимость можно внедрить и через него. Тогда при создании экземпляра класса контейнер вызовет конструктор без аргументов, а потом сеттер, чтобы внедрить зависимость во только что созданный бин.
Определим бин CarWithSetter и внедрим в него бин engine с помощью сеттера.
Для этого используем перед сеттером аннотацию @Autowired:
Так же как в предыдущем случае, аннотацию @Autowired перед сеттером можно опустить.
Более того, можно опустить и сеттер. И просто аннотировать поле car:
И внедрение зависимости все равно произойдет. Несмотря на то, что тут нет ни конструктора, ни сеттера, а поле car имеет модификатор private. Это возможно, потому что под капотом фреймворк использует рефлексию для создания бинов.
Чтобы получить экземпляры машин, надо обратиться к контексту приложения:
Переменная carWithConstructor будет иметь ненулевую ссылку на engine. Хотя мы не создавали ни один объект с помощью оператора new. Все бины создал фреймворк и добавил ссылки на зависимости там, где они были определены.
Обратите внимание, что все бины у нас синглтоны, и обе переменные carWithConstructor и carWithSetter ссылаются на один и тот же engine. Сингтон — самый частый жизненный цикл бина.
Конфигурация бинов с XML
А теперь сконфигурируем все то же самое с помощью XML:
Тег bean задает бин, это аналог аннотации @Component.
Constructor Based Injection
Вот часть вышеприведенного XML, которая определяет бин CarWithConstructor:
Тут constructor-arg определяет внедрение зависимости с помощью конструктора.
Атрибут ref содержит ссылку на идентификатор бина engine.
Setter-Based Injection
А это часть вышеприведенного XML, которая задает бин CarWithSetter:
Здесь тег property задает внедрение зависимости с помощью сеттера.
Обратите внимание, что если мы конфигурируем бины с помощью XML, то задать сеттер в классе необходимо. Иначе будет выброшено исключение. Потому что все послабления в конфигурациях пришли с аннотациями, с XML все гораздо строже.
За контекст, созданный с помощью XML, отвечает другой класс:
Бин из XML-контекста получаем аналогично:
Убедимся, что engine внедрен:
Какой способ внедрения зависимости лучше
Для разработчика большой разницы нет. В документации рекомендуется отталкиваться от класса – его структуры и цели. Если зависимость обязательна в данном классе, то логичнее это поле передавать в конструкторе. А значит это будет внедрение через конструктор. Соответственно если какая-то зависимость необязательна, то внедряем ее через сеттер.
Код примера есть на GitHub.
Способы внедрения зависимостей (Dependency Injection) в Spring: 3 комментария
Большое спасибо вам за ваши статьи. Все очень коротко, ястно и лаконично объясняется…Однозначно лучше чем любой видео-урок на ютубе.
Спасибо, будем продолжать). Правда, как раз хочу записать краткие видео-уроки.
Огромная благодарность Автору статей по Spring, это самые ясные и лаконичные разъяснения работы фреймворка, которые я нашел!
Искренне Ваш, Yustas