Spring Lessons. Введение в Spring Framework.

Spring — это фреймворк для создания веб-приложений. На настоящий момент Spring — это почти стандарт, который используется для написания больших и сложных корпоративных приложений. Spring — это успешный open-source проект, который развивался отдельно от доминирующей когда-то Java EE. В чем преимущество Spring’а? В первую очередь в его идеологии — программирования на базе POJO (Plain Old Java Objects) — простых Java-объектов, которые содержат минимально только то, что им нужно, не реализуя никаких бизнес-интерфейсов сверх этого. Другими важными частями Spring является понятие IoC(Inversion Of Control) — контейнера и AOP(aspect oriented programming) — аспектно-ориентированное программирование. Если разобраться с тем, что это такое, то вы уже на правильном пути.

Внедрение зависимостей.

Внедрение зависимостей(dependency injection) — это специфическая реализация идеи инверсии управления. Любое приложение — это набор объектов различных классов. Эти объекты постоянно взаимодействуют друг с другом. И любому из них может для его работы потребоваться ссылка на другой объект. Например, у нас есть класс Bard — бродячий музыкант, путешествующий по миру и не знающий ничего лучше песен и музыки:

В конструкторе класса Bard создает объект типа Guitar — музыкального инструмента, который использует бард, чтобы сыграть свою мелодию. И все вроде бы хорошо. Но что если наш бард захочет для игры использовать другой инструмент? Например, лютню. Как быть в этом случае? Проблема в том, что наш класс Bard оказался сильно привязан к объекту Guitar. И ничего с этим не поделать. К тому же, с точки зрения логики, наш класс берет на себя лишнюю обязанность в виде создания объекта класса Guitar. Бард не производит музыкальные инструменты — его задача исполнить красивую песню и порадовать зрителей. Такая проблема без использования фреймворков для инжекции зависимостей обычно решается созданием некоторого общего интерфейса, например, Instrument (представляющего музыкальный инструмент), который реализуется конкретными подклассами — например, Guitar и Lute. Объект класса, представляющий нужный барду инструмент, передается через его конструктор:

Теперь бард просто играет на том инструменте, который получит. Он эти инструменты не создает и жесткая привязанность к одной конкретной реализации исчезает. Также данный класс теперь можно спокойно протестировать. В принципе так подобная задача и решается без использования фреймворков для внедрения зависимостей. Этот метод носит название внедрение  через конструктор. Данный пример взят из статьи, посвященной фреймворку DI для Android — Dagger 2.

Код класса Guitar:

Код класса Lute:

 

В случае DI ссылка на нужную зависимость предоставляется третьей стороной — контейнером. Контейнер за вас создает нужный компонент и управляет его жизненным циклом, что уменьшает связанность вашего кода.  Одной из реализацией контейнера в Spring является контекст приложения. Контейнер должен знать, о том, какие компоненты существует в системе и когда их создавать. Эта информация содержится в определении контекста. Определение контекста может быть загружено из конфигурационного файла XML. Например, наш конфигурационный файл config.xml мог выглядеть таким образом:

Каждый компонент описывается внутри тегов <bean></bean>. Атрибут id — это уникальный идентификатор для компонента. Атрибут class задает тип компонента. Наш бард при рождении получает музыкальный инструмент, который передается в качестве аргумента через конструктор. С помощью записи <constructor-arg ref=»guitar»> мы говорим контейнеру: при создании объекта вызови этот публичный конструктор и передай ему ссылку(ref) на компонент с именем guitar(компонент типа Guitar с id=»guitar» описан в файле ниже).

Теперь контейнер будет знать о всех необходимых компонентах в системе. А объект класса Bard будет получать через конструктор нужный инструмент, заданный в конфигурационном файле. Все, что нам теперь осталось сделать — это загрузить определение контекста и «попросить» контейнер предоставить нам объект класса Bard, что услышать знакомую мелодию:)

Первым делом создадим класс Main с точкой входа в приложение, обычным методом main():

Мы загружаем определение контекста приложения из файла, который находится в CLASSPATH. Мы также могли загрузить его из файла(объект FileSystemApplicationContext) или из веб-приложения(XmlWebApplicationContext). После этого мы методом getBean() пытается  получить нужный нам компонент по его id(который мы прописали в файле config.xml). Контейнер создает компонент за нас, предоставляя на него ссылку. После этого мы можем вызывать методы и использовать его также, как и любой другой объект в обычном Java-приложении. Чем мы и пользуемся. В итоге в консоли вы должны увидеть соответствующее сообщение: «PLAY Guitar!» или «PLAY Lute!», в зависимости от того, ссылку на какой объект вы внедрили через конструктор.

Использование аннотаций.

Вместо использования xml-файла мы могли воспользоваться аннотациями. В примере я использую аннотации из стандарта JSR330, посвященному dependency injection в Java. Элемент, который мы хотим инжектировать, можно пометить аннотацией @Inject. Теперь вы можете убрать элемент <constructor-arg> из конфигурационного файла. И немного изменить класс Bard:

Но если сейчас просто запустить пример, то он не запустится. И вот почему. У нас существует два компонента типа Instrument, которые представлены классами Guitar и Lute. Как Spring узнает, какой именно из них нужно внедрить? Для разрешения неоднозначностей существует еще одна аннотация — @Named, которая позволяет уточнить тип инжектируемого компонента. Например, мы хотим, чтобы наш бард играл на гитаре:

Теперь контейнер успешно справиться с этими неоднозначностями, а бард получит желанную гитару! В Spring существует свой аналог аннотаций @Inject и @Named — это аннотации @Autowired и @Qualifier. Возможности последней чуть шире, чем @Named, которая определена с помощью @Qualifier.

И последнее: по умолчанию аннотации отключены, а для того, чтобы их включить, добавьте элемент <context:annotation-config /> в файл конфигурации. Теперь он выглядит так:

Аспектно-ориентированное программирование (AOП).

В реальных программах часто существуют такие вспомогательные задачи как логирование или безопасность. Эти задачи могут выполняться для любого из существующих компонентов системы. Но должны ли об этом знать сами компоненты? Принцип единственной обязанности SOLID говорит о том, что компоненты должны выполнять только свою задачу. Очевидно, что ведение журнала записей — это общесистемная задача. Такие задачи также называют сквозными.

Для выполнения дополнительной работы, представляющей собой описание логики аспекта, в Spring используются прокси-объекты. То есть тот объект, к которому должен быть применен аспект, оборачивается в прокси-класс. Прокси-объект делегирует все методы реальному объекту после того, как выполнит ту самую дополнительную задачу.

proxy-pattern

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *