ru:https://highload.today/blogs/spring-framework/ ua:https://highload.today/uk/blogs/spring-framework/
logo
Инструменты      28/10/2021

Как Spring упрощает жизнь разработчика: что нужно знать о фреймворке

Олег Стрелянный BLOG

Java Developer в NIX

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

Фреймворк Spring включает в себя два десятка модулей и, конечно, уместить все в одной статье не получится. В этом материале я расскажу об основных модулях (Core):

Главный принцип философии Spring — минимальное воздействие. На официальном сайте фреймворка говорится, что Spring делает программирование на Java быстрее, легче, проще, безопаснее и самое главное — продуктивнее. Давайте попробуем разобраться, что имели ввиду авторы этих строк.

Базовая терминология для работы со Spring

Человека, знакомящегося со Spring, может испугать обилие новых терминов. И авторы статей «для начинающих» иногда оставляют объяснения этих терминов за скобками, полагая, что это уже все знают или и так поймут из названия. Но я считаю, не лишним будем проговорить эти понятия:

Фреймворк — платформа (заготовка, каркас), которая собирает в себе готовые решения для типичных задач разработчика по настройке приложения. Любой может добавить фреймворк в свое приложение и сфокусироваться на написании его основной логики.

Зависимость (Dependency) — в контексте Java-приложения зависимостью называют класс B, без которого не может функционировать класс А.

Аннотация — «метка» в исходном коде, которая сама по себе является метадатой, которую потом может использовать компилятор/интерпретатор или же, как в нашем случае, фреймворк.

Онлайн курс UI/UX Design Pro від Ithillel.
Навчіться проєктувати інтерфейси з урахуванням поведінки користувачів, розв'язувати їх проблеми через Customer Journey Mapping, створювати дизайн-системи і проводити дослідження юзабіліті, включаючи проєктування мобільних додатків для Android та iOS і розробку UX/UI на основі даних!
Дізнатися більше

Бин (Bean)— класс в языке Java, написанный по определенным правилам. Java-бины используются для объединения нескольких объектов в один, благодаря чему достигается удобство в передачи данных.

Также в статье будут часто использоваться термины инверсия контроля (Inversion of Control, Ioc) и внедрение зависимостей (Dependency Injection, DI). Для их объяснения недостаточно просто определения, поэтому детально рассмотрим их ниже.

IoC и DI

Часто, когда дают определение Spring, говорят, что это IoC-контейнер. Несмотря на то, что это одна из ключевых функциональностей Spring, такое определение почти ничего не объясняет о нем. Давайте основательно разберемся, что такое IoC и зачем она нужна. Предлагаю сначала выяснить это на примере приложения без Spring.

Инверсия контроля (Inversion of Control — IoC ) — один из популярных принципов объектно-ориентированного программирования (ООП). Она позволяет повысить модульность и расширяемость программы за счет снижения зависимостей между ее компонентами.

Но что значит наличие зависимостей в программировании? Если класс А использует класс В, он становится зависимым от него. По схожему принципу происходит зависимость автомобиля от двигателя, компьютера от процессора. В ООП под зависимостью понимают класс, без которого не может функционировать код другого класса.

Как это выглядит в коде

Давайте выразим в коде пример зависимости уже упомянутых выше машины и двигателя. Первую версию кода напишем без использовании инверсии контроля:

Код

Код без инверсии контроля

Здесь видим, что у нас есть класс Engine в котором имеется метод running (предположим, он запускает двигатель). Чтобы осуществить метод drive, нам необходимо сперва проинициализировать экземпляр зависимого класса Engine (проверить, все ли нормально с нашим двигателем).

Основные недостатки такого подхода:

  • сильная связанность между классами (невозможно разрабатывать класс Car, не делая изменений в классе Engine и наоборот);
  • Онлайн-курс "Фінансовий директор" від Laba.
    Опануйте інструменти управління грошовими потоками, ризиками та активами компанії, щоби перейти на посаду CFO.
    Приєднатися до курсу
  • такое приложение тяжело изменять, а значит его сложно расширять (поддерживать и обновлять).

Этот код можно немного улучшить, добавив интерфейс:

Добавим интерфейс

Добавим интерфейс

