Альтернативой какой концепции java является spring framework
Перейти к содержимому

Альтернативой какой концепции java является spring framework

  • автор:

Использование фреймворков семейства Spring Projects для разработки веб-приложений на платформе Java Текст научной статьи по специальности «Компьютерные и информационные науки»

веб-приложение / платформа Java / аннотации Java / фреймворк / Spring Projects / Spring Frame¬work / архитектура MVC / web application / Java platform / Java annotation / framework / Spring Projects / Spring Framework / MVC architecture.

Аннотация научной статьи по компьютерным и информационным наукам, автор научной работы — Сакович В. В., Кожомбердиева Г. И., Бураков Д. П.

Обсуждаются особенности разработки веб-приложений на платформе Java с использованием фреймворков семейства Spring Projects . Рассматриваются основные концепции Spring Framework : IoC-контейнер, Spring Scopes, Spring MVC и Spring AOP. Освещаются возможности, предоставляемые Spring Boot: запуск приложения, профили, конфигурационные файлы. Кроме того, уделяется внимание организации взаимодействия с базами данных с помощью Spring Data, а также использованию средств авторизации, предоставляемых Spring Security. В качестве демонстрационного примера веб-приложения , разработанного с использованием Spring, представлено клиент-серверное приложение для записи студентов на консультации и управления консультациями.

i Надоели баннеры? Вы всегда можете отключить рекламу.

Похожие темы научных работ по компьютерным и информационным наукам , автор научной работы — Сакович В. В., Кожомбердиева Г. И., Бураков Д. П.

СОВРЕМЕННЫЕ ФРЕЙМВОРКИ ДЛЯ РАЗРАБОТКИ WEB-ПРИЛОЖЕНИЙ
Разработка веб — приложения с использованием Spring Framework и jQuery
РЕАЛИЗАЦИЯ ВЕБ СЕРВИСА С ПРИМЕНЕНИЕМ ПАРАДИГМЫ РЕАКТИВНОГО ПРОГРАММИРОВАНИЯ
СРАВНИТЕЛЬНЫЙ АНАЛИЗ ТЕХНОЛОГИЙ ДЛЯ РАЗРАБОТКИ СЕРВЕРНОЙ ЧАСТИ СИСТЕМЫ УПРАВЛЕНИЯ ПРОДАЖАМИ
SPRING FRAMEWORK В ВЫСОКОНАГРУЖЕННЫХ ПРИЛОЖЕНИЯХ
i Не можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

Using Spring Projects Family Frameworks for Developing Web Applications on Java Platform

The article discusses the features of developing web applications on the Java platform using frameworks from the Spring Projects family. The basic concepts of the Spring Framework are considered: IoC container, Spring Scopes, Spring MVC, and Spring AOP. The possibilities provided by the Spring Boot are covered: application launch, profiles, and configuration files. In addition, attention is paid to the organization of interaction with databases using the Spring Data as well as using of authorization tools provided by the Spring Security. As a demo of a web application developed using the Spring features, the article provides a client server application for signing up students for a consultation and consultation management.

Текст научной работы на тему «Использование фреймворков семейства Spring Projects для разработки веб-приложений на платформе Java»

Использование фреймворков семейства Spring Projects для разработки веб-приложений

на платформе Java

В. В. Сакович, к.т.н. Г. И. Кожомбердиева Петербургский государственный университет путей сообщения Императора Александра I

Санкт-Петербург, Россия lampirg 1@gmail. com, kgi-liizht@yandex. ru

Аннотация. Обсуждаются особенности разработки веб-приложений на платформе Java с использованием фреймворков семейства Spring Projects. Рассматриваются основные концепции Spring Framework: IoC-контейнер, Spring Scopes, Spring MVC и Spring AOP. Освещаются возможности, предоставляемые Spring Boot: запуск приложения, профили, конфигурационные файлы. Кроме того, уделяется внимание организации взаимодействия с базами данных с помощью Spring Data, а также использованию средств авторизации, предоставляемых Spring Security. В качестве демонстрационного примера веб-приложения, разработанного с использованием Spring, представлено клиент-серверное приложение для записи студентов на консультации и управления консультациями.

Ключевые слова: веб-приложение, платформа Java, аннотации Java, фреймворк, Spring Projects, Spring Framework, архитектура MVC.

Широкое распространение веб-приложения получили в начале 2000-х годов в условиях бурного развития сети Интернет и гипертекстовой системы WWW [1]. Вследствие этого в области программной инженерии было выделено даже отдельное направление — веб-программирование, то есть разработка использующих веб-технологии информационных систем, представляющих собой полноценные веб-приложения, или частей этих систем, использующих вебстраницы для представления пользовательского интерфейса.

В настоящей статье рассматриваются особенности использования семейства фреймворков Spring Projects при разработке веб-приложений на платформе Java.

В качестве примера представлено демонстрационное приложение, функциональность которого ориентирована на решение практически значимой в вузовской среде задачи записи студентов на консультации и управления консультациями, а реализация — на показательное применение возможностей и преимуществ семейства фреймворков Spring Projects.

Статья содержит результаты выпускной квалификационной бакалаврской работы В. В. Саковича. Описание фреймворков семейства Spring Projects сопровождается отсылками к конкретным примерам их использования при разработке демонстрационного приложения. Приложение разработано в интегрированной среде разработки IntelliJ IDEA (с использованием в качестве дополнительных инструментов веб-приложения Spring Initializr и средства для

к.т.н. Д. П. Бураков независимый исследователь Санкт-Петербург, Россия burakovdmitry8@gmail.com

сборки проекта Maven) и может рассматриваться как действующий прототип реальной системы.

Веб-приложения и средства их разработки По своей структуре веб-приложения представляют собой приложения, построенные в соответствии с архитектурой «клиент-сервер». Структура типичного приложения представлена на рисунке 1 [2].

Рис. 1. Структура веб-приложения

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

Клиентская часть веб-приложения реализует пользовательский интерфейс, формирует запросы к серверной части приложения и обрабатывает ответы от нее. Серверная часть получает запрос от клиента, выполняет вычисления, после этого формирует веб-страницу и отправляет ее клиенту по сети с использованием протокола HTTP. При этом серверная часть веб-приложения может выступать в качестве клиента других служб, например сервера базы данных или другого веб-приложения, расположенного на другом сервере.

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

HTML, активно используются таблицы стилей CSS и скриптовый язык JavaScript. На серверной стороне веб-приложений в основном применяются средства динамической генерации веб-страниц, например широко известный скриптовый язык PHP. Однако для разработки корпоративных веб-приложений, отличающихся, как правило, сложной структурой и разнообразием сервисов, предоставляемых конечным пользователям, применяются решения, основанные на использовании таких универсальных языков программирования, как Java.

Будучи высокоуровневым кроссплатформенным языком, язык Java используется во многих областях разработки программного обеспечения. При этом в стандартной редакции платформы Java (Java SE) не предусмотрены средства для удобной реализации серверных приложений. Для решения этой проблемы разработчиками Java была создана редакция платформы для разработки корпоративных систем Java EE (Java Platform, Enterprise Edition), включившая в себя такие технологии разработки серверной части веб-приложений, как JSP (Java Sever Pages) и сервлеты. Однако, ввиду того, что Java EE (с 2018 года — Jakarta EE) развивалась довольно медленно, а ее функциональность была относительно низкоуровневой, существовал запрос на альтернативные средства разработки серверных приложений, которые, использовав созданные в Java EE спецификации, позволяли бы создавать негромоздкие, легко масштабируемые, высокоуровневые веб-приложения.

Такой альтернативой стало семейство фреймворков Spring, существенно упростившее создание приложений и вследствие этого завоевавшее популярность среди разработчиков [3]. Данное семейство фреймворков является одним из самых востребованных средств для разработки веб-приложений [4], которое используется в таких компаниях, как Amazon, Google, Microsoft, Netflix и многих других [5].

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

Семейство фреймворков Spring Projects

Изначально под термином Spring подразумевался только Spring Framework — фреймворк с открытым исходным кодом для Java-платформы, появившийся в середине 2000-х годов как альтернатива технологии Enterprise JavaBeans, которая поддерживает разработку серверных компонентов с бизнес-логикой и является частью Java EE. Однако по мере развития этого фреймворка на его основе сформировались новые фреймворки, которые образовали расширенное семейство Spring Projects [6]. Некоторые фреймворки данного семейства представлены на рисунке 2 [7].

Отметим фреймворки семейства, представляющие наибольший интерес в рамках настоящей статьи:

Рис. 2. Семейство фреймворков Spring Projects

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

• Spring Boot — фреймворк, позволяющий быстро создавать и настраивать полноценные и готовые к запуску приложения, основанные на Spring;

• Spring Data — фреймворк для работы с базами данных, работающий с JDBC, JPA, а также NoSQL СУБД (например, MongoDB);

• Spring Security — фреймворк, предоставляющий средства для авторизации и аутентификации, а также базовую защиту от злоумышленных операций.

Для быстрой генерации каркасного проекта приложения, использующего фреймворки семейства Spring Projects, предназначен специальный инструмент — веб-приложение Spring Initializr [8]. Оно позволяет пользователю выбрать инструментальное средство для сборки проекта (например, Maven), версию Spring Boot, версию платформы Java, а также используемые средства семейства Spring Projects (и некоторых других инструментов). На основе выбранных пунктов меню и введенной информации Spring Initializr генерирует готовый zip-архив каркасного проекта приложения.

Возможности фреймворка Spring Framework

Рассмотрим полезные возможности Spring Framework, поддерживаемые ядром и внутренними пакетами этого основного фреймворка семейства. Описание средств, предоставляемых Spring Framework, сопровождается отсылками к примерам их использования В. В. Саковичем при разработке демонстрационного приложения.

Внедрение зависимостей IoC-контейнером

В решениях семейства Spring Projects активно применяется так называемая инверсия управления (Inversion of Control, IoC), которая также иногда называется внедрением зависимостей (Dependency Injection, DI). Это шаблон проектирования, при использовании которого зависимости экземпляров класса (то есть объекты, с которыми они взаимодействуют) определяются на основе аргументов конструктора (или метода, создающего экземпляр класса), а также полей объекта, значение которых определяется уже после создания экземпляра класса.

Внедрением зависимостей после создания экземпляра в Spring Framework занимается так называемый IoC-контейнер. IoC-контейнер представляет собой объект типа интерфейса BeanFactory. Как правило, при этом данный

объект создается на основе класса, реализующего производный интерфейс ApplicationContext. Например, в веб-приложениях используется автоматически создаваемый фреймворком Spring Boot экземпляр класса AnnotationConfigServletWebServerApplicationContext. При внедрении зависимостей Spring использует имеющийся в Java механизм рефлексии, в частности, активно применяются аннотации, опрашиваемые при выполнении приложения с помощью рефлексии [9].

Объекты, которые настраивает и внедряет IoC-контейнер, называются Spring bean-компонентами. Объявить Spring bean можно либо пометив его класс аннотацией @Component, либо использовав метод, помеченный аннотацией @Bean в классе, помеченным аннотацией @Configuration, либо используя xml-файлы [10]. Например, экземпляру класса NotificationAspect (который в демонстрационном приложении используется при формировании уведомления для отправки по электронной почте) для выполнения функций требуется объект типа интерфейса EmailService. Для этого классы, реализующие данный интерфейс, помечены аннотацией @Component. Во время выполнения программы IoC-контейнер создаст экземпляр класса, реализующего интерфейс EmailService и передаст его в качестве аргумента конструктору при создании экземпляра класса NotificationAspect.

Области применения Spring Scopes

Каждый объект Spring bean имеет свою область применения (scope), причем Spring Framework поддерживает шесть основных областей применения:

• singleton — на протяжении всей работы приложения существует только один Spring bean данного класса (выбирается по умолчанию);

• prototype — для каждого использующего его объекта внедряется свой Spring bean;

• request — для каждого HTTP-запроса создается свой Spring bean;

• session — для каждой сессии создается свой Spring bean;

• application — для каждого объекта типа интерфейса ServletContext создается свой Spring bean;

• websocket — для каждого объекта типа интерфейса WebSocket создается свой Spring bean.

Для приписывания области применения объекту Spring bean, необходимо применить аннотацию @Scope к классу, помеченному аннотацией @Component или методу, помеченному аннотацией @Bean. В качестве значения члена аннотации передается название области применения.

Для применения большинства областей scope фреймворку Spring необходимо использование прокси (объекта-посредника). Тип используемого прокси (на основе интерфейсов или на основе классов) также указывается в значении члена аннотации [11].

В качестве примера использования настройки области применения в демонстрационном приложении можно привести класс TeacherController, который содержит поле типа ConsultationPatternListWrapper, предназначенного для хранения данных об одном пользователе-преподавателе на протяжении нескольких запросов. Для этого используется область применения session и прокси на основе классов.

Архитектура Spring MVC

Spring Framework содержит два внутренних фреймворка для создания веб-приложений: Spring MVC и Spring WebFlux (предназначенный для реактивного веб-программирования).

Spring MVC основан на сервлете DispatcherServlet, который обрабатывает запросы, делегируя функции соответствующим компонентам Spring bean.

Архитектура MVC (Model-View-Controller), применяемая при проектировании программного обеспечения, представлена на рисунке 3 [12]. Она основана на взаимодействии трех типов компонентов [13]:

• Model (модель) содержит изменяемые данные, которые обновляет контроллер и которые передаются представлению;

• View (представление, вид) отвечает за отображение данных модели пользователю;

• Controller (контроллер) управляет моделью и отображением на основе входных данных.

Defines data structure e.g. updates application to reflect added item

Updates / e.g. list item to show added item I / \ \ Manipulates

View Defines display (Ul) e.g. user clicks ‘add to cart’ Sends input from user Controller Contains control logic e.g. receives update from view then notifies model to ‘add item’

Sometimes updates directly

Рис. 3. Архитектура MVC

Экземпляр класса DispatcherServlet обрабатывает запрос в соответствии с этой архитектурой, как показано на рисунке 4 [14].

Рис. 4. Обработка НТТР-запроса экземпляром класса

Обработка запроса происходит следующим образом:

1. Экземпляр класса DispatcherServlet принимает НТТР-запрос.

