К основному контенту

IoC-контейнер изнутри

В этой статье предлагаю разобраться чем занимается IoC-контейнер и как это определяет его внутреннюю структуру. Исходить будем из задачи, которую решает контейнер: создать и скомпоновать объекты таким образом, чтобы все зависимости этих объектов были удовлетворены.

Очевидно, что зависимости необходимо некоторым образом описать: объект, называемый так-то, скажем A, зависит от других объектов, например B и C. При этом контейнер ничего не знает о том, какой из объектов мы называем A, а какие — B и C. Это тоже необходимо описать. Контейнер оперирует только теми компонентами, которые он знает (часто их называют бинами). Для создания объекта контейнеру необходимо также знать к какому классу объектов он принадлежит и как долго его помнить. Самый простой способ предоставить все эти данные (с точки зрения устройства контейнера, конечно) — напрямую обратиться к регистрационному методу контейнера, передав ему в качестве параметра некоторый объект с метаданными. Второй вариант: промаркировать наш код некоторым образом, понятным контейнеру. (Стоит помнить, что это приводит к зависимости нашего кода от используемого контейнера.) В этом случае у контейнера должен быть сканер, который найдет весь маркированный код и составит из маркировки и кода необходимое описание. Пользователю контейнера может быть удобно задать метаданные в некотором внешнем источнике и тогда контейнеру необходим компонент, который сможет прочитать метаданные из этого источника. Итого, контейнеру необходим транслятор, который примет от пользователя описание компонентов и их зависимостей и переведёт его во внутреннее представление контейнера.

Компоновка производится путём внедрения одних объектов в другие. Внедрение может происходить как во время создания объекта (зависимости передаются в конструктор) так и после него (зависимости передаются в сеттеры или другие сконфигурированные методы). Внедряться может как один объект так и некоторое их множество (например, все объекты, реализующие определённый интерфейс). Зависимости могут быть обязательными и необязательными. Вариантов внедрения не так уж много и их удобно описать набором стратегий разрешения и внедрения зависимостей.

Имея описание компонентов, их зависимостей и стратегии их внедрения можно переходить к собственно созданию объектов, для чего контейнеру понадобится фабрика. В большинстве случаев создание объекта сводится к вызову его конструктора и сеттеров, в которые передаются ранее созданные зависимости. Дополнительно может вызываться сконфигурированный метод инициализации. Иногда возникает необходимость сконструировать объект определённым способом (например, используя шаблон “строитель”). В этом случае пользователь описывает / регистрирует в контейнере свою фабрику, которой основная фабрика контейнера будет делегировать создание объекта в описанных случаях.

В зависимости от указанного в метаданных времени жизни объект после создания помещается в реестр объектов с таким же временем жизни (реестр - условное название, в коде конкретной реализации ориентируйтесь на scope). Контейнер обращается за объектом сначала в соответствующий реестр, а потом, если нужный объект отсутствует, к фабрике. Объекты находятся в реестре до его закрытия / уничтожения. Контейнер может предоставлять возможность пользователю задать метод объекта, который вызовется перед тем как объект будет забыт.

Надеюсь, эта статья демистифицирует IoC-контейнер в ваших глазах. Я намеренно не привязывался к какой-либо конкретной реализации контейнера и предполагаю, что в деталях вы сможете разобраться сами. В учебных целях могу порекомендовать посмотреть на IoC-контейнер Petite, который входит в состав легковесного фреймворка Jodd. Он достаточно функциональный, но проще того, который предоставляется Spring фреймворком.


Комментарии

Популярные сообщения из этого блога

Одно приложение, несколько баз данных

Рецепт от Spring Boot Некоторое время назад мне довелось писать агрегатор информации, разбросанной по нескольким базам данных с разными схемами. Для реализации был выбран Spring Boot. Ну, потому что модный и судя по примерам существенно упрощает жизнь за счет умной автоконфигурации. В этой статье я опишу, что же необходимо сконфигурировать и как, в случае, если вы отошли от стандартного сценария. Первым делом, необходимо прописать настройки доступа к каждой из баз. Например, вот так: Следующим шагом создадим отдельный класс конфигурации (для удобства), в котором определим dataSources: Обратите внимание, как просто получить настройки с помощью @ConfigurationProperties. Правда, пришлось ввести вспомогательный класс BaseDataSourceProperties — наследник DataSourceProperties, в котором область видимости метода getDriverClassName расширена до public. И осталось совсем немного — сконфигурировать JPA-репозитории. Насчет немного я, конечно, пошутил :) В этой части предстоит больше

Может ли TreeSet содержать дубликаты?

Не спешите давать ответ, а лучше запустите код, приведенный ниже. Возможно вы подумаете, что это надуманный пример, но это лишь немного упрощенная версия того кода, с которым мне пришлось столкнуться на практике. Для начала о проблеме, которую был призван решить этот код. В списке объектов надо было оставить объекты с различными именами, причем неважно какой объект из всего набора с таким же именем останется. Сразу же уточню, что на практике в проекте используется Java 7 и класс объектов, добавляемых в TreeSet, не вложен в какой-либо другой, а приведенный здесь вариант использован для краткости. Может возникнуть вопрос почему выбрано именно такое решение. Давайте разберемся, чтобы не повторять ошибку. Первое, что приходит в голову, когда из списка надо удалить дубликаты, - использовать Set. В данной ситуации HashSet не подходит, потому что он использует equals метод для сравнения и отсеивания дубликатов. Но наш программист не сдаётся и вспоминает, что есть же еще TreeSet, в докум

Обработка изображений с ImageMagick

ImageMagick ( http://www.imagemagick.org ) — набор утилит для создания, редактирования, конвертирования и просмотра растровых изображений. Графический режим необходим только для просмотра. Для остальных действий над изображениями достаточно консоли. То есть налицо два отличия от привычных редакторов растровых изображений (вроде GIMP или Krita): использование набора утилит вместо одной программы для операций над изображениями не требуется GUI. Очевидно, что таким инструментом вряд ли будут пользоваться художники, фотографы или дизайнеры. Чтобы разобраться для кого предназначен этот набор, предлагаю ознакомиться с предоставляемыми возможностями. Что умеет ImageMagick? Чтобы ответить на поставленный вопрос я перечислю входящие в набор утилиты, напишу какой функционал предоставляет каждая из них и, конечно же, приведу примеры использования. identify — информационная утилита, воспользовавшись которой можно узнать формат изображения и множество других его свойств (например, высоту,