Так удастся понизить связность, и у нас появится возможность подставить новые реализации (V8Engine), не изменяя при этом код в методе drive. Но нам все так же необходимо внимательно следить за тем, какую реализацию мы выбрали. А это изрядно усложняет задачу: если реализаций станет слишком много, в них будет легко запутаться.

Что предлагает сделать инверсия контроля? Вынести создание зависимостей за пределы нашего класса, чтобы не прописывать каждый раз его новые элементы через new. IoC дает возможность вызывать новые элементы класса извне, не меняя при этом исходный код.

Инверсии контроля можно достичь различными способами, но в Spring чаще всего применяются способы Dependency Injection (DI; от англ. «внедрение зависимостей»). Рассмотрим именно их.

Давайте добьемся инверсии контроля при помощи DI-метода: Setter Injection (пока что без Spring Framework):

Setter Injection, пока что без Spring Framework

Setter Injection, пока что без Spring Framework

Что изменилось в коде? Был написан специальный метод под названием setEngine, в который мы передали наш двигатель. Теперь нам не нужно вручную создавать каждый новый объект класса engine, а можно передать его в наш класс извне с помощью метода setEngine (как он там создастся — это уже не задача нашего класса).

Внедрение зависимостей можно также осуществить с помощью Construction Injection, где аргументы будут переданы через конструктор:

Внедрение зависимостей с помощью Construction Injection

Пример без Spring Framework, но с инверсией управления Constructor Injection

Как видим, инверсия контроля позволила нам уменьшить количество связей, в результате чего класс стало легче изменять и расширять.

Вместе с этим осталась нерешенной следующая проблема: новые классы по прежнему нужно создавать при помощи оператора new (хотя теперь это и делается снаружи исходного кода):

Курс-професія "Motion Designer" від Skvot.
Навчіться створювати 2D- та 3D-анімації у софтах After Effects, Cinema 4D та Octane Render. Протягом курсу ви створите 14 моушн-роликів, 2 з яких — для реального клієнта.
Детальніше про курс

Давайте рассмотрим пример того, как Spring может справиться с этой проблемой и какие инструменты мы можем использовать.

Добавляем Spring Framework в приложение

Любой ресурс (класс) в Spring называется бином (Bean). Это название взято по аналогии с JavaBeans — классами в языке Java, написанными по определенным правилам (наличие конструктора без параметров, геттеров/сеттеров). Бины в Spring не ограничены такими строгими правилами, но они очень похожи на своих Java-собратьев.

Чтобы добавить Spring в приложение, вам необходимо:

  • создать Spring-конфигурацию;
  • задать контекст Spring — под контекстом в этом случае стоит понимать интерфейс ApplicationContext. Он содержит информацию о бинах и их зависимостях, а также общие данные о конфигурации приложения;
  • получить бины из IoC-контейнера.

Помните, что выбор конфигурации меняет реализацию интерфейса. Ниже разберем несколько примеров возможных конфигураций на Spring.

XML-конфигурация на Spring

Код приложения с использованием Spring Framework будет выглядеть точно так же, как и без него. В этом и заключается то самое «минимальное воздействие», характерное для Spring.

Пример приложения на Spring Framework

Пример приложения на Spring Framework

XML-конфигурацию этого кода можно представить следующим образом:

XВ первой части кода есть масса ссылок. Так в Spring выглядит пространство имен. Но давайте сосредоточим внимание на выделенной области кода. Здесь можно увидеть объявление наших бинов:

Онлайн-курс "Арт Менеджер" від Skvot.
Навчіться шукати фінансування та планувати бюджет, керувати командою, запускати артпроєкти та пітчити їх так, щоб великі компанії захотіли колабитися.
Детальніше про курс
  1. Первый бин — engine. Запрашивая его, можно, например, получить экземпляр класса V8Engine.
  2. Второй бин — car. В этом бине видна зависимость от бина engine, которую внедрили через конструктор (это также можно сделать через сеттер).

Создадим новый класс new PathXmlAplicationContext и передадим ему в аргументы нашу конфигурацию config.xml. Теперь попросим Spring предоставить нам бин car:

Пример приложения на Spring Framework — XML-конфигурация

Пример приложения на Spring Framework — XML-конфигурация

На этом этапе Spring производит скрытое внедрение зависимостей. Прочитав конфигурацию config.xml, фреймворк будет знать, что для класса carсуществует зависимость от класса engine. Разработчику больше не придется дополнительно прописывать ее в коде.

Annotation-конфигурация на Spring

Рассмотрим создание конфигураций на Spring при помощи аннотаций (annotation). Они помогут Spring разобраться, где именно существуют зависимости, и что ему нужно с ними делать. Уберем из нашего config.xmlвсе упоминания о бинах и пропишем необходимость сканирования пакета на наличие аннотации @Component:

Пример приложения на Spring Framework — Annotation конфигурация

Пример приложения на Spring Framework — Annotation конфигурация

Теперь Spring будет автоматически воспринимать бины как классы, отмеченные аннотацией @Component. Места внедрения зависимостей отметим другой аннотацией: @Autowired:

Пример приложения на Spring Framework — Annotation конфигурация

Пример приложения на Spring Framework — Annotation конфигурация

Начиная с версии Spring 4.3, аннотацию @Autowired необязательно ставить на конструктор. Если у нас в коде лишь один конструктор, Spring автоматически определит его как место внедрения зависимостей. Но в случае установки аннотации @Autowired на сеттер ее всегда следует прописывать явно.

Java-конфигурация на Spring

Рассмотрим пример Java-конфигурации на Spring. Полностью удалим наш config.xmlи создадим конфигурацию при помощи обычного Java-класса. На него необходимо повесить аннотацию @Configuration и затем внутри кода объявить все используемые бины.

Пример приложения на Spring Framework — Java конфигурация

Пример приложения на Spring Framework — Java конфигурация: слева — конфигурация, справа — код

В этом типе конфигурации все внедрения зависимостей описываются вручную, что и является его главным недостатком. Параллельно с этим видим, что наши классы полностью очистились от аннотаций и теперь, чтобы использовать их, нам просто необходимо поменять реализацию ApplicationContext.

XML или Annotation?

Какой же подход лучше всего подойдет для создания конфигураций на Spring: XML или аннотации? На мой взгляд, это вопрос вкуса:

Онлайн-курс "Комунікаційний менеджер" від Skvot.
Ви отримаєте скіли комунікації, сформуєте CV та розробите власну one page strategy. Для своєї карʼєри та успішного масштабування бренду.
Програма курсу і реєстрація
  • С одной стороны, практично выносить конфигурации приложения за пределы кода, как это происходит в XML-конфигурации. В таком случае вы избегаете смешения бизнес-логики приложения с его настройкой.
  • С другой стороны, именно аннотации позволяют наблюдать конфигурацию внутри Java-кода. Так вы сразу увидите, какой класс является компонентом и какие у него существуют зависимости.

В любом случае важно помнить, что конфигурации полностью взаимозаменяемы и не отличаются друг от друга по функционалу. Главное — выбрать один вид конфигурации и придерживаться его во время разработки вашего приложения.

Основные аннотации Spring: внедрение зависимостей с помощью @Autowired

В предыдущем разделе мы определили, зачем нужен Spring, какие проблемы он решает и как подключить Spring Framework в свое приложение. Теперь рассмотрим основной функционал Spring, и как с его помощью можно повлиять на поведение приложения. Давайте пройдемся по самым часто используемым аннотациям.

Самая популярная в Spring аннотация — определенно @Autowired. И разработчики часто пренебрегают ею, используют в неположенном месте и просто надеются на магию Spring. Давайте разберемся, для чего она нужна, и какие правила использования существуют.

Аннотация @Autowired отвечает в Spring за внедрение зависимостей. Ею можно пометить место внедрения (сеттер, поле или конструктор), и Spring автоматически свяжет нужный бин с этим местом. Используем в нашем коде внедрение зависимости через сеттер с помощью @Autowired:

@Autowired

@Autowired

Создадим Java-конфигурацию вместе с аннотацией @ComponentScan — с ее помощью нам больше не нужно будет явно указывать наши бины в коде:

@Autowired

@Autowired