2. На основе объекта типа интерфейса HandlerMapping определяется контроллер, которому будет передана обработка запроса.

3. Контроллер обрабатывает запрос.

4. В результате обработки запроса контроллером экземпляр DispatcherServlet получает модель и логическое имя представления.

5. Объект типа интерфейса ViewResolver выбирает представление по его имени.

6. Представление, данные которого основаны на модели, формирует HTTP-ответ.

7. Пользователю отправляется HTTP-ответ.

Представление может быть создано при помощи таких

средств, как генератор веб-страниц Thymeleaf или технология JSP (Java (или Jakarta) Server Pages) [14].

Контроллер представляет собой Java-класс, помеченный аннотацией @Controller. Он объявляет методы, к которым применяется аннотация @RequestMapping (или одна из аннотаций, которая помечена данной аннотацией, например, @GetMapping или @PostMapping). На основе значений членов данной аннотации объект типа HandlerMapping сможет сопоставить HTTP-запрос соответствующему методу контроллера [15].

Примером контроллера в демонстрационном приложении является класс TeacherController. Этот класс объявлен как Spring bean-компонент, что позволяет внедрить в объект этого класса экземпляры других классов для выполнения различных функций. При этом в классе объявлены такие методы, как getTeacherProfile, deleteConsultation и т. д., к которым применяются аннотации @GetMapping или @PostMapping. Данные методы обрабатывают запрос, используя объект типа интерфейса Model, если необходимо передать пользователю данные.

Концепция Spring AOP

Для того чтобы неявным образом вызвать функцию после, перед или вместо выполнения определенного метода, Spring Framework использует аспекты и средства Spring AOP (Aspect Oriented Programming).

Внутренний фреймворк Spring AOP основан на AspectJ и использует идею прокси (объекта-посредника). Прокси подменяет собой изначальный объект и при вызове метода сначала выполняет свою логику, а затем делегирует полномочия изначальному методу. Принцип его работы приведен на рисунке 5 [16].

fоо() on the proxy

then foo<> on the object

Рис. 5. Принцип работы прокси-объекта

Для того чтобы объявить класс в качестве аспекта, необходимо применить к нему аннотацию @Aspect. Для того чтобы метод аспекта был выполнен при вызове метода другого объекта, необходимо применить к нему одну из следующих аннотаций: @Before (перед), @AfterReturning (после при успешном завершении), @AfterThrowing (после при завершении в результате возникновения исключительной ситуации), @After (после) или @Around (перед и после). Метод или

методы, при вызове которых должны отработать аспекты, определяются на основе значения члена одной из перечисленных аннотаций.

Такие методы в качестве аргумента могут принимать объект типа интерфейса JoinPoint, который содержит различную информацию об аспекте и методе, к которому применяется метод аспекта (например, аргументы метода). Если к методу аспекта применяется аннотация @Around, то он должен принимать в качестве аргумента объект типа интерфейса ProceedingJoinPoint, который содержит метод proceed для вызова оригинального метода [17].

Примером использования аспекта в демонстрационном приложении является уже упоминавшийся класс Notifica-tionAspect, в котором объявлен метод noti-fyStudentsAboutDeletion. Метод помечен аннотацией @After, а в качестве значения члена аннотации устанавливается название метода удаления консультации. Таким образом, данный метод будет вызван после удаления консультации независимо от места вызова метода удаления.

Другие фреймворки семейства Spring Projects

Описание средств, которыми располагают фреймворки Spring Boot, Spring Data и Spring Security, сопровождается отсылками к примерам их использования В. В. Саковичем при разработке демонстрационного приложения.

Фреймворк Spring Boot предоставляет возможность запуска веб-приложения при помощи вызова метода run класса SpringApplication из метода main. Класс, содержащий метод main, должен быть помечен аннотацией @SpringBootApplicationю.

При вызове метода run Spring Boot автоматически настроит множество параметров, а для веб-приложения дополнительно автоматически создаст сервер, используя встроенный контейнер сервлетов (по умолчанию — Tomcat).

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

Реализовать такую возможность позволяет аннотация @Profile, которая принимает в качестве значения члена аннотации профиль или логически объединенное (операторами И, ИЛИ, НЕ) множество профилей. Аннотация применяется при настройке Spring bean (либо к классу, помеченному аннотацией @Component, либо к методу, помеченному аннотацией @Bean) [3]. Таким образом, имитационный сервис включается только при активном профиле dev, а настоящий сервис — при активном профиле prod.

Spring Boot позволяет настраивать приложение посредством конфигурационных файлов (расширения properties илиyml). Отметим некоторые параметры, предоставляемые Spring Boot [18]:

i Не можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

• server.port — адрес порта, с которого сервер принимает запросы (по умолчанию — 8080);

• spring.proffles.active — активные профили приложения;

• spring.security.user.password — пароль пользователя, создаваемый по умолчанию;

• server.servlet.session.timeout — время действия сессии;

• spring.mail.host — хост SMTP-сервера.

Используя разделитель «—», можно настроить разные

значения конфигурационных параметров для разных профилей. Для этого необходимо добавить конфигурационный параметр spring.config.activate.on-profile [3].

Фреймворк Spring Data содержит средства для работы с базами данных. Фреймворк предоставляет приложению интерфейс Repository, который является интерфейсом-маркером, с помощью которого выявляются потомки этого интерфейса. Наследующие интерфейсу Repository интерфейсы CrudRepository и ListCrudRepository объявляют базовые методы по работе с базой данных (CRUD — Create, Read, Update, Delete).

Для практического применения средств для работы с базами данных при разработке приложения необходимо создать интерфейс, наследующий Repository. Объявить метод интерфейса-репозитория можно двумя способами: указанием имени метода в соответствии с соглашением об именах или при помощи аннотации @Query, указав в качестве значения члена аннотации запрос к базе данных.

При первом подходе Spring Data предлагает множество вариантов создания комплексного запроса. Среди них поиск по какому-либо полю, поиск с условием, логические объединения (И, ИЛИ), сортировка значений, разбиение запроса на страницы и т. д. При этом Spring Data позволяет такому методу возвращать как единственный объект типа, соответствующего хранимым в репозитории данным, так и коллекцию таких объектов.

При запуске приложения Spring Data проанализирует всех потомков интерфейса Repository и, если у какого-либо интерфейса не будет реализации (и он не помечен аннотацией @NoRepositoryBean), обеспечит реализацию интерфейса с помощью механизма рефлексии в соответствии с соглашением об именах методов или на основе значения члена аннотации @Query. Однако, при необходимости, разработчик может создать реализацию интерфейса самостоятельно (в виде компонента Spring bean) [19].

В качестве примера автоматической реализации интерфейса в демонстрационном приложении можно привести интерфейс TeacherRepository, для которого на этапе выполнения программы создается прокси-объект типа jdk.proxy4.$Proxy139.

Интерфейс TeacherRepository объявляет метод для поиска консультации, который помечен аннотацией @Query. На этапе выполнения прокси на основе значения члена аннотации @Query реализует метод, выполняющий поиск консультации в базе данных. Помимо этого, прокси реализует метод findByEmail суперинтерфейса PersonRepository на основе имени метода.

Средства обеспечения безопасности и авторизации пользователей предоставляются фреймворком Spring Security и основаны на фильтре сервлетов, который делегирует

полномочия по настройке безопасности набору фильтров SecurityFilterChain, как показано на рисунке 6 [20].

Набор фильтров безопасности может быть создан методом, помеченным аннотацией @Bean в классе, помеченным аннотацией @Configuration. Такой метод должен принимать экземпляр класса HttpSecurity в качестве аргумента [3].

Рис. 6. Принцип работы средств безопасности Spring Security

Отметим некоторые методы класса HttpSecurity [21]:

• securityMatcher — устанавливает типы URL-адресов, при которых будет применятся набор фильтров;

• authenticationProvider — устанавливает источник аутентификации;

• authorizeHttpRequests — настраивает правила доступа пользователей к веб-страницам;

• formLogin — настраивает страницу входа в аккаунт;

• logout — настраивает страницу выхода из аккаунта.

В качестве примера настройки авторизации в демонстрационном приложении, структура и функциональность которого представлены в следующем разделе статьи, можно рассматривать класс TeacherSecurityConfig, который содержит три метода, помеченных аннотацией @Bean:

• teacherDetailService возвращает объект типа интерфейса UserDetailsService, который находит объект типа интерфейса UserDetails, предоставляющий следующую информацию (для поиска используются средства для работы с базой данных) [21]: имя пользователя; пароль; права доступа; различная информация о состоянии аккаунта.

• teacherAuthenticationProvider возвращает объект типа интерфейса AuthenticationProvider, который аутенти-фицирует пользователя. Для поиска пользователя используется объект типа интерфейса UserDetailsService.

• teacherFilterChain настраивает фильтры безопасности, в качестве источника аутентификации используя объект, возвращаемый предыдущим методом. Метод возвращает объект типа интерфейса SecurityFilterChain.

Веб-приложение для записи студентов

на консультации и управления консультациями

Источником примеров использования средств фрейм-ворков семейства Spring Projects, описываемых в предыдущих разделах, является демонстрационное приложение, позволяющее преподавателю организовать запись студентов

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

Структура приложения соответствует общей структуре веб-приложений, представленной на рисунке 1.

Клиентская часть приложения представлена html-стра-ницами, доступными для просмотра в веб-браузере.

Серверная часть реализована на платформе Java с использованием описанных ранее возможностей фреймвор-ков семейства Spring Projects. Серверная часть приложения реализует следующие функции:

• Авторизация. Функции приложения недоступны для неавторизированных пользователей, в процессе авторизации определяется две роли пользователей — студенты и преподаватели, которым доступны разные функции приложения.

• Взаимодействие с базой данных. Информация о студентах, преподавателях, консультациях и записях о них хранится в реляционной БД (используется СУБД H2, предоставляемая Spring по умолчанию). Реализованы средства для создания, чтения, обновления и удаления данных.

• Запись студентов на консультации позволяет авторизованному студенту записаться на консультацию или отменить свою запись.

• Управление консультаций преподавателями позволяет преподавателям создавать, просматривать и удалять консультации или планы консультаций.

• Оповещение по электронной почте. При записи студента на консультацию оповещается преподаватель, при удалении преподавателем консультации оповещаются все записанные на нее студенты.

На рисунках 7 и 8 представлены диаграммы прецедентов для разрабатываемого веб-приложения, полученные на этапе его логического проектирования при анализе постановки задачи.

Рис. 5. Диаграмма прецедентов студента

Рис. 6. Диаграмма прецедентов преподавателя

Исходя из выявленных прецедентов, для клиентской части разработано одиннадцать веб-страниц приложения, реализующих варианты взаимодействия с пользователями типа «студент» и «преподаватель». В качестве средства для реализации клиентской части выбран инструмент генерации веб-страниц на основании шаблона Thymeleaf.

На рисунке 9 представлена диаграмма компонентов разработанного веб-приложения.

Приложение включает в себя такие компоненты, как:

• общая клиентская часть, предназначенная для всех пользователей;

• интерфейс студента, предназначенный для работы студентов с веб-приложением;

• интерфейс преподавателя, предназначенный для работы преподавателей с веб-приложением.

• средства по взаимодействию с базой данных, предназначенные для манипуляции данными в БД;

• средства авторизации, предназначенные для авторизации пользователя и определения его типа;

• компонент записи на консультации, с помощью которого студенты могут пользоваться функциями, обеспечивающими запись на консультации;

• компонент управления консультациями, с помощью которого преподаватели могут пользоваться функциями, связанными с созданием, планированием и отменой консультаций;

• компонент оповещения по электронной почте студентов и преподавателей об изменениях в графике проведения консультаций.

В настоящее время семейство фреймворков Spring Projects активно применяется для разработки корпоративных систем и веб-сервисов на платформе Java.

В статье обсуждаются особенности разработки веб-приложений с использованием средств фреймворков Spring Framework, Spring Boot, Spring Data и Spring Security. Кратко описывается инструмент Spring Initializr, применяемый для создания проектов приложений. Рассматриваются основные концепции Spring Framework: IoC-контейнер, Spring

Рис. 7. Диаграмма компонентов веб-приложения

Scopes, Spring MVC и Spring AOP. Освещаются возможности, предоставляемые Spring Boot: запуск приложения, профили, конфигурационные файлы. Уделяется внимание организации взаимодействия с базами данных с помощью Spring Data и использованию средств авторизации, предоставляемых Spring Security.

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

По мнению авторов, преимущества использования Spring Projects заключаются в следующем.

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

Таким образом, Spring Projects хорошо подходит для разработки сложных и многофункциональных систем, в том числе в области транспорта, например для решения задач комплексного управления логистикой и предоставления услуг клиентам такими компаниями в области транспорта, как ОАО «РЖД».

Использованные в статье материалы прошли апробацию на LXXXIII Всероссийской научно-технической конференции студентов, аспирантов и молодых ученых

«Транспорт: проблемы, идеи, перспективы», состоявшейся в ПГУПС в рамках фестиваля «Неделя науки — 2023». Материалы доклада В. В. Саковича, подготовленного под руководством его соавторов, рекомендованы к публикации кафедрой «Информационные и вычислительные системы».

1. Хомоненко, А. Д. Разработка Web-приложений для работы с базами данных: Учебное пособие / А. Д. Хомоненко, В. В. Рогальчук, А. В. Тырва; под ред. А. Д. Хомоненко. — Санкт-Петербург: ПГУПС, 2012. — 87 с.

2. Web Application Architecture: The Latest Guide 2022. — 2022. — 10 March // ClicklT. DevOps & Software Development URL: http://www.clickittech.com/devops/web-applica-tion-architecture (дата обращения 18.06.2023).

3. Уоллс, К. Spring в действии. Шестое издание = Spring in Action. Sixth Edition / пер. с англ. А. Н. Киселева. — Москва: ДМК Пресс, 2022. — 544 c.

4. Vermeer B. Spring Dominates the Java Ecosystem with 60% Using It for Their Main Applications — 2020. — 5 February // Snyk. Developer Security Platform. URL: http://snyk.io/blog/ spring-dominates-the-java-ecosystem-with-60-using-it-for-their-main-applications (дата обращения 18.06.2023).

5. Why Spring? // Spring. URL: http://spring.io/why-spring (дата обращения 18.06.2023).

6. Projects // Spring. URL: http://spring.io/projects (дата обращения 18.06.2023).

7. Understanding Spring Framework and Spring Ecosystem // Pranay Bathini’s Blog. — 2021. — 02 July.

URL: http://www.pranaybathini.com/2021/07/understanding-spring-framework-and-ecosystem.html (дата обращения 18.06.2023).

8. Spring Initializr. URL: http://start.spring.io (дата обращения 18.06.2023).

9. Шилдт, Г. Java 8. Полное руководство. Девятое издание = Java: The Complete Reference. Ninth Edition / пер. с англ. и редакция И. В. Берштейна. — Москва: Издательский дом «Вильямс», 2015. — 1376 с.

10. Spring Framework. Core Technologies // Spring. URL: http://docs.spring.io/spring-framework/reference/core.html (дата обращения 18.06.2023).

11. Spring Framework. Bean Scopes // Spring.

URL: http://docs.spring.io/spring-framework/reference/core/ beans/factory-scopes.html (дата обращения 18.06.2023).

12. MVC — MDN Web Docs Glossary: Definitions of Web-related terms // MDN Web Docs.

URL: http://developer.mozilla.org/en-US/docs/Glossary/MVC (дата обращения 18.06.2023).

13. Späth, P. Beginning Java MVC 1.0: Model View Controller Development to Build Web, Cloud, and Microservices Applications. — New York: Apress, 2020. — 460 p.

14. Walls, C. Spring in Action. Fourth Edition. — New York: Manning, 2014. — 624 c.

15. Spring Framework. Spring Web MVC // Spring. URL: http://docs.spring.io/spring-framework/reference/web/ webmvc.html (дата обращения 18.06.2023).

16. Spring Framework. Proxying Mechanisms // Spring. URL: http://docs.spring.io/spring-framework/reference/core/aop/ proxying.html (дата обращения 18.06.2023).

17. Spring Framework. Aspect Oriented Programming with Spring // Spring. URL: http://docs.spring.io/spring-framework/ reference/core/aop.html (дата обращения 18.06.2023).

18. Common Application Properties // Spring.

URL: http://docs.spring.io/spring-boot/docs/cunient/reference/html/ application-properties.html (дата обращения 18.06.2023).

19. Spring Data Commons — Reference Documentation. Version 3.1.1 / O. Gierke, T. Darimont, C. Strobl, [et al.] // Spring. — Обновлено 16.06.2023.

URL: http://docs.spring.io/spring-data/commons/docs/current/ reference/html (дата обращения 18.06.2023).

20. Spring Security. Architecture // Spring.

URL: http://docs.spring.io/spring-security/reference/servlet/ architecture.html (дата обращения 18.06.2023).

21. Spring Security Docs 6.1.0 API // Spring.

URL: http://docs.spring.io/spring-security/site/docs/current/api/ index.html (дата обращения 18.06.2023).

Using Spring Projects Family Frameworks for Developing Web Applications on Java Platform

V. V. Sakovich, PhD G. I. Kozhomberdieva Emperor Alexander I St. Petersburg State Transport University Saint Petersburg, Russia lampirg1@gmail.com, kgi-liizht@yandex.ru

Abstract. The article discusses the features of developing web applications on the Java platform using frameworks from the Spring Projects family. The basic concepts of the Spring Framework are considered: IoC container, Spring Scopes, Spring MVC, and Spring AOP. The possibilities provided by the Spring Boot are covered: application launch, profiles, and configuration files. In addition, attention is paid to the organization of interaction with databases using the Spring Data as well as using of authorization tools provided by the Spring Security. As a demo of a web application developed using the Spring features, the article provides a client server application for signing up students for a consultation and consultation management.

Keywords: web application, Java platform, Java annotation, framework, Spring Projects, Spring Framework, MVC architecture.

i Не можете найти то, что вам нужно? Попробуйте сервис подбора литературы.

1. Khomonenko A. D., Rogalchuk V. V., Tyrva A. V. Development of Web Applications for Working with Databases: Study guide [Razrabotka Web-prilozheniy dlya raboty s bazami dan-nykh: Uchebnoe posobie]. Saint Petersburg, PSTU, 2012, 87 p.

2. Web Application Architecture: The Latest Guide 2022, ClickIT. DevOps & Software Development. Published online at March 10, 2022. Available at: http://www.clickittech.com/ devops/web-application-architecture (accessed 18 Jun 2023).

3. Walls C. Spring in Action. Sixth Edition [Spring v deystvii. Shestoe izdanie]. Moscow, DMK Press Publishing House, 2022, 544 p.

4. Vermeer B. Spring Dominates the Java Ecosystem with 60% Using It for Their Main Applications, Snyk. Developer Security Platform. Published online at February 05, 2020. Available at: http://snyk.io/blog/spring-dominates-the-java-ecosys-tem-with-60-using-it-for-their-main-applications (accessed 18 Jun 2023).

5. Why Spring, Spring. Available at: http://spring.io/why-spring (accessed 18 Jun 2023).

6. Projects, Spring. Available at: http://spring.io/projects (accessed 18 Jun 2023).

7. Understanding Spring Framework and Spring Ecosystem, Pranay Bathini’s Blog. Published online at July 02, 2021. Available at: http://www.pranaybathini.com/2021/07/under-standing-spring-framework-and-ecosystem.html (accessed 18 Jun 2023).

8. Spring Initializr. Available at: http://start.spring.io/ (accessed 18 Jun 2023).

PhD D. P. Burakov independent researcher Saint Petersburg, Russia burakovdmitry8@gmail.com

9. Schildt H. Java: The Complete Reference. Ninth Edition [Java 8. Polnoe rukovodstvo. Devyatoe izdanie]. Moscow, Williams Publishing House, 2015, 1376 c.

10. Spring Framework. Core Technologies, Spring. Available at: http://docs.spring.io/spring-framework/reference/ core.html (accessed 18 Jun 2023).

11. Spring Framework. Bean Scopes, Spring.

Available at: http://docs.spring.io/spring-framework/reference/ core/beans/factory-scopes.html (accessed 18 Jun 2023).

12. MVC — MDN Web Docs Glossary: Definitions of Web-related terms, MDN Web Docs. Available at: http://devel-oper.mozilla.org/en-US/docs/Glossary/MVC (accessed 18 Jun 2023).

13. Späth P. Beginning Java MVC 1.0: Model View Controller Development to Build Web, Cloud, and Microservices Applications. New York, Apress, 2020, 460 p.

14. Walls C. Spring in Action. Fourth Edition. New York, Manning, 2014, 624 p.

15. Spring Framework. Spring Web MVC, Spring. Available at: http://docs.spring.io/spring-framework/reference/web/ webmvc.html (accessed 18 Jun 2023).

16. Spring Framework. Proxying Mechanisms, Spring. Available at: http://docs.spring.io/spring-framework/reference/ core/aop/proxying.html (accessed 18 Jun 2023).

17. Spring Framework. Aspect Oriented Programming with Spring, Spring. Available at: http://docs.spring.io/spring-framework/reference/core/aop.html (accessed 18 Jun 2023).

18. Common Application Properties, Spring. Available at: http://docs.spring.io/spring-boot/docs/current/reference/html/ application-properties.html (accessed 18 Jun 2023).

19. Gierke O., Darimont T., Strobl C., et al. Spring Data Commons — Reference Documentation. Version 3.1.1, Spring. Last updated at June 16, 2023.

Available at: http://docs.spring.io/spring-data/commons/docs/ current/reference/html (accessed 18 Jun 2023).

20. Spring Security. Architecture, Spring.

Available at: http://docs.spring.io/spring-security/reference/ servlet/architecture.html (accessed 18 Jun 2023).

21. Spring Security Docs 6.1.0 API, Spring.

Сравнение стеков Java EE и Spring: возможности и ограничения

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

У меня же на фирме вследствие определенных ограничений (мы работаем на внутренний рынок и поэтому вынуждены держать рейты невозможно низко для аутсорса) мы стараемся оптимизировать расходы клиентов на разработку. И разработка на стеке Java EE оказалась существенно дешевле разработки на стеке Spring при некоторых ограничениях.

Сразу хочу оговориться: я не рассматриваю холиварного противостояния — что на самом деле «тру», а что нет, — не разбираю вопросов «правильности» или соответствия идеалам красоты. Только деньги, ничего личного. Я же все-таки уже не разработчик, а «бездушный галеровладелец». Как пишут на одном широко известном ресурсе 🙂

Начнем с истории вопроса

Я — человек занудный. Поэтому не могу отойти от канонов академического образования: для любой вещи сначала надо сказать о том, откуда она вообще появилась на свет.

Итак, сначала было слово. А, нет, сначала была Java. И был Java Community Process (JCP), процедура в соответствии с которой и происходят все правки и выходят новые версии самой Java и связанные с ней спецификации. Например — Java EE и ее составные части.

И надо сказать, JCP — еще та неповоротливая и забюрократизированная система. Да еще и построенная на демократических принципах: индивидуально в JCP может войти любой и бесплатно. Фирмами — за денежку. И голосовать по поводу всех важных и не очень решений, связанных с Java. Короче — такая международная Верховная Рада. Только про Java. Ну вы поняли.

Тут я должен сделать еще одно отступление (люблю их!) и напомнить, что Java — первопроходец в массе разных направлений. Поэтому сейчас, конечно, приятно ругать JCP за кучу принятых ошибочных решений. Проблема в том, что на тот момент просто не было опыта таких решений, и кто-то же должен был быть первым. Примером такого плохого решения как раз можно считать спецификации EJB 1.0 и 1.1, о которых в Wikipedia сказано мрачно: «The EJB specification was originally developed in 1997 by IBM and later adopted by Sun Microsystems (EJB 1.0 and 1.1) in 1999». Короче, ребята из JCP вообще не знали, как делать большие Enterprise проекты (и никто на тот момент как следует не знал!). Но вот ребята из IBM (между прочим — один из исполнительного комитета JCP) пришли и сказали: «Мы знаем, как это делать». И в непередаваемом IBM стиле наваяли спецификацию. Два года, потраченные на адаптацию этого решения намекают, что документация была еще та.

В итоге у Java EE появился ключевой его элемент — EJB. Я как сертифицированный специалист по этим первым EJB могу точно сказать: это было чудовищно сложно и криво. Однако у EJB были свои преимущества: автоматическая, прямо из коробки, масштабируемость путем поддержки кластеров серверов приложений, опять же из коробки поддержка авторизации и самое главное — это был стандарт!

Итогом появления первых EJB стали две вещи. Во-первых, страшный хайп в java-мире: «Вот сейчас мы завоюем мир. У нас есть мощные EJB!» Честно-честно, в те годы в мире Java было полно хайпа. Вообще хочу заметить, что если быть в IT больше 10 лет (а лучше 20), то можно увидеть, что хайпы повторяются с определенной цикличностью. Например, современный хайп по Front-end — уже третий. И я точно знаю, чем он закончится (спойлер — возвратом к ультратонкому клиенту). Если интересно, чтобы я рассказал об этом в отдельной статье — напишите здесь в комментариях.

Но я отвлекся. Вторая вещь, которая стала следствием первой — это жуткое разочарование в EJB у всех, кто с ними работал. Реально, первая версия была чудовищно неудобной, глючной, требующей дикого количества дополнительных настроечных файлов к каждому бину и странных действий от программиста. А самое главное — инструментарий первых EJB оставлял желать смерти авторам. Понять по стек-трейсам, что именно у вас не так, было крайне сложно.

В итоге все те, кто повелся на рекламу и хайп, начали от новой технологии плеваться и поносить ее всеми ругательными словами, которые только можно придумать. Достаточно сказать, что термин POJO был придуман именно как альтернатива громоздким, корявым и неудобным конструкциям первых библиотек Java EE и в особенности EJB.

Но как говорят, свято место пусто не бывает, и программистам все равно нужны были фреймворки для облегчения работы. И они, естественно, пришли. Были их сотни, большинство так и сгинуло, но несколько фреймворков выжило. И парочка из них определила все дальнейшее развитие Java. Я имею в виду Hibernate и Spring DI aka IoC.

История Hibernate не менее драматична, чем история со Spring, но ее я расскажу как-нибудь в другой раз, если вам будет интересно.

А сейчас разговор про Spring. Итак, Spring был маленькой, красивой и приятной в обращении библиотекой. Да, еще не было никаких аннотаций (их добавили позже — уже в Java 5), все конфигурирование заключалось в правке XML файла. Но он был один! Он имел простую и понятную структуру а-ля web.xml! И по сравнению с ужасом конфигурационных файлов Java EE того времени был просто лапочкой. О чем там говорить, все, включая меня, были в диком восторге от Spring. Прошло совсем немного времени, и наличие Spring на проекте стало просто обязательным.

Надо сказать, что JCP не стали ревновать выскочек к успеху и самоотверженно начали впиливать поддержку Spring IoC в стек Java EE. И всё чудесно вертелось. Все, кто хотели (скажем честно — вообще все), использовали Spring IoC в своих проектах, не обращая внимания на то, на чем проект написан.

Но время шло, и жизнь не стояла на месте. Компания Pivotal, разработчик Spring, решила захватить мир, в смысле. Ну да, захватить мир, а что — так нельзя было? И начала клепать новые проекты со словом Spring в названии, чтобы покрыть все возможные и невозможные потребности Java разработчиков. Постепенно то, что раньше называлось Spring, сначала стало одним из проектов, а потом и вовсе слепилось с несколькими другими проектами в то, что сейчас называется Spring Core. А список всех остальных проектов (который раньше висел на сайте в виде красивой инфографики) спрятали от посторонних лиц. И правильно. Если все эти проекты вынести на инфографику, то придется использовать шрифт, и то картинка будет во всю стену. Постепенно следить за всем этим адом из зависимостей стало уж совсем сложно и появилась необходимость в отдельной библиотеке, которая должна сама все загружать и запускать. Ага, привет, Spring Boot.

Что же случилось с забытой всеми J2EE? Ее переименовали в красивую Java EE, убрав пугающую новичков двойку. Все это время JCP работал над одним — добиться максимального упрощения всего, что только можно. В итоге в современном EJB для описания бина достаточно указать одну аннотацию над классом. Все, у вас уже есть доступ ко всей мощи EJB.

И что? Обрадованные Java разработчики повыкидывали ставший монстром Spring и вернулись к Java EE, который стал таким простеньким? А фиг там. Мыши плакали, кололись, но продолжали жрать кактус.

Собственно, для того, чтобы обратить внимание «мышей» на колючки я и написал эту статью.

Но вступление, кажется, затянулось. Поехали ближе к теме, рассмотрим оба стека технологий в реальном использовании. Я уже говорил, что за последний год управлял несколькими проектами на Spring стеке и одним на Java EE стеке. Так что есть с чем сравнить.

Стек Java EE

Для начала вот вам картинка с простейшим Java EE приложением.

Как видите, ничего сложного вообще. Запрос обрабатывается JSF страницей, а затем — бином. Управление дальше уходит на бизнес-логику в EJB, которая работает с базой данных через JPA. Пока все просто и понятно. Самое приятное во всей этой картине, что если нагрузка на это приложение увеличится на несколько порядков, то его схема не изменится вообще никак. Приложение надо будет установить просто на кластер из нескольких мощных нод, и все будет работать в кластерном окружении без малейших правок. Если мощности все равно будет не хватать, то достаточно добавить еще нод в кластер — хватит.

Именно из-за этого решения на стеке Java EE я рекомендую использовать в тех случаях, когда вопрос масштабирования для вашего приложения самый критический. Но на этом я остановлюсь потом еще раз подробнее.

Но это мы изучали самое простое Java EE приложение. Посмотрим на что-то более мощное.

Вот так выглядит Java EE с точки зрения его документации. Опять же — скажем, не Rocket Science. Однако все необходимо есть, более того — есть из коробки в любом Enterprise Application Server. Причем все это работает из коробки вместе и на распределенном (кластерном) окружении так же.

Пока просто запомним это и двинемся вперед.

Стек Spring

Простое приложение на Spring стеке не сильно-то и отличается от Java EE.

Картинка отличается от предыдущей по большому счету только большей детализацией. Так что среднее приложение на Spring стеке от приложения Java EE не отличается практически никак.

Единственное отличие, которое сразу стоит озвучить, — это то, что приложение на Java EE может работать в общем случае только в рамках Enterprise Application Server’a (напоминаю, что Tomcat им не является), а приложение на Spring стеке может работать на чем угодно (на том же Tomcat) и даже вообще без сервера (так как запустит его внутри себя самостоятельно). Это делает Spring стек идеальным для реализации элементов микросервисной архитектуры, но за счет отказа от поддержки всех возможностей серверов приложений. В общем случае кластерное приложение — это не про Spring, и вопросы масштабирования для Spring приложения должны решаться отдельно. В большинстве случаев — существенно затратнее.

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

Если присмотреться, то все выглядит весьма похоже на предыдущую картинку 🙂 Да так оно на самом деле и есть. У всех выживших на сегодняшний момент стеков (двух) есть практически идентичное представление о том, как надо писать Enterprise приложения.

Каждый стек считает, что объекты предметной области должны быть тупыми Value Object, а сама логика бизнес-слоя должна храниться отдельно от них (в сервисах у Spring и в EJB у Java EE). А это, как вы понимаете, — разделение данных и поведение — ни разу не соответствует ООП. Это самый что ни на есть обычный процедурный код со всеми своими проблемами. Как минимум мы не можем использовать ни единого шаблона проектирования. Как будто и не было всех этих лет развития Computer Science.

Да, это тема для отдельного разговора. Если интересно — пишите в комментариях, может, я созрею написать статью и про это.

Попарное сравнение стеков

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

Spring DI (Dependency Injection) vs CDI (Context Dependency Injection)

Даже из названия понятно, что ядра в обоих стеках совершенно идентичны по характеристикам. Так и есть. Фактически в Java EE стеке изначально и использовался спринговый DI. Но после того как его включили прямо в Core, использовать его отдельно стало невозможно. Пришлось делать свой DI с аннотациями и программистками.

Получилось в общем-то неплохо, даже чуть-чуть лучше, на мой вкус. Но различия настолько незначительны, что ими можно пренебречь.

Spring Beans vs EJB (Enterprise Java Beans)

Тут, скажем, ситуация прямо противоположная. Spring beans — это просто обычные джава бины, которые можно куда-то заинжектить и ничего больше. EJB — достаточно мощная штуковина, в которую встроена поддержка распределенного (мультинодного) исполнения, включая распределенный сборщик мусора, аутентификацию, поддержку транзакций и черти что еще. Другой разговор, если все это вам не надо. Ну вы поняли 🙂

Spring Service Locator vs JNDI

Тут тоже ситуация аналогичная. Служба, помогающая найти в том же JVM сервис, и служба, которая может работать на распределенной системе и находить соответствие между чем угодно. Включая привязку к LDAP компании. Формально, предназначены они для одного и того же, однако согласитесь — разница на лицо.

@Async vs JMS

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

Spring MVC vs Java Server Faces (JSF)

Проблема этого сравнения в том, что SpringMVC в качестве темплейтного движка обычно используется Thymeleaf или в более современном варианте — какой-либо Front-end движок (React, Angular & etc). В то время, как JSF — полноценное GUI решение с поддержкой AJAX из коробки и даже SPA приложений с помощью дополнительных библиотек, включая такие гиперудобные и красивые, как PrimeFaces и IceFaces.

Вот тут я вынужден обратиться к моему личному опыту и рассказать про несколько наших проектов.

Первый проект решили стартовать на стеке Spring. ТЗ было достаточно простое, куча бизнес-логики, но интерфейс достаточно простой и особых проблем вызывать не должен был. И надо сказать, поначалу все шло отлично — MVP мы нарисовали с опережением графика месяца на полтора. Заказчик сказал: «Вау, ребята, вы — молодцы. Делаем теперь основной проект. Мы же можем задействовать MVP?» Мы его заверили, что ни строчки кода не пропадет. Он сказал: «Отлично, давайте начнем с того, что добавим во все формочки AJAX». Все-таки сейчас 2017 год, перегрузка страниц для заполнения формы — как-то не очень.

И тут оказалось, что нативной поддержки AJAX в Spring MVC + Thymeleaf просто нет. Надо каждую операцию писать руками. А у нас практически каждый элемент требует AJAX поведения, причем в разных ситуациях — разного. И писать эту тонну JavaScript мои джаверы вообще не горят желанием. Ну что же. Скрепя сердце были вынуждены выкинуть весь написанный интерфейс и переделать все приложение на Angular (для чего взять еще и Front-end разработчика). И заняло это переписывание где-то больше месяца. Вот и считайте, в какие деньги вышло заказчику наше решение использовать стек Spring.

Второй проект был практически идентичен первому, но в нем заказчик изначально просил учесть серьезные нагрузки в будущем — надо мол будет обрабатывать миллионы обращений. Все обдумав, я решил делать проект на стеке Java EE. Думаю, сложнее морду рисовать, зато в будущем будет легкость масштабирования. И что бы вы думали? Мои разработчики нарисовали примерно аналогичное по объему MVP еще быстрее, но при этом сразу из коробки с поддержкой AJAX. Продолжают работать только джаверы, фронтендер им не нужен. Да, конечно, рано или поздно надо будет сделать красивый дизайн, но дизайнер парт-тайм — это не фронтендер фулл-тайм по деньгам.

Spring Data vs JPA

Это единственный пункт, в котором Spring кроет как бык овцу стек Java EE. Spring Data — прекрасна, великолепна, быстра и удобна. Снимаю шляпу. Очень жаль, что аналога в Java EE стеке нет.

Spring Security vs JAAS

Что хотел я сказать. Spring Security — фреймворк, который мне создал на моих проектах проблем больше, чем все остальные вместе взятые. Возможно, документация фиговая (это единственный Spring фреймворк с фиговой документацией). Возможно, сама тема сложная, но мои программисты традиционно мучаются с ней очень много. Полномасштабного использования JAAS у нас пока не было, но то, что простейшее — прилепляется легко.

Отличия фреймворков вы понимаете и сами. JAAS, естественно, поддерживает распределенность.

Spring Boot vs Enterprise Server

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

Напоминаю, что аналогом Spring Boot для стека Java EE является просто любой Enterprise server. Там уже есть все нужные фреймворки нужных и консистентных версий, уже настроенные для работы друг с другом.

Выводы

  • Не надо пытаться все приложения делать на каком-то одном стеке — они предназначены немного для разных вещей. Но надо четко понимать, для чего каждый из стеков подходит лучше. Проще говоря, Java EE — для легко масштабируемого монолитного приложения, Spring — для совсем маленьких приложений с GUI на Front-end или для микросервисной архитектуры.
  • Не надо забывать и о том, AJAX — это сейчас очень важно. Если вы планируете делать серьезное приложение на SpringMVC + Thymeleaf — готовьтесь серьезно терять во времени разработки. Писать AJAX руками — это больно. Так что либо React/Angular (модно, красиво, молодежно!) либо по старинке JSF + PrimeFaces. Не настолько прекрасно, но тоже весьма ничего. И без Front-ender. Заметим, что именно для приложений бек-офиса (всяких админок и т. п.) PrimeFaces и их аналоги предлагают запредельное количество готовых бизнес-компонентов, типа календарей, колор-пикеров, часовых поясов и всякого такого. Скорость разработки вы существенно повысите
  • Ваши программисты могут и не знать стека Java EE. Как показала моя практика — учатся они ему очень быстро.
  • И последнее. Я думаю, маятник, который когда-то вынес Spring на вершину, может пойти обратно к Java EE и скинуть Spring вниз. Я бы не исключал такой возможности.

Все про українське ІТ в телеграмі — підписуйтеся на канал DOU

�� Подобається Сподобалось 5

До обраного В обраному 0

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

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

Практичний курс від skvot: Артменеджер.
Управляйте творчим процесом.

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

Професійний курс від skvot: PR basis.
Засвоєте основи PR та комунікації.

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

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

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

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

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

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

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

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

IoC и DI

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

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

Спеціалізований курс від robotdreams: Frontend Engineer.
Створюйте вражаючий веб.

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

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

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

Код

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

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

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

  • сильная связанность между классами (невозможно разрабатывать класс Car , не делая изменений в классе Engine и наоборот);
  • такое приложение тяжело изменять, а значит его сложно расширять (поддерживать и обновлять).

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

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

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

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

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

Ефективний курс від skvot: Режисура відеороликів.
Творча магія кінематографу.

Давайте добьемся инверсии контроля при помощи 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 (хотя теперь это и делается снаружи исходного кода):

Давайте рассмотрим пример того, как 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 выглядит пространство имен. Но давайте сосредоточим внимание на выделенной области кода. Здесь можно увидеть объявление наших бинов:

  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 или аннотации? На мой взгляд, это вопрос вкуса:

  • С одной стороны, практично выносить конфигурации приложения за пределы кода, как это происходит в XML-конфигурации. В таком случае вы избегаете смешения бизнес-логики приложения с его настройкой.
  • С другой стороны, именно аннотации позволяют наблюдать конфигурацию внутри Java-кода. Так вы сразу увидите, какой класс является компонентом и какие у него существуют зависимости.

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

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

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

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

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

@Autowired

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

@Autowired

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

  • Constructor Injection ;
  • Setter Injection ;
  • Field Injection .

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

Сonstructor Injection

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

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

Setter Injection

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

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

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

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

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

  • при этом типе внедрения нельзя сделать зависимость неизменяемой;
  • плотная зависимость от 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 : при его внедрении для каждой зависимости будет всегда создаваться новый объект:

Скоупы 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

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

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

  • @Controler ;
  • @Service ;
  • @Repository .

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

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

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

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

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

  • Самый главный источник — официальная документация. Книга Spring 4(5) для профессионалов. Первые несколько глав будут очень полезны для начинающих.
  • Практические туториалы.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Что такое Spring Framework? От внедрения зависимостей до Web MVC

Вы можете использовать это руководство для различных целей:

  • Чтобы понять, что такое Spring Framework
  • Как работают ее основные фичи: такие как внедрение зависимостей или Web MVC
  • Это также исчерпывающий FAQ (Перечень часто задаваемых вопросов)

Примечание: Статья ~ 9000 слов, вероятно, не стоит читать ее на мобильном устройстве. Добавьте ее в закладки и вернитесь позже. И даже на компьютере ешь читай этого слона по одному кусочку за раз 🙂

Введение

Сложность экосистемы Spring

Многие компании используют Spring, однако когда вы захотите узнать об этом фреймворке и перейдете на сайт spring.io, то увидите, что вселенная Spring на самом деле состоит из 21 различных активных проектов. Ой!

Кроме того, если вы начали программировать с помощью Spring в последние пару лет, очень велика вероятность того, что вы перешли непосредственно к Spring Boot или Spring Data.

Однако это руководство касается только одного, самого важного из этих проектов: Spring Framework. Почему?

Потому что важно понимать, что Spring Framework является основой для всех других проектов. Spring Boot, Spring Data, Spring Batch — все это построено поверх Spring.

Это имеет два последствия:

  • Без надлежащего знания Spring Framework вы рано или поздно потеряетесь. Вы не будете в полной мере понимать, например. Spring Boot, несмотря на то, что, как вы думаете знание ядра Spring Framework неважно.
  • Потраченные ~ 15 минут на чтение этого руководства, которое охватывает самые важные 80% Spring Framework, многократно окупятся в вашей профессиональной карьере.

Что такое Spring Framework?

Краткий ответ:

По сути Spring Framework представляет собой просто контейнер внедрения зависимостей, с несколькими удобными слоями (например: доступ к базе данных, прокси, аспектно-ориентированное программирование, RPC, веб-инфраструктура MVC). Это все позволяет вам быстрее и удобнее создавать Java-приложения.

Только это не очень помогает, не так ли?

К счастью, есть и длинный ответ:

Остальная часть этого руководства.

Основы внедрения зависимостей

Если вы уже знаете, что такое внедрение зависимостей, не стесняйтесь переходить прямо к следующему разделу Контейнер Spring IOC / Dependency Injection. В противном случае читайте дальше.

Что такое зависимость?

Представьте, что вы пишете Java класс, который позволяет вам получить доступ к таблице пользователей в вашей базе данных. Вы бы назвали эти классы DAO (объект доступа к данным) или репозитории. Итак, вы собираетесь написать класс UserDAO.

public class UserDao < public User findById(Integer id) < // execute a sql query to find the user >>

Ваш класс UserDAO имеет только один метод, который позволяет вам находить пользователей в вашей таблице базы данных по их идентификаторам.

Чтобы выполнить соответствующий SQL запрос, вашему классу UserDAO требуется соединение с базой данных. А в мире Java вы, обычно, получаете соединение с базой данных из другого класса, называемого DataSource. Итак, ваш код теперь будет выглядеть примерно так:

import javax.sql.DataSource; public class UserDao < public User findById(Integer id) throws SQLException < try (Connection connection = dataSource.getConnection()) < // (1) PreparedStatement selectStatement = connection.prepareStatement("select * from users where // use the connection etc. >> >
  1. Вопрос в том, откуда ваш UserDao получает свою информацию об источнике данных? Очевидно, что DAO зависит от действительного источника данных для запуска этих SQL-запросов.

Внедрение зависимостей с помощью new()

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

import com.mysql.cj.jdbc.MysqlDataSource; public class UserDao < public User findById(Integer id) < MysqlDataSource dataSource = new MysqlDataSource(); // (1) dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase"); dataSource.setUser("root"); dataSource.setPassword("s3cr3t"); try (Connection connection = dataSource.getConnection()) < // (2) PreparedStatement selectStatement = connection.prepareStatement("select * from users where // execute the statement..convert the raw jdbc resultset to a user return user; >> >
  1. Мы хотим подключиться к базе данных MySQL, для чего мы используем MysqlDataSource и задали непосредственно в коде url/username/password для облегчения чтения кода.
  2. Мы используем наш недавно созданный источник данных для запроса.

Это работает, но давайте посмотрим, что произойдет, когда мы расширим наш класс UserDao другим методом, findByFirstName.

К сожалению, этому методу также нужен источник данных для работы. Мы можем добавить этот новый метод к нашему UserDAO и применить некоторые рефакторинги, введя метод newDataSource.

import com.mysql.cj.jdbc.MysqlDataSource; public class UserDao < public User findById(Integer id) < try (Connection connection = newDataSource().getConnection()) < // (1) PreparedStatement selectStatement = connection.prepareStatement("select * from users where // TODO execute the select , handle exceptions, return the user >> public User findByFirstName(String firstName) < try (Connection connection = newDataSource().getConnection()) < // (2) PreparedStatement selectStatement = connection.prepareStatement("select * from users where first_name = ?"); // TODO execute the select , handle exceptions, return the user >> public DataSource newDataSource() < MysqlDataSource dataSource = new MysqlDataSource(); // (3) dataSource.setUser("root"); dataSource.setPassword("s3cr3t"); dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase"); return dataSource; >> 
  1. findById был переписан для использования нового метода newDataSource().
  2. findByFirstName был добавлен и также использует новый метод newDataSource().
  3. Это наш недавно извлеченный метод, способный создавать новые источники данных.

Этот подход работает, но имеет два недостатка:

  1. Что произойдет, если мы хотим создать новый класс ProductDAO, который также выполняет операторы SQL? Ваш ProductDAO также будет иметь зависимость DataSource, которая теперь доступна только в вашем классе UserDAO. Затем у вас будет другой подобный метод или извлечен вспомогательный класс, содержащий ваш DataSource.
  2. Мы создаем совершенно новый источник данных для каждого запроса SQL. Учтите, что DataSource открывает реальное сокет-соединение от вашей Java-программы к вашей базе данных. Это занимает время и довольно дорого. Было бы намного лучше, если бы мы открыли только один источник данных и использовали его повторно, вместо того, чтобы открывать и закрывать их тонны. Одним из способов сделать это может быть сохранение источника данных в закрытом поле в нашем UserDao, чтобы его можно было повторно использовать между методами, но это не помогает при дублировании между несколькими DAO.

Зависимости в глобальном классе приложения

Чтобы решить эти проблемы, вы можете подумать о написании глобального класса Application, который выглядит примерно так:

import com.mysql.cj.jdbc.MysqlDataSource; public enum Application < INSTANCE; private DataSource dataSource; public DataSource dataSource() < if (dataSource == null) < MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUser("root"); dataSource.setPassword("s3cr3t"); dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase"); this.dataSource = dataSource; >return dataSource; > >

Ваш класс UserDAO теперь может выглядеть так:

import com.yourpackage.Application; public class UserDao < public User findById(Integer id) < try (Connection connection = Application.INSTANCE.dataSource().getConnection()) < // (1) PreparedStatement selectStatement = connection.prepareStatement("select * from users where // TODO execute the select etc. >> public User findByFirstName(String firstName) < try (Connection connection = Application.INSTANCE.dataSource().getConnection()) < // (2) PreparedStatement selectStatement = connection.prepareStatement("select * from users where first_name = ?"); // TODO execute the select etc. >> >

Это улучшение по двум направлениям:

  1. Вашему UserDAO больше не нужно создавать свою собственную зависимость DataSource, вместо этого он может попросить класс Application предоставить ему полнофункциональную. То же самое для всех других ваших DAO.
  2. Ваш класс приложения является одноэлементным (это означает, что будет создан только один INSTANCE), и этот одноэлементный компонент приложения содержит ссылку на одноэлементный объект DataSource.

Однако у этого решения есть еще несколько недостатков:

  1. UserDAO активно должен знать, где получить свои зависимости, он должен вызвать класс приложения → Application.INSTANCE.dataSource().
  2. Если ваша программа становится больше, и вы получаете все больше и больше зависимостей, у вас будет один монстр класс Application.java, который обрабатывает все ваши зависимости. В этот момент вы захотите разделить вещи на несколько классов / фабрик и т.д.

Инверсия управления (IoC, Inversion of Control)

Давайте сделаем еще один шаг вперед.

Было бы неплохо, если бы вам в классе UserDAO вообще не приходилось беспокоиться о поиске зависимостей? Вместо того, чтобы активно вызывать Application.INSTANCE.dataSource(), ваш UserDAO мог бы (как-то) кричать, что он ему нужен, но больше не контролирует, когда / как / откуда он его получает?

Это то, что называется инверсией управления (Inversion of Control).

Давайте посмотрим, как может выглядеть наш класс UserDAO, с новым конструктором.

import javax.sql.DataSource; public class UserDao < private DataSource dataSource; private UserDao(DataSource dataSource) < // (1) this.dataSource = dataSource; >public User findById(Integer id) < try (Connection connection = dataSource.getConnection()) < // (2) PreparedStatement selectStatement = connection.prepareStatement("select * from users where // TODO execute the select etc. >> public User findByFirstName(String firstName) < try (Connection connection = dataSource.getConnection()) < // (2) PreparedStatement selectStatement = connection.prepareStatement("select * from users where first_name = ?"); // TODO execute the select etc. >> >
  1. Всякий раз, когда вызывающий создает новый UserDao через свой конструктор, вызывающий также должен передать действительный источник данных.
  2. Методы findByX будут просто использовать этот источник данных.

С точки зрения UserDao это выглядит намного лучше. Он больше не знает ни о классе приложения, ни о том, как создавать сами источники данных. Он только объявляет миру, что «если вы хотите создать (то есть использовать) меня, вам нужно дать мне источник данных».

Но представьте, что вы хотите запустить свое приложение. Если раньше вы могли вызывать «new UserService()», то теперь вам нужно обязательно вызвать новый UserDao(dataSource).

public class MyApplication < public static void main(String[] args) < UserDao userDao = new UserDao(Application.INSTANCE.dataSource()); User user1 = userDao.findById(1); User user2 = userDao.findById(2); // etc . >>

Контейнеры для внедрения зависимостей

Следовательно, проблема в том, что вы, как программист, все еще создаете UserDAO с помощью их конструктора и, таким образом, устанавливаете зависимость DataSource вручную.

Разве не было бы хорошо, если бы кто-то знал, что ваш UserDAO имеет зависимость от конструктора DataSource, и знал, как ее создать? А затем волшебным образом сконструирует для вас оба объекта: работающий DataSource и работающий UserDao?

Этот кто-то является контейнером внедрения зависимостей и является именно тем, что представляет собой среда Spring.

Контейнер Spring IOC / Dependency Injection

Как уже упоминалось в самом начале, Spring Framework по своей сути является контейнером внедрения зависимостей, который управляет написанными вами классами и их зависимостями для вас (см. Предыдущий раздел). Давайте выясним, как это происходит.

Что такое ApplicationContext? Для чего тебе это?

Тот, кто контролирует все ваши классы и может управлять ими соответствующим образом (читай: создайте их с необходимыми зависимостями), называется ApplicationContext во вселенной Spring.

Чего мы хотим добиться, так это следующего кода (я описал UserDao и DataSource в предыдущем разделе, перейдите по ссылке, если вы пришли сюда и пропустили его):

import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import javax.sql.DataSource; public class MyApplication < public static void main(String[] args) < ApplicationContext ctx = new AnnotationConfigApplicationContext(someConfigClass); // (1) UserDao userDao = ctx.getBean(UserDao.class); // (2) User user1 = userDao.findById(1); User user2 = userDao.findById(2); DataSource dataSource = ctx.getBean(DataSource.class); // (3) // etc . >>
  1. Здесь мы создаем наш Spring ApplicationContext. Мы подробно расскажем о том, как это работает, в следующих параграфах.
  2. ApplicationContext может дать нам полностью сконфигурированный UserDao, то есть один с его набором зависимостей DataSource.
  3. ApplicationContext может также предоставить нам источник данных напрямую, который является тем же источником данных, который он устанавливает внутри UserDao.

Это довольно круто, не правда ли? Вам, как вызывающей стороне, больше не нужно беспокоиться о создании классов, вы можете просто попросить ApplicationContext предоставить вам рабочие классы!

Но как это работает?

Что такое ApplicationContextConfiguration? Как построить ApplicationContexts из конфигураций.

В приведенном выше коде мы помещаем переменную с именем someConfigClass в конструктор AnnotationConfigApplicationContext. Вот быстрое напоминание:

import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class MyApplication < public static void main(String[] args) < ApplicationContext ctx = new AnnotationConfigApplicationContext(someConfigClass); // (1) // . >>

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

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyApplicationContextConfiguration < // (1) @Bean public DataSource dataSource() < // (2) MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUser("root"); dataSource.setPassword("s3cr3t"); dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase"); return dataSource; >@Bean public UserDao userDao() < // (3) return new UserDao(dataSource()); >>
  1. У вас есть выделенный класс конфигурации ApplicationContext, помеченный аннотацией @Configuration , который немного похож на класс Application.java из раздела Зависимости в глобальном классе приложения.
  2. У вас есть метод, который возвращает DataSource и аннотируется @Bean для Spring.
  3. У вас есть другой метод, который возвращает UserDao и создает указанный UserDao, вызывая метод bean-компонента dataSource.

Этого класса конфигурации уже достаточно для запуска самого первого приложения Spring.

import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class MyApplication < public static void main(String[] args) < ApplicationContext ctx = new AnnotationConfigApplicationContext(MyApplicationContextConfiguration.class); UserDao userDao = ctx.getBean(UserDao.class); // User user1 = userDao.findById(1); // User user2 = userDao.findById(1); DataSource dataSource = ctx.getBean(DataSource.class); >>

Теперь давайте выясним, что именно Spring и AnnotationConfigApplicationContext делают с тем классом конфигурации, который вы написали.

Почему мы создали AnnotationConfigApplicationContext? Существуют ли другие классы ApplicationContext?

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

Посмотрите на класс MyApplicationContextConfiguration сверху. Это класс Java, который содержит аннотации Spring. Вот почему вам необходимо создать аннотацию AnnotationConfigApplicationContext.

Если вместо этого вы хотите создать свой ApplicationContext из файлов XML, вы должны создать ClassPathXmlApplicationContext.

Есть и много других, но в современном приложении Spring вы обычно начинаете с контекста приложения, основанного на аннотациях.

Что делает аннотация @Bean ? Что такое Spring Bean?

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

Эти экземпляры, которые создаются этими фабричными методами, называются bean-компонентами. Spring контейнер создал их и они находятся под его управлением.

Но это приводит к вопросу: сколько экземпляров определенного компонента должно быть создано Spring?

Что такое Spring bean scope?

Сколько экземпляров наших DAO следует создать в Spring? Чтобы ответить на этот вопрос, вам нужно узнать о bean scope (область применения бина).

  • Должен ли Spring создать singleton: все ваши DAO используют один и тот же источник данных?
  • Должен ли Spring создать prototype: все ваши DAO получают свой собственный источник данных?
  • Или ваши компоненты должны иметь еще более сложные области действия, например: новый источник данных для HttpRequest? Или за HttpSession? Или за WebSocket?

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

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Configuration; @Configuration public class MyApplicationContextConfiguration < @Bean @Scope("singleton") // @Scope("prototype") etc. public DataSource dataSource() < MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUser("root"); dataSource.setPassword("s3cr3t"); dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase"); return dataSource; >>

Аннотация области (scope annotation) определяет, сколько экземпляров создаст Spring. И, как упоминалось выше, это довольно просто:

  • Scope(«singleton») → Ваш бин будет синглтоном, т.е. будет только один экземпляр.
  • Scope(«prototype») → Каждый раз, когда кому-то нужна ссылка на ваш компонент, Spring создает новый. (Здесь есть несколько предостережений, например, внедрения прототипов в синглтоны).
  • Scope(«session») → Для каждого сеанса HTTP пользователя будет создан один компонент.
  • и т.п.

Суть: большинство приложений Spring почти полностью состоят из одноэлементных bean-компонентов, в которые время от времени добавляются другие области действия bean-компонента (прототип, запрос, сессия, websocket и т.д.).

Теперь, когда вы знаете о ApplicationContexts, Beans & Scopes, давайте еще раз рассмотрим зависимости или то, как наш UserDAO может получить DataSource.

Что такое Spring Java Config?

До сих пор вы явно настраивали свои bean-компоненты в конфигурации ApplicationContext с помощью аннотированных @Bean методов Java.

Это то, что вы бы назвали Spring Java Config, в отличие от указания всего в XML, который исторически был подходом для Spring. Просто краткий обзор того, как это выглядит:

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyApplicationContextConfiguration < @Bean public DataSource dataSource() < MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUser("root"); dataSource.setPassword("s3cr3t"); dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase"); return dataSource; >@Bean public UserDao userDao() < // (1) return new UserDao(dataSource()); >>
  1. Один вопрос: почему вы должны явно вызывать новый UserDao() с ручным вызовом dataSource()? Разве Spring не может понять все это сам?

Вот где появляется другая аннотация @ComponentScan .

Что делает @ComponentScan ?

Первое изменение, которое вам нужно применить к своей конфигурации контекста, — это добавить к ней дополнительную аннотацию @ComponentScan .

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan // (1) public class MyApplicationContextConfiguration < @Bean public DataSource dataSource() < MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUser("root"); dataSource.setPassword("s3cr3t"); dataSource.setURL("jdbc:mysql://localhost:3306/myDatabase"); return dataSource; >// (2) // no more UserDao @Bean method! >
  1. Мы добавили аннотацию @ComponentScan .
  2. Обратите внимание, что определение UserDAO теперь отсутствует в конфигурации контекста!

То, что делает эта аннотация @ComponentScan , это сказать Spring: Посмотрите на все классы Java в том же пакете, что и конфигурация контекста, если они выглядят как Spring Bean!

Это означает, что, если ваш MyApplicationContextConfiguration находится в пакете com.marcobehler, Spring будет сканировать каждый пакет, включая подпакеты, который начинается с com.marcobehler для поиска потенциальных компонентов Spring.

Как Spring узнает, является ли что-то бином Spring? Легко: Ваши классы должны быть помечены аннотацией маркера, называемой @Component .

Что делают аннотации @Component и @Autowired ?

Давайте добавим аннотацию @Component к вашему UserDAO.

import javax.sql.DataSource; import org.springframework.stereotype.Component; @Component public class UserDao < private DataSource dataSource; private UserDao(DataSource dataSource) < // (1) this.dataSource = dataSource; >> 
  1. Это говорит Spring, аналогично тому методу @Bean , который вы написали ранее: эй, если вы обнаружите, что меня аннотируют с помощью @Component через ваш @ComponentScan , тогда я хочу быть бином Spring, управляемым вами, контейнером внедрения зависимостей!

(Когда вы посмотрите на исходный код аннотаций, таких как @Controller , @Service или @Repository , позже, вы обнаружите, что все они состоят из нескольких дополнительных аннотаций, всегда включая @Component !).

Отсутствует только один маленький кусочек информации. Как Spring узнает, что он должен взять DataSource, который вы указали как метод @Bean , а затем создать новые UserDAO с этим конкретным DataSource?

Легко, с другой аннотацией: @Autowired . Следовательно, ваш окончательный код будет выглядеть следующим образом.

import javax.sql.DataSource; import org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired; @Component public class UserDao < private DataSource dataSource; private UserDao(@Autowired DataSource dataSource) < this.dataSource = dataSource; >>

Теперь Spring имеет всю информацию, необходимую для создания bean-компонентов UserDAO:

  • UserDAO аннотируется @Component → Spring создаст его
  • UserDAO имеет аргумент конструктора @Autowired → Spring автоматически внедрит источник данных, настроенный с помощью вашего метода @Bean
  • Если в ваших конфигурациях Spring не было настроено ни одного источника данных, вы получите исключение NoSuchBeanDefinition во время выполнения.

Внедрение зависимости через конструктор и Autowired

Я лгал вам чуть-чуть в предыдущем разделе. В более ранних версиях Spring (до 4.2) вам нужно было указывать @Autowired , чтобы внедрение зависимости работало.

В более новых версиях Spring действительно достаточно умен, чтобы внедрять эти зависимости без явной аннотации @Autowired в конструкторе. Так что это также будет работать.

@Component public class UserDao < private DataSource dataSource; private UserDao(DataSource dataSource) < this.dataSource = dataSource; >>

Почему я упомянул @Autowired тогда? Поскольку это не повредит, то есть делает вещи более явными и потому что вы можете использовать @Autowired во многих других местах, кроме конструкторов.

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

Что такое Field Injection? Что такое Setter Injection?

Проще говоря, Spring не должен использовать конструктор для внедрения зависимостей.

Он также может напрямую внедрять поля.

import javax.sql.DataSource; import org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired; @Component public class UserDao

Кроме того, Spring также может внедрять сеттеры.

import javax.sql.DataSource; import org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired; @Component public class UserDao < private DataSource dataSource; @Autowired public void setDataSource(DataSource dataSource) < this.dataSource = dataSource; >>

Эти два стиля внедрения (поля, сеттеры) имеют тот же результат, что и внедрение конструктора: вы получите работающий Spring Bean. На самом деле есть еще один метод, называемый внедрением метода, который мы не будем здесь описывать.

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

Внедрение зависимости через конструктор или через поле

Было много споров в сети что лучше внедрение зависимости через конструктор или через поле и множеством громких голосов, утверждавших, что внедрение с помощью метода установки (setter) вредна.

Чтобы не добавлять шума к этим аргументам сформулируем суть этой статьи:

  1. В последние годы я работал с обоими стилями, внедрением зависимости через конструктор и через поле в различных проектах. Основываясь исключительно на личном опыте, у меня в действительности нет предпочтения одного стиля над другим.
  2. Важна согласованность: не стоит использовать в 80% ваших бинов внедрение зависимости через конструктор, в 10% — инъекцию поля и для оставшихся 10% — внедрение метода.
  3. Подход Spring из официальной документации кажется разумным: используйте внедрением зависимости через конструктор для обязательных зависимостей и внедрение с помощью метода установки / через поле для необязательных зависимостей. Еще раз предупреждаю: будьте действительно последовательны с этим.

Резюме о контейнере Spring IoC

К настоящему моменту вы должны знать почти все, что вам нужно знать о контейнере зависимостей Spring.

Конечно, это еще не все, но если вы хорошо разбираетесь в ApplicationContexts, Beans, зависимостях и различных методах внедрения зависимостей, то вы уже на правильном пути.

Давайте посмотрим, что еще может предложить Spring, кроме инъекций чистой зависимости.

Spring AOP (Аспектно-ориентированное программирование) и прокси

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

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyApplicationContextConfiguration < @Bean public UserService userService() < // (1) return new UserService(); >>
  1. Предположим, что UserService — это класс, который позволяет вам находить пользователей из таблицы базы данных или сохранять пользователей в этой таблице базы данных.

Вот где проявляется функция скрытая особенность Spring:

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

Однако Spring может обманывать и создавать что-то еще, кроме вашего класса UserService. Как? Почему?

Spring может создавать прокси

Потому что под капотом любой метод Spring @Bean может вернуть вам то, что (в вашем случае) выглядит и ощущается как UserService, но на самом деле это не так.

Он может вернуть вам прокси.

Прокси-сервер в какой-то момент делегирует службу UserService, которую вы написали, но сначала он выполнит свою собственную функциональность.

В частности, Spring по умолчанию создаст динамические прокси-серверы Cglib, которым не нужен интерфейс для работы прокси-серверов (например, внутренний механизм прокси-сервера JDK): вместо этого Cglib может прокси-классы посредством их подклассов на лету. (Если вы не уверены относительно отдельных шаблонов прокси, прочитайте больше о прокси в Википедии.)

Почему Spring желает создавать прокси?

Потому что это позволяет Spring дать вашим компонентам дополнительные функции без изменения кода. В сущности, это то, что является аспектно-ориентированным (или: AOP) программированием.

Давайте рассмотрим самый популярный пример AOP — аннотацию Spring @Transactional .

Spring аннотация @Transactional

Ваша реализация UserService выше может выглядеть примерно так:

import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @Component public class UserService < @Transactional // (2) public User activateUser(Integer id) < // (1) // execute some sql // send an event // send an email >>
  1. Мы написали метод activUser, который при вызове должен выполнить некоторый SQL-запрос, чтобы обновить состояние пользователя в базе данных, возможно, отправить сообщение электронной почты или событие обмена сообщениями.
  2. @Transactional для этого метода сигнализирует Spring, что для работы этого метода необходимо открытое соединение с базой данных / транзакция и что указанная транзакция также должна быть зафиксирована в конце. И Spring должна сделать это.

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

Но что она может сделать, так это создать прокси вокруг вашего UserService, который будет транзакционным. Таким образом, только прокси-сервер должен знать, как открывать и закрывать соединение с базой данных, а затем может просто делегировать его вашему UserService.

Давайте еще раз посмотрим на эту безопасную ContextConfiguration.

@Configuration @EnableTransactionManagement // (1) public class MyApplicationContextConfiguration < @Bean public UserService userService() < // (2) return new UserService(); >>
  1. Мы добавили аннотацию, сигнализирующую Spring: да, нам нужна поддержка @Transactional , которая автоматически включает прокси Cglib под капотом.
  2. С указанным выше набором аннотаций Spring не просто создает и возвращает ваш UserService здесь. Он создает Cglib-прокси вашего компонента, который выглядит, пахнет и делегирует ваш UserService, но фактически оборачивает ваш UserService и предоставляет свои функции управления транзакциями.

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

Нужно ли Spring использовать прокси Cglib?

Прокси являются выбором по умолчанию при программировании AOP с помощью Spring. Однако вы не ограничены использованием прокси, вы также можете пройти полный маршрут AspectJ, который при желании изменяет ваш фактический байт-код. Однако покрытие AspectJ выходит за рамки данного руководства.

Также смотрите раздел: В чем разница между Spring AOP и AspectJ?

Резюме о поддержке Spring AOP

Конечно, о аспектно-ориентированном программировании можно сказать гораздо больше, но это руководство дает представление о том, как работают наиболее популярные сценарии использования Spring AOP, такие как @Transactional или Spring Security, @Secured . Вы можете даже написать свои собственные аннотации AOP, если хотите.

В качестве утешения для неожиданного конца, если вы хотите получить больше информации о том, как подробно работает управление @Transactional в Spring, посмотрите мое руководство по @Transactional .

Управление ресурсами Spring

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

Подумайте, как бы вы попытались получить доступ к файлу в Java через HTTP или FTP. Вы можете использовать класс URL в Java и написать некоторый код.

Точно так же, как бы вы читали в файлах из пути к классам вашего приложения? Или из контекста сервлета, это означает из корневого каталога веб-приложений (по общему признанию, это становится все реже и реже в современном приложении packaged.jar).

Опять же, вам нужно написать довольно много стандартного кода, чтобы он работал, и, к сожалению, код будет отличаться для каждого варианта использования (URL-адреса, пути к классам, контексты сервлетов).

Но есть решение: абстракция ресурсов Spring. Это легко объяснить в коде.

import org.springframework.core.io.Resource; public class MyApplication < public static void main(String[] args) < ApplicationContext ctx = new AnnotationConfigApplicationContext(someConfigClass); // (1) Resource aClasspathTemplate = ctx.getResource("classpath:somePackage/application.properties"); // (2) Resource aFileTemplate = ctx.getResource("file:///someDirectory/application.properties"); // (3) Resource anHttpTemplate = ctx.getResource("https://marcobehler.com/application.properties"); // (4) Resource depends = ctx.getResource("myhost.com/resource/path/myTemplate.txt"); // (5) Resource s3Resources = ctx.getResource("s3://myBucket/myFile.txt"); // (6) >>
  1. Как всегда, вам нужен ApplicationContext для начала.
  2. Когда вы вызываете getResource() для applicationContext со строкой, которая начинается с classpath:, Spring будет искать ресурс в вашем application classpath.
  3. Когда вы вызываете getResource() со строкой, начинающейся с file:, Spring будет искать файл на вашем жестком диске.
  4. Когда вы вызываете getResource() со строкой, которая начинается с https: (или http), Spring будет искать файл в Интернете.
  5. Если вы не укажете префикс, это зависит от того, какой тип контекста приложения вы настроили. Подробнее об этом здесь.
  6. Это не работает из коробки со Spring Framework, но с дополнительными библиотеками, такими как Spring Cloud, вы даже можете напрямую обращаться к путям s3://.

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

public interface Resource extends InputStreamSource < boolean exists(); String getFilename(); File getFile() throws IOException; InputStream getInputStream() throws IOException; // . other methods commented out >

Как видите, он позволяет вам выполнять самые распространенные операции с ресурсом:

  • Это существует?
  • Какое имя файла?
  • Получить ссылку на фактический объект File.
  • Получить прямую ссылку на необработанные данные (InputStream).

Это позволяет вам делать все, что вы хотите с ресурсом, независимо от того, живете ли вы в Интернете, на вашем пути к классам или на жестком диске.

Абстракция ресурсов выглядит как такая крошечная функция, но она действительно сияет в сочетании со следующей удобной функцией, предлагаемой Spring: Properties.

Что такое Spring Environment?

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

В простейшем виде эти свойства находятся в файлах .properties, и их может быть много:

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

Spring пытается упростить вам регистрацию и автоматический поиск свойств во всех этих различных источниках с помощью абстракции environment.

import org.springframework.core.env.Environment; public class MyApplication < public static void main(String[] args) < ApplicationContext ctx = new AnnotationConfigApplicationContext(someConfigClass); Environment env = ctx.getEnvironment(); // (1) String databaseUrl = env.getProperty("database.url"); // (2) boolean containsPassword = env.containsProperty("database.password"); // etc >>
  1. Через applicationContext вы всегда можете получить доступ к текущей среды (environment) выполняемого Spring приложения.
  2. Среда (environment), с другой стороны, позволяет вам, помимо прочего, получать доступ к свойствам.

Что такое environment?

Что такое Spring @PropertySources ?

В двух словах, среда состоит из одного или многих источников свойств выполняемого Spring приложения. Например:

  • /mydir/application.properties
  • classpath:/application-default.properties

(Примечание. Среда также состоит из профилей, то есть профилей «dev» или «production», но мы не будем вдаваться в подробности о профилях в этом пересмотре этого руководства).

По умолчанию среда веб-приложения Spring MVC состоит из параметра ServletConfig/Context, источников системных свойств JNDI и JVM. Они также являются иерархическими, это означает, что они имеют порядок важности и перекрывают друг друга.

Тем не менее, довольно легко определить новые @PropertySources самостоятельно:

import org.springframework.context.annotation.PropertySources; import org.springframework.context.annotation.PropertySource; @Configuration @PropertySources( <@PropertySource("classpath:/com/$/app.properties"), @PropertySource("file://myFolder/app-production.properties")>) public class MyApplicationContextConfiguration < // your beans >

Теперь стало намного понятнее, почему мы говорили об управлении ресурсами Spring раньше. Потому что обе функции идут рука об руку.

Аннотация @PropertySource работает с любым допустимым классом конфигурации Spring и позволяет вам определять новые дополнительные источники с помощью абстракции ресурсов Spring: помните, что все дело в префиксах: http://, file://, classpath: и т.д.,

Определение свойств через @PropertySources — это хорошо, но разве нет лучшего способа, чем пройти через среду, чтобы получить к ним доступ? Да, есть.

Spring аннотация @Value и внедрение значений свойств

Вы можете внедрять значения свойств в ваши bean-компоненты, так же, как вы бы добавили зависимость с аннотацией @Autowired . Но для свойств вам нужно использовать аннотацию @Value .

import org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Value; @Component public class PaymentService < @Value("$") // (1) private String paypalPassword; public PaymentService(@Value("$") String paypalUrl) < // (2) this.paypalUrl = paypalUrl; >>
  1. Аннотация @Value работает непосредственно с полями .
  2. Или по аргументам конструктора.

Там действительно не так много всего. Всякий раз, когда вы используете аннотацию @Value , Spring будет проходить через вашу (иерархическую) среду и искать соответствующее свойство — или выдавать сообщение об ошибке, если такого свойства не существует.

Spring Web MVC

Spring Web MVC, также известный как Spring MVC, является веб-средой Spring. Это позволяет создавать все, что связано с сетью, от небольших веб-сайтов до сложных веб-сервисов. Он также поддерживает фреймворки, такие как Spring Boot.

Что такое MVC?

Если вы совершенно не знакомы с MVC, вы можете прочитать страницу Model-View-Controller в Википедии, прежде чем продолжить чтение.

В контексте рендеринга HTML-страниц, скажем, страницы учетной записи пользователя, вот как выглядит MVC в Spring:

  • Ваша Model (модель) содержит данные, которые вы хотите отобразить на веб-странице. Однако данные полностью независимы от вашего HTML, это простые объекты Java (например, объекты пользователя), из которых состоит ваше приложение.
  • Ваше View (представление) будет HTML-шаблоном, который является каркасом для вашей HTML-страницы, написанной с определенной библиотекой шаблонов. Эти библиотеки позволяют включать заполнители в ваши шаблоны, которые позволяют получить доступ к данным модели, например, имени пользователя.
  • Controller (Контроллер) будет аннотированным методом @Controller , который отвечает на HTTP-запрос /account и знает, как преобразовать HTTP-запрос в объекты Java, а также ваши объекты Java в ответ HTML.

Итак, вашей конечной целью является написание контроллеров с помощью Spring, которые сопоставляются с конкретными HTTP-запросами, такими как (/account), и предоставляют пользователю соответствующую HTML-страницу, которая визуализируется путем объединения представления и данных, необходимых для этой страницы.

Та же логика верна для написания веб-сервисов JSON или XML.

Прежде чем мы рассмотрим реализацию класса типа контроллер, давайте сделаем шаг назад и посмотрим, как мы реализовали бы веб-страницу в низко-низко-низкоуровневом Java: с помощью старого доброго API сервлета Java (на котором основывается Spring MVC). ).

HttpServlet памятка

На данный момент игнорируем MVC: чтобы написать что-нибудь, связанное с HTTP с Java, вы бы использовали сервлеты или, в частности, HttpServlets (примечание для придир: да, есть и другие способы, спасибо за замечание). Сервлеты могут обрабатывать HTTP-запросы, а также возвращать браузеру или клиенту соответствующий HTTP-ответ.

После написания вашего сервлета вы должны зарегистрировать его в контейнере сервлетов, таком как Tomcat или Jetty. Регистрация сервлета всегда включает путь, чтобы указать, за какие URL в вашем веб-приложении отвечает ваш сервлет. Давайте предположим путь «/*», поэтому каждый входящий HTTP-запрос к вашему приложению обрабатывается одним сервлетом.

Вот как может выглядеть этот сервлет:

import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyServlet extends HttpServlet < // (1) @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) < // (2) if (request.getRequestURI().startsWith("/account")) < String userId = request.getParameter("userId"); // return or or for an account get request > else if (request.getRequestURI().startsWith("/status")) < // return or or for a health status get request > // etc > @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) < // (3) // return or or for a post request, like a form submission > >
  1. Ваш сервлет должен расширить Java HttpServlet.
  2. Вы можете переопределить метод doGet(), чтобы обрабатывать запросы Http GET. С отображением сервлета «/» это означает для всех запросов GET. Таким образом, запрос «/status», «/info», «/account» в конечном итоге будет выполнен в одном и том же методе doGet.
  3. Вы можете переопределить метод doPost() для обработки запросов POST Http. С отображением сервлета «/» это означает для всех запросов POST. Таким образом, отправка форм в «/register», «/submit-form», «/password-recovery» в конечном итоге будет осуществляться одним и тем же методом doPost().

Обрабатывать каждый запрос к вашему приложению всего двумя способами — это немного громоздко, и для того, чтобы получить правильный MVC, требуется довольно много работы.

Кроме того, вашему MyServlet (контроллеру) необходимо выполнить довольно много ручного HTTP-специфического подключения, проверки URI запроса, просмотра строк, преобразования requestParameters и ответов и так далее.

Было бы намного приятнее, если бы вам не пришлось заботиться обо всем этом слесарном деле и позволить Spring сделать это за вас. Вот тут и приходит DispatcherServlet.

Что делает DispatcherServlet?

Uber-контроллер в среде Spring MVC Spring представляет собой сервлет, который называется DispatcherServlet.

Он называется DispatcherServlet, потому что он может буквально обрабатывать любой входящий HTTP-запрос, анализировать его содержимое и пересылать данные в виде симпатичных маленьких объектов Java в класс Controller.

Он также достаточно умен, чтобы брать выходные данные с этих контроллеров и конвертировать их в HTML / JSON / XML, в зависимости от того, что подходит. Весь процесс выглядит следующим образом (с пренебрежением большим количеством промежуточных классов, потому что DispatcherServlet не выполняет всю работу сам).

Это именно то, что вы хотите.

  • Spring заботится обо всей HTTP механике.
  • Вы пишете свои контроллеры.
  • А также представления (шаблоны) и модели (ваши Java объекты).

Давайте посмотрим на эти классы типа Controller более подробно.

Как писать классы типа Controller

Наконец, мы можем написать наш Controller класс, который обрабатывает запросы /account.

Страница /account будет HTML-страницей с несколькими динамическими переменными, такими как имя пользователя, адрес, информация о подписке и т.д.

import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; /** * A class that responds to /account requests. * Think of Netflix's account page, where you want to see your username/password/subscription info */ @Controller // (1) public class AccountController < @GetMapping("/account/") public String account(@PathVariable Integer userId) < // (2) // TODO retrieve name, address, subscription information return "templates/account"; // (3) >>

В этих двух строчках происходит МНОГО, давайте посмотрим, что именно.

У вас есть класс AccountController, который аннотируется @Controller . Он сообщает Spring: этот класс хочет реагировать на HTTP-запросы и ответы, чтобы DispatcherServlet знал об этом.

У вас есть простой Java-метод, называемый account(). Что еще интереснее, метод аннотируется @GetMapping . Это сообщает DispatcherServlet, что все запросы, похожие на /account/, должны обрабатываться именно этим методом контроллера. Более того, dispatcherServlet возьмет , преобразует его в целое число и использует его в качестве параметра метода!

Наш метод Java возвращает строку с именем account. На самом деле это не просто строка, а ссылка на представление (HTML-шаблон). Давайте посмотрим на эти шаблоны на секунду.

Как генерировать HTML представление (view) с помощью Spring Web MVC

Spring MVC по умолчанию предполагает, что вы хотите визуализировать некоторый HTML. И, конечно же, вы не хотите сами отображать HTML-строки с помощью конкатенации строк, скорее вы захотите использовать шаблонную среду, такую как Velocity или Freemarker. Spring интегрируется со всеми этими технологиями.

Итак, вы могли бы иметь следующее представление (шаблон):
classpath:/templates/account.vm

  Hello $user.name, this is your account! 

Это очень простая HTML-страница, содержащая одну переменную, $user.name. Как эта переменная попадает в шаблон? Пока что этого не было в нашем контроллере аккаунта. Давайте вставим это.

import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @Controller public class AccountController < @GetMapping("/account/") public String account(Model model, @PathVariable Integer userId) < // (1) // TODO validate user id model.addAttribute("user", userDao.findById(userId)); // (2) return "templates/account"; // (3) >>
  1. Spring может автоматически ввести параметр «Model» в методы вашего контроллера. Как упоминалось ранее, модель содержит любые данные, которые вы хотели бы представить в своем представлении, т.е. ваш шаблон.
  2. Модель Spring ведет себя почти как map (отображение), вы просто добавляете в нее все свои данные и затем можете ссылаться на ключи отображения из вашего шаблона.
  3. Опять же, это ссылка на ваш view, шаблон аккаунта, который мы написали выше.

Вот и все, разработка Spring MVC полностью завершена!

Как генерировать JSON / XML (представления) с помощью Spring Web MVC

С веб-сервисами вы не генерируете HTML, а генерируете XML или JSON. Это довольно просто, с Spring MVC также.

Конечно, вам нужна соответствующая библиотека, такая как Jackson, добавленная в ваш проект, но тогда вы можете просто аннотировать свой Controller дополнительной аннотацией, чтобы сигнализировать Spring: пожалуйста, преобразуйте мои объекты Java напрямую в XML / JSON, вместо того, чтобы я давал вам ссылка на вид.

import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class HealthController < @GetMapping("/health") @ResponseBody // (1) public HealthStatus health() < return new HealthStatus(); // (2) >>
  1. На этот раз вы добавляете дополнительную аннотацию @ResponseBody в метод контроллера, который сообщает Spring, что вы хотите записать свой Java-объект HealthStatus непосредственно в HttpResponse (например, в виде XML или JSON).
  2. Вы просто возвращаете простой Java-объект внутри вашего метода, а не строковую ссылку на ваше представление.

Но как Spring узнает, должен ли он возвращать XML, JSON или что-то еще?

Как работает согласование контента (content negotiation) Spring MVC

Существует множество способов, с помощью которых вы, как клиент, можете указать Spring MVC, какой формат ответа вы хотите при выполнении запроса к приложению Spring MVC.

  • Указав заголовок Accept, такой как «Accept: application/json» или «Accept: application/xml». Это работает «из коробки», но требует определенных библиотек в вашем пути к классам для поддержки сортировки XML или JSON.
  • Добавив расширение URL к вашему пути запроса, например /health.json или /health.xml. Это требует настройки на стороне Spring MVC для работы.
  • Добавив параметр запроса в путь запроса, например /health?Format = json. Это требует настройки на стороне Spring MVC для работы.

Затем Spring знает: вы аннотировали этот метод с помощью @ResponseBody , и клиент хочет «JSON». У меня есть библиотека на пути к классам, как Джексон, которая может визуализировать JSON? Если да, хорошо, давайте преобразуем HealthStatus в JSON. В противном случае выведите исключение.

Если вы хотите узнать больше о согласовании контента, ознакомьтесь с официальной документацией.

Какой тип ввода HTTP-запроса понимает Spring?

Spring MVC понимает в основном все, что предлагает HTTP — с помощью сторонних библиотек.

Это означает, что вы можете выбросить в него тела запросов JSON, XML или HTTP (Multipart) Fileupload, и Spring удобно преобразует этот ввод в объекты Java.

Какие HTTP-ответы может написать Spring MVC?

Spring MVC может записывать все что угодно в HttpServletResponse — с помощью сторонних библиотек.

Будь то HTML, JSON, XML или даже тела ответов WebSocket. Более того, он берет ваши объекты Java и генерирует эти тела ответов для вас.

А как насчет других концепций Spring MVC?

Официальная документация Spring MVC буквально содержит сотни страниц, описывающих, как работает веб-фреймворк.

Поэтому, если вы хотите узнать больше о RequestParams, Моделях, Представлениях, ViewHandlers, RootContexts, Фильтрах, Кэшировании и Безопасности, я приглашаю вас проверить это. Это просто не входит в рамки данного руководства, чтобы охватить все.

Однако в разделе часто задаваемых вопросов данного руководства есть ответы на несколько дополнительных вопросов.

Дополнительно: Кратко о Spring Boot
Хотя это руководство не касается Spring Boot, давайте немного отвлечемся, посмотрев на источник аннотации @RestController , с которой вы, возможно, уже сталкивались в своем проекте Spring Boot.

Вы могли бы подумать, что она присуща Spring Boot (как я ошибочно делал слишком долго), но, как правильно заметил Мацей Волковяк, это часть простого старого Spring MVC. Вот его источник:

// some other annotations left out @Controller @ResponseBody public @interface RestController

Это верно, Spring MVC @RestController — это не что иное, как Spring MVC @Controller в сочетании с аннотацией Spring MVC @ResponseBody — хотя вы можете подумать, что это что-то связанное со Spring Boot.

Резюме: Spring MVC

Spring MVC — это старый добрый фреймворк MVC, который позволяет довольно легко писать HTML / JSON / XML веб-сайты или веб-сервисы. Он прекрасно интегрируется с контейнером внедрения зависимостей Spring, включая все его вспомогательные утилиты.

Вкратце: он позволяет вам сосредоточиться на написании Java-классов, вместо того, чтобы иметь дело с сланцевым кодом сервлета, то есть работать с Http-запросами и ответами, и дает вам хорошее разделение проблем между вашей моделью и представлениями.

Дополнительные модули Spring Framework

Мы рассмотрели контейнер IoC Spring, Spring Web MVC и несколько других небольших модулей Spring. Но есть еще много. Что они делают?

О чем дополнительные модули Spring Framework?

Spring Framework состоит из еще большего количества удобных утилит, чем вы видели до сих пор. Давайте назовем их модулями и не путайте эти модули с 20 другими проектами Spring на spring.io. Наоборот, все они являются частью рамочного проекта Spring.

Итак, о каком удобстве идет речь?

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

Вот пример: отправка вложений электронной почты с помощью Java API Почты, безусловно, выполнима, но немного неудобна в использовании. Смотрите здесь примера кода.

Spring предоставляет приятную маленькую API-оболочку поверх Java Mail API, с тем дополнительным преимуществом, что все, что он предлагает, прекрасно вписывается в контейнер внедрения зависимостей Spring.

import org.springframework.core.io.FileSystemResource; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; public class SpringMailSender < @Autowired private JavaMailSender mailSender; // (1) public void sendInvoice(User user, File pdf) throws Exception < MimeMessage mimeMessage = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true); // (2) helper.setTo("john@rambo.com"); helper.setText("Check out your new invoice!"); FileSystemResource file = new FileSystemResource(pdf); helper.addAttachment("invoice.pdf", file); mailSender.send(mimeMessage); >>
  1. Все, что связано с настройкой почтового сервера (URL, имя пользователя, пароль), абстрагируется от класса MailSender, специфичного для Spring, который вы можете внедрить в любой компонент, который хочет отправлять электронную почту.
  2. Spring предлагает конструкторы удобства, такие как MimeMessageHelper, для создания составных электронных писем, скажем, из файлов, как можно быстрее.

Итак, подытоживая, цель Spring Framework состоит в том, чтобы «упростить» доступную функциональность Java, подготовить ее к внедрению зависимостей и, следовательно, упростить использование API в контексте Spring.

Есть ли список всех дополнительных модулей Spring Framework?

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

  • Spring’s Data Access: не путать с библиотеками Spring Data (JPA / JDBC). Это основа для поддержки Springs @Transactional , а также чистой интеграции JDBC и ORM (например, Hibernate).
  • Spring Integration модули: упрощает отправку электронных писем, интеграцию с JMS или AMQP, планирование задач и т. Д.
  • Spring Expression Language (SpEL): Даже если это не совсем правильно, думайте о нем как о DSL или Regex для создания / конфигурации / внедрения Spring Bean. Это будет описано более подробно в следующих версиях этого руководства.
  • Реактивные модули Spring. Позволяет писать реактивные веб-приложения.
  • Фреймворк тестирования Spring. Позволяет (интегрировать) тестировать контексты Spring и, следовательно, приложения Spring, включая вспомогательные утилиты для тестирования служб REST.

Spring Framework: FAQ

Какую версию Spring я должен использовать?

Выбор версии Spring относительно прост:

  • Если вы создаете новые проекты Spring Boot, используемая вами версия Spring уже предопределена. Например, если вы используете Spring Boot 2.2.x, вы будете использовать Spring 5.2.x (хотя теоретически вы можете переопределить это).
  • Если вы используете обычный Spring в новом проекте, вы можете выбрать любую версию, какую захотите. Текущая последняя версия Spring 5.2.3.RELEASE, и вы всегда можете найти анонсы новых версий в блоге Spring.
  • Если вы используете Spring в унаследованном проекте, вы всегда можете подумать о переходе на более новую версию Spring, если это имеет смысл с точки зрения бизнеса (или если вы хотите следовать EOL анонсу) — версии Spring имеют высокую степень совместимости (см. следующий параграф).

В реальности вы можете найти проекты на Spring версии 4.x-5.x, используемые компаниями, хотя имеются и редкие, унаследованные проекты на Spring 3.x (первоначальный выпуск: 2009).

Как часто выпускаются новые версии Spring? Как долго они поддерживаются?

Вот хороший маленький график, показывающий историю версий Spring:

Вы можете видеть, что первоначальная версия Spring была ~ 17 лет назад, а основные версии платформы выпускались каждые 3-4 года. Это не учитывает филиалы обслуживания, как бы то ни было.

  • Например, Spring 4.3 был выпущен в июне 2016 года и будет поддерживаться до конца 2020 года.
  • Даже поддержка Spring 5.0 и 5.1 будет прекращена в конце 2020 года, в пользу Spring 5.2, которая была выпущена в сентябре 2019 года.

Примечание. Текущий EOL() (больше никаких обновлений и поддержки) для всех версий Spring, кроме 5.2, в настоящее время настроен на 31 декабря 2020 года.

Какие библиотеки вам нужны, чтобы начать работу с Spring?

На самом деле существует только одна зависимость, которая вам нужна, когда вы хотите настроить проект Spring. Это называется spring-context. Это абсолютный минимум, чтобы заставить работать инъекционный контейнер Spring.

Если вы работаете над проектом Maven или Gradle, вы можете просто добавить к нему следующую зависимость (см. также вопрос выше: Какую версию Spring мне следует использовать?) — вместо загрузки указанного файла .jar и добавления его в ваш проект вручную.

  org.springframework spring-context 5.2.3.RELEASE 
// Gradle compile group: 'org.springframework', name: 'spring-context', version: '5.2.3.RELEASE'

Для дополнительных функций Spring (таких как поддержка Spring JDBC или JMS) вам потребуются другие дополнительные библиотеки.

Вы можете найти список всех доступных модулей в официальной документации Spring, хотя с точки зрения зависимостей Maven artifactIds действительно следуют за именем модуля. Вот пример:

  org.springframework spring-webmvc 5.2.3.RELEASE  org.springframework spring-jdbc 5.2.3.RELEASE 

Чем отличаются версии Spring?

Подобно JVM, версии Spring безумно обратно совместимы, что означает, что вы можете (по существу) по-прежнему запускать файлы Spring 1.0 xml с последней версией Spring 5.0 (хотя я, по общему признанию, еще не пробовал это). Кроме того, обновление с, скажем, 3 до 5 также возможно с небольшими усилиями (см. Это руководство по миграции).

Таким образом, в целом, новые версии Spring основаны на более старых версиях Spring и имеют минимальные критические изменения (по сравнению, скажем, с Python 2 против 3). Итак, все основные концепции, которые вы изучили для Spring версии 3 или 4, остаются верными для Spring версии 5.

Вы можете получить отличный обзор того, что изменилось за последние 7 лет в отдельных версиях Spring, здесь:

Чтобы дать вам резюме:

Ядро (внедрение зависимостей, управление транзакциями и т.д.) Всегда остается неизменным или расширяется. Однако Spring идет в ногу со временем и предлагает поддержку для новых версий языка Java, улучшений инфраструктуры тестирования, веб-сокетов, реактивного программирования и т.д.

Что сейчас происходит в 20 других проектах Spring.io? Как насчет изменения реального байт-кода?

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

  • Spring Boot: Пожалуй, самый популярный проект Spring. В Spring Boot принят «компетентный» подход к Spring Framework. Посмотрите ниже вопрос про разницу между Spring и Spring Boot чтобы выяснить, что на самом деле означает эта довольно бессмысленная фраза.
  • Spring Batch: библиотека, которая помогает вам писать старые добрые пакетные задания.
  • Spring Cloud: набор библиотек, которые помогают вашему проекту Spring легче интегрироваться с «облаком» (например, AWS) или писать микросервисы.
  • Spring Security: библиотека, которая помогает вам защитить, например, ваше веб-приложение с OAuth2 или Basic Auth.
  • и многое другое .

Вывод: все эти библиотеки расширяют Spring Framework и основываются на его основных принципах внедрения зависимостей.

В чем разница между Spring и Spring Boot?

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

Spring предлагает вам возможность читать .properties файлы из разных мест, например с помощью аннотаций @PropertySource . Он также предлагает вам возможность писать контроллеры JSON REST с помощью своей инфраструктуры Web MVC.

Проблема в том, что вы должны сами написать и настроить все эти отдельные части. Spring Boot, с другой стороны, берет эти отдельные части и связывает их вместе. Пример:

  • Всегда и автоматически ищите файлы application.properties в разных местах и ​​читайте их.
  • Всегда загружайте встроенный Tomcat, чтобы вы могли сразу увидеть результаты работы ваших Rest контроллеров.
  • Автоматически настраивайте все для отправки / получения JSON, не беспокоясь о конкретных зависимостях Maven / Gradle.

Все это запускается основным методом в классе Java, который снабжен аннотацией @SpringBootApplication . Более того, Spring Boot предлагает плагины Maven / Gradle, которые позволяют вам упаковать ваше приложение в файл .jar, который вы можете запустить следующим образом:

java -jar mySpringBootApp.jar

Итак, Spring Boot — это все, что нужно для того, чтобы собрать существующие части Spring Framework, предварительно сконфигурировать и упаковать их с минимальными затратами на разработку.

В чем разница между Spring AOP и AspectJ?

Как упомянуто выше в разделе Нужно ли Spring использовать прокси Cglib?» в Spring по умолчанию используется AOP на основе прокси. Он оборачивает ваши компоненты в прокси для достижения таких вещей, как управление транзакциями. Это имеет несколько ограничений и предостережений, но является довольно простым и понятным способом реализации наиболее распространенных проблем AOP, с которыми сталкиваются разработчики Spring.

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

Однако вы можете настроить Spring на использование AOP AspectJ вместо AOP по умолчанию на основе прокси.

Вот пара ссылок, если вы хотите получить больше информации по этой теме:

  • Домашняя страница AspectJ
  • Spring AOP и AspectJ
  • Spring AOP официальная документация

В чем разница между Spring и Spring Batch?

Spring Batch — это фреймворк, упрощающий написание пакетных заданий, т.е. «Читать эти 95 CSV-файлов каждую ночь в 3 часа ночи и вызывать внешнюю службу проверки для каждой записи».

Опять же, он построен на основе Spring Framework, но во многом является собственным проектом.

Тем не менее, обратите внимание, что по сути невозможно создать надежное пакетное задание без хорошего понимания общего управления транзакциями в Spring Framework и его отношения к Spring Batch.

В чем разница между Spring и Spring Web MVC?

Если вы прочитали это руководство, вы должны понимать, что Spring Web MVC является частью среды Spring.

На очень высоком уровне это позволяет вам превратить ваше Spring-приложение в веб-приложение с помощью DispatcherServlet, который маршрутизирует классы @Controller .

Это могут быть RestControllers (где вы отправляете XML или JSON клиенту) или старые добрые HTML-контроллеры, где вы генерируете HTML с такими фреймворками, как Thymeleaf, Velocity или Freemarker.

В чем разница между Spring и Struts?

Вопрос должен быть: чем отличается Spring Web MVC от Struts?

Краткий исторический ответ таков: Spring Web MVC начинал как конкурент Struts, который, как утверждается, был плохо спроектирован разработчиками Spring (см. Википедию).

Современный ответ заключается в том, что, хотя Struts 2, безусловно, все еще используется в странном устаревшем проекте, Spring Web MVC является основой для всего, что связано с сетью во вселенной Spring. От Spring Webflow до RestControllers Spring Boot.

Что лучше? Spring XML или аннотации или Java конфигурация?

Spring начинался только с конфигурации XML. Затем медленно, все больше и больше появлялись аннотации / возможности Java конфигурации.

Сегодня вы заметитите, что конфигурация XML в основном используется в более старых, унаследованных проектах, а в новых проектах используется конфигурации на основе Java или аннотаций.

Обратите внимание на две вещи:

  1. По сути, ничто не мешает объединить XML / Аннотации / Java Config в одном проекте, но это обычно приводит к путанице.
  2. Вы должны стремиться к однородности в своей конфигурации Spring, то есть не генерировать случайным образом некоторые конфигурации с XML, некоторые с конфигурацией Java, а некоторые с компонентным сканированием.

Что лучше? Внедрение зависимостей на основе конструктора или поля?

Как уже упоминалось в разделе о внедрении зависимостей, этот вопрос вызывает много разных мнений. Самое главное, что ваш выбор должен быть одинаковым для всего вашего проекта: не используйте внедрение на основе конструктора для 83% ваших бинов и внедрение на основе поля для остальных 17%.

Разумным подходом будет использование рекомендованного способа в документации Spring: использование внедрения конструктора для обязательных зависимостей, установка / установка поля для необязательных зависимостей, а затем проверка этих дополнительных зависимостей по всему классу на ноль.

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

Есть ли альтернативы контейнеру внедрения зависимостей в Spring?

Да, две популярные в экосистеме Java:

Обратите внимание, что Dagger предлагает только внедрение зависимостей, без дополнительных удобных функций. Guice предлагает внедрение зависимостей и другие функции, такие как управление транзакциями (с помощью Guice Persist).

Заключение

Если вы читали это далеко, то теперь у вас должно быть достаточно глубокое понимание того, что такое Spring Framework.

Вы узнаете, как это связано с другими библиотеками экосистемы Spring (такими как Spring Boot или Spring Data) в последующих руководствах, но сейчас я хочу, чтобы вы помнили эту метафору при попытке ответить на вопрос «Что такое Spring Фреймворк?»

Представьте, что вы хотите отремонтировать дом (~ = создать программный проект).

Spring Framework — это ваш DIY-магазин (~ = контейнер для инъекций зависимости), который предлагает множество различных инструментов, от горелок Бунзена (~ = ресурсы / свойства) до кувалд (~ = Web MVC) для вашего обновления. Эти инструменты просто помогут вам быстрее и удобнее отремонтировать ваш дом (создать приложение на Java).

(примечание: не спрашивайте меня, как я придумал эти сравнения;))

Вот и все на сегодня. Если у вас есть какие-либо вопросы или предложения, напишите мне по адресу marco@marcobehler.com или оставьте комментарий ниже. Для практических занятий ознакомьтесь с учебным курсом Spring Framework.

Спасибо за прочтение. Auf Wiedersehen.

Благодарности

  • Patricio Moschcovich, за то, что он проделал потрясающую работу, вычитав эту статью и указав на кучу небольших ошибок.
  • Maciej Walkowiak за то, что указал, что @RestController всегда был частью Spring MVC, а не Spring Boot.

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

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