Что такое ioc контейнер spring
Руководство по Spring. Контейнеры IoC.
Ключевым элементом Spring Framework является Spring Container. Container создаёт объекты, связывает их вместе, настраивает и управляет ими от создания до момента уничтожения.
Для управления компонентами, из которых состоит приложение, Spring Container использует Внедрение Зависимостей (DI). Эти объекты называются Spring Beans, которые мы обсудим далее.
Spring Container получает инструкции какие объекты инстанциировать и как их конфигурировать через метаданные.
Метаданные могут быть получены 3 способами:
– XML
– Аннотации Java
– Java код
На картинке, которая находится выше схематично показан этот процесс.
Java POJO классы поступает в Spring Container, который на основании инструкций, полученных через метаданные, выдаёт приложение, готовое к использованию.
В Spring имеется 2 различных вида контейнеров:
1. Spring BeanFactory Container;
2. Spring ApplicationContext Container;
Spring BeanFactory Container
Это самый простой контейнер, обеспечивающий базовую поддержку DI и который основан на интерфейсе org.springframework.beans.factory.BeanFactory. Такие интерфейсы, как BeanFactoryAware и DisposableBean всё ещё присутствуют в Spring для обеспечения обратной совместимости.
Наиболее часто используемая реализация интерфейса BeanFactory – XmlBeanFactory.
XmlBeanFactory получает метаданные из конфигурационного XML файла и использует его для создания настроенного приложения или системы.
BeanFactory обычно используется тогда, когда ресурсы ограничены (мобильные устройства). Поэтому, если ресурсы не сильно ограничены, то лучше использовать ApplicationContext.
Конфигурационный файл message-bean.xml
Результат работы программы
Исходный код проекта по ЭТОЙ ССЫЛКЕ.
Spring ApplicationContext Container
ApplicationContext является более сложным и более продвинутым Spring Container-ом. Так же, как BeanFactory, ApplicationContext загружает бины, связывает их вместе и конфигурирует их определённым образом. Но кроме этого, ApplicationContext обладает дополнительной функциональностью: распознание текстовых сообщений из файлов настройки и отображение событий, которые происходят в приложении различными способами. Этот контейнер определяется интерфейсом org.springframework.context.ApplicationContext.
Чаще всего используются следующие реализации AppicationContext:
– FileSystemXmlApplicationContext
Загружает данные о бине из XML файла. При использовании этой реализации в конструкторе необходимо указать полный адрес конфигурационного файла.
– ClassPathXmlApplicationContext
Этот контейнер также получает данные о бине из XML файла. Но в отличие от FileSystemApplicationContext, в этом случае необходимо указать относительный адрес конфигурационного файла (CLASSPATH).
– WebXmlApplicationContext
Эта реализация ApplicationContext получает необходимую информацию из веб-приложения.
В примере создания простого Spring приложения мы уже использовали ClassPathXmlApplicationContext. Теперь мы попробуем использовать FileSystemXmlApplicationContext.
Конфигурационный файл message-bean.xml
Результат работы программы
Исходный код проекта доступен по ЭТОЙ ССЫЛКЕ.
Заключение
Как мы увидели из этого руководства – контейнер ApplicationContext обладает большей функциональностью, чем BeanFactory, поэтому если мы не находимся в условиях крайней ограниченности ресурсов, более предпочтительным является использование ApplicationContext.
IoC, DI, IoC-контейнер — Просто о простом
Думаю сейчас слова IoC, DI, IoC-контейнер, как минимум у многих на слуху. Одни этим активно пользуются, другие пытаются понять, что же это за модные веяния.
На данный момент, на эту тему уже довольно сказано, написано, в том числе и на хабре, но как раз из-за обилия информации сложно найти действительно полезный контент. Кроме того, данные понятия часто смешивают и/или путают. Проанализировав множества материалов я решил изложить вам свое видение предмета.
Теория
Для меня взаимосвязь между IoC и DI такая же как между Agile и Scrum, т.е.
Inversion of Control (инверсия управления) — это некий абстрактный принцип, набор рекомендаций для написания слабо связанного кода. Суть которого в том, что каждый компонент системы должен быть как можно более изолированным от других, не полагаясь в своей работе на детали конкретной реализации других компонентов.
Dependency Injection (внедрение зависимостей) — это одна из реализаций этого принципа (помимо этого есть еще Factory Method, Service Locator).
IoC-контейнер — это какая-то библиотека, фреймворк, программа если хотите, которая позволит вам упростить и автоматизировать написание кода с использованием данного подхода на столько, на сколько это возможно. Их довольно много, пользуйтесь тем, чем вам будет удобно, я продемонстрирую все на примере Ninject.
Практика
Согласно подходу инверсии управления если у нас есть клиент, который использует некий сервис, то он должен делать это не напрямую, а через посредника, своего рода аутсорсинг.
То как технически это будет сделано и определяет каждая из реализаций подхода IoC.
Мы будем использовать DI, на простом примере:
Скажем есть некий класс, который создает расписание, а другой класс его отображает (их как правило много, скажем один для десктоп-приложения, другой для веба и т.д.).
Если бы мы ничего не знали о IoC, DI мы бы написали что-то вроде этого:
Вроде бы все хорошо, код решает поставленную задачу, но что если мы захотим в последствии изменить реализацию менеджера расписаний и/или же иметь несколько таких менеджеров и динамически их заменять. Тогда в последствии нам придется менять и что-то в ScheduleViewer, а значит и снова его тестировать.
К счастью, разработчики, ленивые люди в хорошем смысле этого слова, и не любят делать одно и тоже дважды.
Мы, например, воспользуемся внедрением зависимостей (DI) для того, чтобы разорвать этот клубок стальных ниток — сделаем связь между этими классами более слабой, добавив прослойку в виде интерфейса IScheduleManager. И будем разрешать ее одним из способов техники DI, а именно Constructor Injection (помимо этого есть Setter Injection и Method Injection — если в двух словах, то везде используется интерфейс вместо конкретного класса, например в типе свойства или в типе аргумента метода):
И далее там где мы хотим воспользоваться нашим классом для отображения расписания мы пишем:
Вот уже почти идеально, но что если у нас много различных ScheduleViewer, разбросанных по проекту, которые использует всегда именно ScheduleManager (придется его руками каждый раз создавать) и/или мы хотим как-либо настроить поведение, так что бы в одной ситуации везде использовать ScheduleManager, а в другой скажем AnotherScheduleManager и т.д.
Решить эту проблему как раз и призваны IoC-контейнеры.
IoC-контейнеры
Они помогают уменьшить количество рутины, позволяя задать соответствие между интерфейсом и его конкретной реализацией, чтобы потом везде этим пользоваться.
Как я уже говорил выше, мы будем рассматривать это на примере Ninject —
1. Сначала мы создаем конфигурацию контейнера:
Теперь везде где требуется IScheduleManager будет подставляться ScheduleManager.
2. Создаем сам контейнер, указывая его конфигуратор:
Контейнер сам создаст экземпляр класса ScheduleManager, вызовет конструктор ScheduleViewer и подставит в него свежесозданный экземпляр ScheduleManager.
Урок 2: Введение в Spring IoC контейнер
Этот урок освещает работу с Spring Framework IoC контейнером и основан на оригинальной документации §5. The IoC container.
Что вы создадите
Вы создадите некоторое количество классов, в которых будет рассмотрена функциональность Spring Framework IoC контейнера.
Что вам потребуется
Настройка проекта
Введение
Inversion of Control (IoC), также известное как Dependency Injection (DI), является процессом, согласно которому объекты определяют свои зависимости, т.е. объекты, с которыми они работают, через аргументы конструктора/фабричного метода или свойства, которые были установлены или возвращены фабричным методом. Затем контейнер inject(далее «внедряет») эти зависимости при создании бина. Этот процесс принципиально противоположен, поэтому и назван Inversion of Control, т.к. бин сам контролирует реализацию и расположение своих зависимостей, используя прямое создание классов или такой механизм, как шаблон Service Locator.
Описание работы IoC контейнера
ApplicationContext представляет собой Spring IoC контейнер и необходим для инициализации, настройки и сборки бинов для построения приложения.
В метаданных конфигурации разработчик описывает как инициализировать, настроить IoC контейнер и собрать объекты в вашем приложении. В данном и других уроках этого цикла везде, где возможно, будет использоваться подход на основе аннотаций и Java-конфигурации. Если вы сторонник XML-конфигурации, либо хотите посмотреть как делать тоже самое через XML, обратитесь к оригинальной документации по Spring Framework или соответствующего модуля/проекта.
Настройка IoC контейнера
Как вариант, можно инициализировать контекст(ы) таким образом:
Использование @Bean аннотации
Теперь, для того, чтобы объект с типом GreetingService был доступен для использования, необходимо описать его в конфигурации следующим образом:
А для того, чтобы использовать его, достаточно выполнить следующее:
Метод getBean() может принимать в качестве аргумента как класс(как показано выше), так и названия бина(подробнее будет рассмотрено ниже), либо другие варианты, с которыми вы можете ознакомится в документации. Однако такой подход не рекомендуется использовать в production-конфигурациях, т.к. для подобных целей существует механизм Dependency Injection (DI), собственно говоря, для чего и предназначен Spring IoC контейнер. Использование DI будет рассмотрено ниже в отдельной главе.
Иногда полезно предоставить более подробное описание бина, например, в целях мониторинга. Для этого существует аннотация @Description :
Жизненный цикл бина
При совместном использовании методов, интерфейсов и аннотаций, описанных выше, учитывайте их порядок вызовов. Для методов инициализации порядок будет следующий:
Для методов разрушения порядок будет следующий:
Если вам необходимо реализовать свою собственную модель жизненного цикла бина, то в таком случае бин должен реализовывать один из интерфейсов, приведенных ниже:
После этого у вас появятся результаты работы методов при разрушении бина. Однако стоит заметить ещё раз, что это относится к обычным приложения, не относящимся к web-приложения(поскольку для них применяется отдельный тип контекста и подобный метод в них уже есть).
Области видимости(scopes) бинов
Когда вы создаете определение бинов, вы вы создаете рецепт для создания экземпляров класса, который определяет бин. Важно понять, что определение бинов является рецептом, потому что он означает, какого класса вы можете создать множество экземпляров по этому рецепту.
Использование @Configuration аннотации
Кода бин имеет зависимость от другого бина, то зависимость выражается просто как вызов метода:
В большинстве случаев, имеются такие случаи, когда бин в одной конфигурации имеет зависимость от бина в другой конфигурации. Поскольку конфигурация является источником определения бинов, то разрешить такую зависимость не является проблемой, достаточно объявить поле класса конфигурации с аннотацией @Autowired (более подробно оисано в отдельной главе):
При этом LessonsConfiguration остается без изменений:
Процесс разрешения зависимостей
IoC контейнер выполняет разрешение зависимостей бинов в следующем порядке:
Spring контейнер может разрешать зависимости между бинами через autowiring(далее, автоматическое связывание). Данный механизм основан на просмотре содержимого в ApplicationContext и имеет следующие преимущества:
Соответственно, у одной из реализации GreetingService должна быть установлена соответствующая аннотация @Qualifier :
Spring также поддерживает использование JSR-250 @Resource аннотации автоматического связывания для полей класса или параметров setter-методов:
Использование стандартных JSR-330 аннотаций
Spring Framework поддерживает JSR-330 аннотации. Эти аннотации работают таким же способом, как и Spring аннотации. Для того, чтобы работать с ними, необходимо добавить в pom.xml следующую зависимость:
Ниже приведена таблица сравнения JSR-330 и Spring аннотаций для DI:
Работа с IoC-контейнером в Spring
В этой статье рассказывается о шаблонах проектирования, на которых строится работа со Spring-контейнером и о том, каким классом он представлен во фреймворке Spring.
Мы создадим простейший пример, в котором конфигурируются бины, создается контейнер и из него извлекаются готовые бины.
Инверсия управления (Inversion of Control) – что это
Ключевая особенность приложения, написанного на Spring, состоит в том что большую часть объектов создаем не мы, а Spring. Мы лишь конфигурируем классы (с помощью аннотаций либо в конфигурационном XML), чтобы «объяснить» фреймворку Spring, какие именно объекты он должен создать за нас, и полями каких объектов их сделать. Spring управляет созданием объектов и потому его контейнер называется IoC-контейнер. IoC расшифровывается как Inversion of Control. А объекты, которые создаются контейнером и находятся под его управлением, называются бинами.
Иллюстрировать это можно так:
В общем на вход контейнер Spring принимает:
А на выходе он производит объекты – бины. То есть экземпляры классов, созданные в соответствии с конфигурацией и внедренные куда нужно (в другие бины). После этого никакие операторы new нам не понадобятся, мы будем работать в классе-бине с его полями-бинами так, будто они уже инициированы. Конечно, не со всеми полями, а только с теми, которые сконфигурированы как бины. Остальные инициализируются как обычно, в том числе с помощью оператора new.
Что такое внедрение зависимости (Dependency Injection)
Внедрение зависимости — это и есть инициализация полей бинов другими бинами (зависимостями).
Ведь помимо создания объектов, Spring-контейнер внедряет эти объекты в другие объекты, то есть делает их полями других объектов. Иногда это выглядит магически – например, контейнер способен внедрить зависимость в поле с модификатором private, для которого нет сеттера. Как же код Spring может это сделать? Дело в том, что под капотом он использует рефлексию, так что это реально. Но эти детали для нас как разработчиков не важны, главное знать, как объяснить фреймворку, какие объекты вы хотите отдать под его управление, и в какие поля других объектов вы хотите их внедрить.
Кстати, шаблон Dependency Injection не привязан к Spring, это всего лишь инициализация поля класса. Это такая обычная вещь и так часто встречается в коде (через конструктор либо сеттер), что даже странно выделять ее в отдельный шаблон. В связи со Spring это название мелькает часто наверно потому, что внедрение выполняет Spring, и у программиста тут много возни с конфигурацией зависимостей. Но с другой стороны, создание и внедрение Spring-ом — это уже другой шаблон — инверсия контроля (IoC).
Класс ApplicationContext для работы с IoC Контейнером
Конфигурация Maven
Чтобы иметь возможность работать с контейнером, добавьте в pom.xml зависимость:
Последнюю версию зависимости можно взять тут.
Класс ApplicationContext
Для работы с контейнером существует не один класс. Но удобнее всего работать с классом ApplicationContext. Чтобы инициализировать контейнер и создать в нем бины, нужно создать экземпляр класса ApplicationContext.
Как уже сказано, контейнеру для создания бинов. требуется конгфигурация, так что конструктор контейнера принимает аргумент. Существуют два подкласса ApplicationContext: ClassPathXmlApplicationContext берет конфигурацию из XML-файла, а AnnotationConfigApplicationContext – из аннотаций:
Так что контейнер можно создать, используя именно тот вид конфигурации, которая используется в вашем приложении. Обычно это аннотации, XML немного устарел.
В конфигурации прописаны как исходные классы, которые надо сделать бинами, так и их зависимости, а также каким образом внедрить эти зависимости. Обычно зависимость внедряется либо через конструктор, либо через сеттер, в зависимости от дизайна класса.
После того, как мы инициализировали контейнер, и он создал бины, появляется возможность их получить непосредственно из контейнера. Хотя это не всегда нужно, но можно. Делается это так:
Мы получили бин типа Animal из контейнера в предположении, что он сконфигурирован в нашем приложении. Давайте теперь его на самом деле сконфигурируем.
Конфигурация бинов с помощью аннотаций
Задавать конфигурацию будем с помощью аннотаций, поскольку это более современный и удобный способ.
Во-первых, создадим класс конфигурации и аннотируем его с помощью @Configuration:
Во-вторых, допустим, у нас есть класс Animal, который мы хотим делать бином:
Чтобы сделать Animal бином, создадим в классе конфигурации метод,который создает и возвращает Animal, и аннотируем этот метод с помощью @Bean:
Все, бин сконфигурирован. Теперь экземпляр Animal будет создаваться контейнером автоматически, и мы сможем получить его из контейнера, как показано выше.
Возможно вы спросите, в чем смысл этого, ведь все равно мы создаем экземпляр Animal отдельным методом. Где же польза контейнера?
Да, мы прописываем метод, создающий Animal. Но мы не вызываем этот метод. Мы просто создаем контейнер. А метод вызывается контейнером во время его инициализации. И учитывая то, что бины обычно конфигурируются однотипно, выгода есть. К тому же они обычно создаются контейнером в единственном экземпляре (хотя это зависит от конфигурации) и внедряются в поля других бинов согласно конфигурации автоматически.
Причем вместо создания аннотированного @Bean метода, можно аннотировать класс Animal изнутри аннотацией @Component – а это и вовсе одна строчка.
О том, как сконфигурировать внедрение бинов в поля других бинов, читайте статью про внедрение зависимостей.
Разница между аннотациями @Bean и @Component в том, что @Bean более гибкая аннотация, ею мы аннотируем метод, а не класс:
Давайте сконфигурируем второй бин Man с помощью аннотации @Component:
Только учтите, что если мы аннотируем классы с помощью @Component, то в файл конфигурации надо добавить аннотацию @ComponentScan для автоматического поиска этих аннотированных классов. В ней надо указать имя пакета или нескольких пакетов, в который лежат эти классы. В этом пакете контейнер будет пытаться их найти автоматически.
Давайте добавим в файл конфигурации одну строчку с аннотацией @ComponentScan и именем пакета для поиска бинов:
Все бины, аннотированные с помощью @ComponentScan, должны лежать в пакете «ru.javalang.ioc».
Проверим, что оба бина действительно создаются:
Мы рассмотрели, что такое IoC-контейнер, как его создать и как из него получить бины. Также мы узнали, что можно сконфигурировать бины с помощью аннотаций @Bean и @Component.
Исходый код примера для этой статьи доступен на GitHub.
Подготовка к 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.
Как обычно, просьба присылать правки или найденные ошибки в личку.