Существует три вида внедрения зависимостей (DI) при помощи @Autowired, и каждая — со своими правилами применения:

  • Constructor Injection;
  • Курс Project Manager від Powercode academy.
    Онлайн-курс Project Manager. З нуля за 3,5 місяці до нової позиції Без знання коду, англійської та стресу.
    Зарееструватися
  • Setter Injection;
  • Field Injection.

Давайте рассмотрим каждый тип зависимостей более детально.

Сonstructor Injection

Внедрение через конструктор следует использовать когда зависимость является обязательной, или ее необходимо сделать неизменяемой (с помощью ключевого слова final).

Благодаря Constructor Injection также легче заметить «суперклассы» — перегруженные классы с большим количеством зависимостей. Если в классе все зависимости подключаются через конструктор, то в глаза сразу бросается большое количество параметров. И у разработчика появится ощущение, что он делает что-то не так.

Setter Injection

Внедрение через сеттер следует использовать, когда зависимость является опциональной.

Setter Injection позволяет делать опциональные зависимости — такие зависимости можно внедрять повторно. Но использование внедрения через сеттер для обязательной зависимости может привести к NullPointerException и остановке приложения. Хоть и существует способ избежать этого с помощью аннотации @Required, все равно следует внимательно следить за этим типом DI.

Setter Injection часто используется в классах, которые должны легко поддаваться реконфигурации. Помимо этого, сеттеры дают возможность определять зависимости в интерфейсе.

Field Injection и основные причины его избегать

Внедрение зависимостей при помощи Field Injection не рекомендуется делать по нескольким причинам:

Бізнес англійська від Englishdom.
Тут навчають за методикою Кембриджу, завдяки якій англійську вивчили понад 1 мільярд людей. Саме вона використовується в найкращих навчальних закладах світу, і саме за нею створені курси.
Інформація про курс
  • при этом типе внедрения нельзя сделать зависимость неизменяемой;
  • плотная зависимость от IoC-контейнера — если вы захотите заменить спринговый IoC-контейнер или просто его убрать, то перед этим придется переписывать много кода;
  • ряд дополнительных сложностей с рефлексией при написании юнит-тестов;
  • слишком простая процедура добавления зависимостей, в результате которой легко создать «суперкласс» и перезагрузить код программы.

Setter или Constructor Injection?

До недавнего времени мнения насчет того, какой тип внедрения зависимости использовать, разнились даже у самих разработчиков Spring Framework. До версии 4.0 команда создателей Spring рекомендовала внедрять зависимости через сеттер. Он объясняли это тем, что большое количество аргументов конструктора может стать очень громоздким. Особенно, когда их свойства являются необязательными.

Но начиная с версии 4.0, команда Spring начала явно выступать за внедрение зависимостей через Constructor. Это позволяет реализовать компоненты приложения как неизменяемые объекты. Таким образом можно гарантировать, что требуемые нам зависимости будут точно проинициализированы.

Вы всегда вправе выбрать любой тип внедрения зависимостей и при необходимости даже смешивать их. Главное помнить, что выбор типа внедрения всегда должен быть основан на ваших потребностях и потребностях разрабатываемого вами ПО.

Bean Scopes и их разновидности

@Scope(“singleton”)

По умолчанию все бины в Spring создаются как singleton — один объект на контейнер. Это означает, что каждый раз, когда вы будете запрашивать у контейнера бин, вам будет приходить одна и та же ссылка с одним и тем же выделенным объектом:

@Scope(“prototype”)

Singleton-подход предназначен для экономии ресурсов при работе в фреймворке и используется в Spring по умолчанию. Это поведение можно изменить, предварительно указав аннотацию @Scope и внедрив через нее prototype scope. Prototype является полной противоположной singleton: при его внедрении для каждой зависимости будет всегда создаваться новый объект:

Онлайн-курс "Директор з продажу" від Laba.
Як стратегічно впливати на дохід компанії, мотивувати сейлзів перевиконувати KPI та впроваджувати аналітику — навчить комерційний директор Laba з 12-річним досвідом у продажах.
Приєднатись до курсу

Скоупы web-приложения

Существует целый ряд бин-скоупов, характерных только для веб-приложений. С их помощью можно ограничивать жизненный цикл экземпляра одним запросом (request), сессией (session), сервлетом  (application) или веб-сокетом (websocket).

Всегда следует помнить: prototype-зависимость работает только для prototype-компонентов. Именно поэтому в singleton-компоненты нельзя внедрять prototype-зависимости. Можно обойти это ограничение с помощью Method Injection.

Жизненный цикл бина: использование @PostConstruct и @PreDestroy

Перед тем, как спринговый бин будет доступен к использованию, он проходит ряд этапов своего «рождения».

Существует две основные аннотации для внедрения в жизненный цикл. Ими помечается метод, который будет внедрен в жизненный цикл.

  • @PostConstruct — метод будет вызван после конструктора;
  • @PreDestroy— метод будет вызван перед уничтожением бина;

В этих методах удобно инициализировать дополнительные ресурсы или же наоборот — подчищать их после себя. Некоторые методы работают только для singleton, либо только для prototype-компонентов. Если мы используем singleton-компонент, то метод будет вызван только один раз. Однако, совсем неочевидно, что метод @PreDestroy невозможно вызвать для prototype-бинов, когда для каждой зависимости создается новый объект.

Без ошибок: аннотация @Qualifier

Если у интерфейса, который внедряется с помощью @Autowired существует несколько реализаций (engine, electric engine и т.п.), возникнет неопределенность, которая может привести к программной ошибке. Этого можно избежать путем прописывания аннотации @Qualifier. Без добавления аннотации @Qualifier Spring не сможет понять, к какой именно реализации мы хотим обратиться.

@Qualifier

@Qualifier

Три компонента одного @Component

У аннотации @Component существует три аналога:

  • @Controler;
  • Онлайн-курс Frontend-разробник від Powercode academy.
    Курс на якому ти напишеш свій чистий код на JavaScript, попрацюєш із різними видами верстки, а також адаптаціями проектів під будь-які екрани. .
    Зарееструватися
  • @Service;
  • @Repository.

На сегодня эти аннотации функционально ничем друг от друга не отличаются. Их просто можно использовать в качестве маркеров вместо @Component. Но разработчики не гарантируют, что в будущем у них не появятся новые функции. К примеру, уже сейчас @Repositoryполучила возможность отлавливать Dao-exceptions для репозиториев.

Если вы не можете ответить на вопрос, для какого конкретно случая вы используете аннотацию из списка выше — просто прописывайте @Component.

Вместо вывода

Про Spring можно рассказывать бесконечно: фреймворк очень объемен и полон различных нюансов. Тем не менее не стоит ошибочно путать массивность Spring с его сложностью. Spring создан, чтобы облегчить разработчику жизнь. При правильном понимании теоретических основ ООП и уделении должного внимания чтению технической документации Spring поможет вам сэкономить уйму времени, которое вы могли потратить на монотонную и ненужную работу.

Для тех, кто хочет знать больше:

Читайте также: Коллекционер хорошего кода: что нужно знать о Java Collections Framework

Онлайн-курс "Computer Vision" від robot_dreams.
Застосовуйте Machine Learning / Deep Learning та вчіть нейронні мережі розпізнавати об’єкти на відео. Отримайте необхідні компетенції Computer Vision Engineer.
Дізнатись більше про курс

If you have found a spelling error, please, notify us by selecting that text and pressing Ctrl+Enter.

Онлайн-курс "Business English" від Laba.
Вивчіть базу граматики, лексики та вокабуляру.Використовуйте англійську в спонтанній розмові з колегами та клієнтами.Прокачайте її до впевненого В1 — для розвитку кар’єри в бізнесі.
Приєднатись до курсу

Этот материал – не редакционный, это – личное мнение его автора. Редакция может не разделять это мнение.

Топ-5 самых популярных блогеров марта

PHP Developer в ScrumLaunch
Всего просмотровВсего просмотров
2434
#1
Всего просмотровВсего просмотров
2434
Founder at Shallwe, Python Software Engineer (Django/React)
Всего просмотровВсего просмотров
113
#2
Всего просмотровВсего просмотров
113
Career Consultant в GoIT
Всего просмотровВсего просмотров
95
#3
Всего просмотровВсего просмотров
95
CEO & Founder в Trustee
Всего просмотровВсего просмотров
94
#4
Всего просмотровВсего просмотров
94
Рейтинг блогеров

Ваша жалоба отправлена модератору

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: