Skip to content
Gallery
Theory for java developer
Share
Explore
Java

icon picker
Spring Boot

Фреймворк - это внешний каркас, предоставляющий заранее определенные точки расширения. В эти точки расширения вы и вставляете свой код, но когда он будет вызван определяет именно фреймворк. Создавать объекты классов и вызывать методы за нас будет уже сам фреймворк.
image.png

Spring - общее названия для целого ряда небольших фреймворков, каждый из которых выполняет свою работу. Как видно, у спринга модульная структура. Это позволяет подключать только те модули, что нам нужны для нашего приложения и не подключать те, которыми мы заведомо не будем пользоваться. Именно этот подход и помог спрингу обойти своего конкурента в то время (EJB).
Dependency Inversion(DI) - инверсию зависимостей, то есть попытки не делать жестких связей между вашими модулями/классами, где один класс напрямую завязан на другой (SOLID - D):
Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций.
Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Принцип который говорит о том что сущность не сама создает свои зависимости, они подставляются из вне, а DI(ниже) это частный случай.
Inversion of Control (IoC) — инверсия управления, при использовании библиотеки вы сами прописываете в своем коде какой метод какого объекта вызвать, а в случае с фреймворками уже фреймворк будет вызывать в нужный ему момент тот код, который вы написали. Фреймворк управляете процессом выполнения программы за вас. Вы передали ему управление (инверсия управления).
Dependency Injection(DI) — внедрение зависимостей, специальный паттерн, который уменьшает связь между компонентами. При применении DI, ваш код становится легче понимать и тестировать. Согласно паттерну DI, создание объектов для зависимостей переходит на фабрику или отдается третьей стороне. Это означает, что мы можем сосредоточиться на использовании этих объектов вместо их создания.
Это когда объекты создаются не вы в main-е и потом передаются в методы своих объектов, а за вас их внедряет спринг(если они помечены аннотацией), а вы ему просто говорите что-то типа "хочу сюда получить тот объект" и он вам его передает в ваш метод.
ApplicationContext или как сделать свой IoC - это набор бинов (объектов), реализация простая:
У нас есть куча классов с кучей методов, где иногда нам нужен для метода котик, а для другого метода — собачка, а в каких-то методах — все два.
Да, мы можем в main-е сначала создать эти два объекта, а потом их передавать в наши классы, а уже внутри классов — в нужные нам методы... И так по всей программе. Сложно, не красиво. Тогда мы решаем хранить мапу, где ключ - имя нужного нам объекта, а значением — сам объект.
Мы сможем доставать нужные нам объекты по их имени: get("попугайчик") и в ответ получили объект попугайчика. Или ключ — это класс объекта, а значение — сам объект, тогда мы сможем указать уже не имя объекта, а просто класс нужного нам объекта, тоже удобно.
Написать обертку над мапой, чтобы в каких-то случаях доставать объекты по их имени, а в других случаях — по классу.
Написать специальные аннотации чтобы они автоматически подтягивали по имени поля объект из map. И мы каждый раз не обращались к мапе и не тащили ее через все объекты.
Мы переложили ответственность за создание объектов, передачу их в методы - спрингу (IoC).
Bean - это объект класса который может храниться в ApplicationContext и связываться(подставляться) в нужное место при его внедрении. В Spring-е бином (bean) называют любой класс, который управляется контейнером Spring. То есть такими вещами, как создание экземпляра бина, его инициализация, внедрение зависимостей и параметров, деинициализация, генерация всевозможных оберток над бином, занимается не наш код, а IoC-контейнер Spring.

Proxy

image.png
Тут не вызовется @Transactional по тому что нет Proxy т.к. создается в том же классе: 1) Переместить метод createAccount(Account acc) в другой класс. 2) использовать селф инициализацию

Bean Scope - 6

singleton - создается один раз инжектится один и тот же бин.
prototype - при каждом запросе создает новый.
-----------Web Scope----------
request - создает экземпляр бина для одного HTTP-запроса.
session - создает экземпляр бина для HTTP-сессии.
application - Это похоже на область видимости singleton, но есть очень важное отличие в области видимости. Тот же экземпляр бина используется в нескольких приложениях на основе сервлетов, работающих в одном и том же контексте сервлета, в то время как бины с областью видимости singleton ограничены только одним контекстом приложения.
websocket - Создается один раз и виден во всей сессии Websocket, он проявляет себя как singleton, но ограниченный только сессией WebSocket.

Жизненный цикл Bean

С бинами происходит множество процессов под капотом. Во многие можно вмешаться, добавив собственную логику в разные этапы жизненного цикла. Через следующие этапы проходит каждый отдельно взятый бин:
Инстанцирование объекта - техническое начало жизни бина, работа конструктора его класса;
Внедрение зависимостей - установка свойств из конфигурации бина;
Нотификация aware-интерфейсов - BeanNameAware, BeanFactoryAware и другие. Технически, выполняется системными подтипами BeanPostProcessor, и совпадает с шагом 4;
Пре-инициализация - метод postProcessBeforeInitialization() interface BeanPostProcessor;
Инициализация - разные способы применяются в таком порядке:
Метод бина с аннотацией @PostConstruct - из стандарта JSR-250 (рекомендуемый способ);
Метод - afterPropertiesSet() interface InitializingBean;
Init-метод - для отдельного бина его имя устанавливается в параметре определения initMethod. В xml-конфигурации можно установить для всех бинов сразу, с помощью default-init-method;
Пост-инициализация - метод postProcessAfterInitialization() interface BeanPostProcessor.
Уничтожение бина - Когда IoC-контейнер завершает свою работу, мы можем кастомизировать этот этап. Как со всеми способами финализации в Java, при жестком выключении (kill -9) гарантии вызова этого этапа нет. Три альтернативных способа «деинициализации» вызываются в том же порядке, что симметричные им методы инициализации:
Метод с аннотацией @PreDestroy;
Метод с именем, которое указано в свойстве destroyMethod определения бина (или в глобальном default-destroy-method);
Метод destroy() interface DisposableBean.
image.png
Жизненный цикл компонента состоит из семи этапов:
1. Создать экземпляр: Компонент создается контейнером Spring с использованием определения компонента, найденного в файле конфигурации XML.
2. Заполнение свойств: Spring заполняет все определенные свойства из XML-файла с помощью внедрения зависимостей.
3. Установить имя компонента: Spring передает идентификатор компонента методу setBeanName (), если компонент использует интерфейс BeanNameAware.
4. Установить baen factory: Spring передает beanfactory методу setBeanFactory (), если компонент настроен на использование интерфейса BeanFactoryAware.
5. Предварительная инициализация: Spring вызывает любые BeanPostProcessors, связанные с компонентом, с помощью метода postProcessorBeforeInitialization ().
6. Инициализация: Затем инициализируется компонент. Выполняется любой специальный процесс инициализации, указанный в методе инициализации.
7. Постинициализация: Вызываются все определенные методы postProcessAfterInitialization (). Теперь bean завершен. Компоненты, реализующие DisposableBean, будут удалены с помощью функции destroy() после завершения их работы.
image.png
Не путать жизненный цикл отдельного бина с жизненным циклом контекста и этапами подготовки фабрик бинов.

Типы Bean and Annotation

image.png

Основные аннотации

@SpringBootApplication - определяет автоматическое сканирование пакета, где находится главный класс. Если ваш код целиком находится в указанном пакете или его подпакетах - ОК, но если бин вне этого пакета, вы должны использовать аннотацию - ComponentScan, где перечислите дополнительные пакеты для сканирования. Эта аннотация включает другие:
@Configuration
@EnableAutoConfiguration
@ComponentScan - Показывает в каких пакетах и их под пакетах искать где искать бины.
@Bean - Ставится над методом в классе с аннотацией @Configuration. Он отделяет объявление компонента от определения класса и позволяет создавать и настраивать компоненты на ваше усмотрение.По умолчанию имя компонента совпадает с именем метода.
@Component / @Service / @Repository - Используется для автоматического обнаружения бинов, настраивает их спринг, между анноируемым классом и компонентом однозначное сопоставление.
@Service - Тоже самое что и Component но смысл что ставятся над классами с бизнес логику.
@Repository - Тоже самое что и Component но смысл что ставятся над классами с подключением к бд, также имеют обработку JDBC Exception.
@Autowired - Подставляет(Binding / Связывание) бин из контекста в нужное место: В сеттер, конструктор (если он один можно просто создать приватное поле без @Autowired, все равно будет инжект). Параметр required = false, если бин не найден не падает с NoSuchBeanDefinitionException. Best practice - не использовать @Autowired над полями:
Повышается coupling (сцепление = плохо) - внедрении прямо в поля вы не предоставляете прямого способа создания экземпляра класса со всеми необходимыми зависимостями.
Скрытие зависимостей - Когда класс более не отвечает за получение зависимостей, он должен явно взаимодействовать с ними, используя публичные интерфейсы — методы или конструкторы. Таким образом становится четко понятно, что требует класс, а также опциональные ли это зависомости (через сеттеры) или обязательные (конструктор).
Зависимость от DI-контейнера - класс не должен зависеть от конкретного используемого контейнера. Другими словами, это должен быть простой POJO-класс, экземпляр которого может быть создан самостоятельно, если вы передадите ему все необходимые зависимости. Таким образом, вы можете создать его в юнит-тесте без запуска контейнера и протестировать его отдельно.
@Qualifier / @Primary - Если бинов одного типа несколько (name1 и name2), способы:
inject @Qualifier(name1) - выбирает этот бин по имени.
над бином name1 @Primary - выбирает этот бин по умолчанию.
@Lazy - Если у нас цикличная зависимость бинов или Refactoring т.к плохая архитектура.

Web аннотации (используют Servlet)

@Controller - служит для авто обнаружения бина этого класса. Может вернуть – mvc view.
@RequestMapping(value = "/simple1") сообщаем, что данный метод контроллера или весь класс будет обрабатывать запрос, URI которого "/simple1". Если хотим вернуть данные @ResponseBody.
@ResponseBody - возаращает body значение в ответ на запрос, имеет статусы SUCCESS(200) и SERVER ERROR(500)
@ResponseEntity - если мы хотим кастомизировать ответ, добавив к нему статус. Во всех остальных случаях будем использовать @ResponseBody.
@RestController = @Controller + @ResponseBody - превращает помеченный класс в бин. Этот бин для конвертации входящих/исходящих данных использует Jackson. Как правило данные представлены в json или xml. Не возвращает view.

Другие аннотации

@Scheduled - запускает метод(задачу) по расписанию используя эту аннотацию. ​cron = "0 15 10 15 * ?", zone = "Europe/Paris" - запускать в 10:15 каждый 15-й день месяца по Парижу.
@ConditionalOnProperty(value="project.mq.enabled", matchIfMissing = false) - Самым распространенным способом управления контекстом спринга являются профили (@Profile). Они позволяют быстро и просто регулировать создание бинов. Но иногда может потребоваться более тонкая настройка.
Аннотация
Описание
1
ConditionalOnBean
в случае если присутствует нужный бин в BeanFactory.
2
ConditionalOnClass
если нужный класс есть в classpath.
3
ConditionalOnCloudPlatform
когда активна определенная платформа.
4
ConditionalOnExpression
когда SpEL выражение вернуло положительное значение.
5
ConditionalOnJava
когда приложение запущено с определенной версией JVM.
6
ConditionalOnJndi
только если через JNDI доступен определенный ресурс.
7
ConditionalOnMissingBean
в случае если нужный бин отсутствует в BeanFactory.
8
ConditionalOnMissingClass
если нужный класс отсутствует в classpath.
9
ConditionalOnNotWebApplication
если контекст приложения не является веб контекстом.
10
ConditionalOnProperty
если в файле настроек заданы нужные параметры.
11
ConditionalOnResource
если присутствует нужный ресурс в classpath.
12
ConditionalOnSingleCandidate
бин уже содержится в BeanFactory и он единственный.
13
ConditionalOnWebApplication
если контекст приложения является веб контекстом.
There are no rows in this table

Filters, Listeners, Interceptors

Filters - ИНТЕРФЕЙС из пакета javax.servlet, его имплементации выполняют фильтрацию. В веб-приложении мы можем написать несколько фильтров, которые вместе называются цепочкой фильтров. Выполняется, в соответствии с порядком регистрации фильтров.
Фильтры - это часть стандартного веб-контейнера (например, Apache Tomcat) и применяются до и после обработки запроса и ответа. Они могут обрабатывать HTTP-запросы и ответы, а также выполнять некоторые операции, такие как логирование, аутентификация, авторизация, фильтрация запросов и другие общие операции.
Фильтры находятся ниже уровня Spring и обрабатывают запросы и ответы независимо от контекста Spring.
В Spring вы также можете определить собственные фильтры, реализовав интерфейс javax.servlet.Filter.
Listeners - это класс, который реализует интерфейс javax.servlet.ServletContextListener. Он инициализируется только ОДИН раз при запуске веб-приложения. Слушатель ждет, когда произойдет указанное событие, затем «перехватывает» событие и запускает собственное событие.
Слушатели - это компоненты, которые позволяют приложению реагировать на различные события жизненного цикла веб-приложения, такие как запуск и остановка приложения, создание и уничтожение сессии, атрибуты контекста и другие события.
Слушатели также являются частью стандартного веб-контейнера и не специфичны для Spring.
В Spring вы можете определить свои собственные слушатели, реализовав интерфейсы javax.servlet.ServletContextListener или javax.servlet.http.HttpSessionListener.
Interceptors - Это ИНТЕРФЕЙС из пакета org.aopalliance.intercept, предназначенный для AOP аспектно-ориентированного программирования. Предположим, у нас есть простое веб-приложение Spring MVC, которое содержит один контроллер для обработки запроса на показ приветственного сообщения. Мы можем создать перехватчик, который будет записывать время обработки каждого запроса и выводить его в лог.
Перехватчики - это механизмы Spring MVC, которые позволяют вам встраиваться в процесс обработки запросов и ответов перед и после выполнения контроллеров.
Они работают на уровне Spring MVC и позволяют выполнять общие задачи, такие как аутентификация, логирование, изменение модели, контроль доступа и другие.
Перехватчики специфичны для Spring MVC и являются частью контекста Spring.
В Spring вы можете определить свои собственные перехватчики, реализовав интерфейс org.springframework.web.servlet.HandlerInterceptor.

MVC (Model-View-Controller)

Контроллер - управляет запросами пользователя. Его основная функция — вызывать и координировать действие необходимых ресурсов и объектов.
Модель - Бизнес-логика.
Вид - обеспечивает различные способы представления данных, которые получены из модели.
Веб приложение обычно состоит из набора контроллеров, моделей и видов.

Свой стартер спринг

@Configuration
public class TelegramBotAutoconfiguration {
@Bean(AUTORESPONDER_EXECUTORS_SERVICE)
public ExecutorService executorService(
Пока это ничем не примечательный модуль с обычным классом конфигурации, что делает его стартером? Стартером его сделает особый файл, создадим его.
В папке resources создаём папку META-INF, в ней папку spring, и в ней файл org.springframework.boot.autoconfigure.AutoConfiguration.imports.
Этот файл должен содержать перечисление конфигураций. В моём случае это одна конфигурация. Каждую конфигурацию указывайте с новой строки.
dev.struchkov.godfather.telegram.starter.config.TelegramBotAutoconfiguration
SpringBoot автоматически найдёт этот файл, возьмёт перечисленные конфигурации и создаст указанные в них бины, добавив их в контекст. Так класс конфигурации превращается в класс автоконфигурации. Вот и вся магия 🪄

Reactive Programming

традиционное императивное программирование имеет некоторые ограничения, когда дело доходит до удовлетворения требований сегодняшнего дня, когда приложения должны иметь высокую доступность и обеспечивать низкое время отклика даже при высокой нагрузке.
Традиционный Spring MVC
Контейнер сервлетов имеет выделенный пул потоков для обработки 1 HTTP-запросов = 1 поток
Этот поток будет обрабатывать весь жизненный цикл запроса (модель «поток на запрос»).
Сумма одновременных запросов = размеру пула потоков.
Чем больше пул тем больше потребление памяти.
дорогостоящей для приложений с большим количеством одновременных запросов.
потоки часто блокируются в ожидании ответа от другой службы, что приводит к огромной трате ресурсов.
Другой проблемой традиционного императивного программирования является время отклика, когда службе необходимо выполнить более одного запроса ввода-вывода
Реактивный подход
отходим от модели поток на запрос и можем обрабатывать больше запросов с небольшим количеством потоков
предотвращаем блокировку потоков при ожидании завершения операций ввода-вывода
упрощаем параллельные вызовы
поддерживаем «обратное давление», давая клиенту возможность сообщить серверу, с какой нагрузкой он может справиться
Спецификация Reactive Streams (java 9) включает следующие интерфейсы:
Publisher - представляет производителя данных/источник данных и имеет один метод, который позволяет подписчику зарегистрироваться на издателе.
Subscriber - методы:
public interface Subscriber<T> {
public void onSubscribe(Subscription s); // Publisher перед началом обработки
public void onNext(T t); // чтобы сигнализировать о том, что был отправлен новый элемент
public void onError(Throwable t); // произошел сбой Publisher и больше никаких элементов не будет
public void onComplete(); // все элементы были успешно отправлены
}
Subscription - содержат методы, которые позволяют клиенту управлять выдачей элементов Publisher (т.е. обеспечивать поддержку противодавления).
request позволяет Subscriber сообщить, Publisher сколько дополнительных элементов будет опубликовано
cancel позволяет подписчику отменить дальнейшую отправку элементов Publisher

Project reactor / WebFlux

Это библиотека Reactive для создания неблокирующих приложений на JVM, основанная на спецификации Reactive Streams. Reactor - это основа реактивного стека в экосистеме Spring, и он разрабатывается в тесном сотрудничестве со Spring. WebFlux, веб фреймворк с реактивным стеком Spring, использует Reactor в качестве базовой зависимости.

Flux and Mono

Flux - это Publisher, который может испускать от 0 до N элементов, а Mono - испускать от 0 до 1 элемента. Оба они завершаются либо сигналом завершения, либо ошибкой, и они вызывают методы onNext, onComplete и onError нижестоящего подписчика. Помимо реализации функций, описанных в спецификации Reactive Streams, Flux и Mono предоставляют набор операторов для поддержки преобразований, фильтрации и обработки ошибок.

RSocket

Протокол, моделирующий семантику реактивных потоков по сети. Это двоичный протокол для использования в транспортных потоках байтовых потоков, таких как TCP, WebSockets и Aeron. В качестве введения в эту тему я рекомендую следующий пост в блоге, который написал мой коллега Pär:

Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.