Skip to content

Latest commit

 

History

History
2411 lines (2000 loc) · 249 KB

Cобеседование по Java. Spring Framework.md

File metadata and controls

2411 lines (2000 loc) · 249 KB

Cобеседование по Java. Разбор вопросов и ответов.

     

Нажмите ★, если вам нравится проект. Ваш вклад сердечно ♡ приветствуется.

Если вам интересно мое резюме: https://github.com/DEBAGanov

  1. Что такое Spring Framework?

  2. Какие основные преимущества Spring Framework?

  3. Что такое IoC (Inversion of Control) и DI (Dependency Injection)?

  4. Какие способы реализации DI вы знаете?

  5. Какие архитектурные шаблоны используются в Spring Framework?

  6. Что такое AOP (Aspect-Oriented Programming)?

  7. Какие аннотации в Spring Framework вы знаете? Для чего они используются?

  8. Как использовать Spring MVC для создания web-приложений?

  9. Какие компоненты Spring MVC вы знаете?

  10. Какой контейнер сервлетов поддерживает Spring MVC?

  11. Как обрабатывать исключения в Spring MVC?

  12. Как обрабатывать формы в Spring MVC?

  13. Как производится валидация данных в Spring MVC?

  14. Как использовать Hibernate в Spring Framework?

  15. Как использовать JDBC Template в Spring Framework?

  16. Как использовать JPA в Spring Framework?

  17. Какие ORM фреймворки вы знаете? Как они работают с Spring Framework?

  18. Какие библиотеки доступны для работы с базами данных в Spring Framework?

  19. Как использовать Spring Security для обеспечения безопасности в web-приложениях?

  20. Как обеспечить транзакционность в Spring Framework?

  21. Какие уровни логирования вы знаете? Как настроить логирование в Spring Framework?

  22. Как использовать Spring Boot для создания приложений?

  23. Что такое RESTful сервисы? Как их можно реализовать в Spring Framework?

  24. Как добавить Swagger в ваше Spring Boot приложение?

  25. Как работать с Kafka в Spring Framework?

  26. Что такое RabbitMQ? Как его использовать в Spring Framework?

  27. Как работать с NoSQL базами данных в Spring Framework?

  28. Что такое Spring Cloud? Какие компоненты он включает?

  29. Как использовать Spring Data JPA для работы с базами данных?

  30. Как использовать Spring Batch для пакетной обработки данных?

  31. Как использовать Spring Integration для интеграции приложений?

  32. Как использовать Spring Mobile для разработки мобильных приложений?

  33. Как использовать Spring Social для интеграции приложений социальных сетей?

  34. Как использовать Spring Web Services для разработки веб-сервисов?

  35. Как использовать Spring HATEOAS для создания RESTful сервисов с гиперссылками?

  36. Как использовать Spring WebSocket для обмена данными в реальном времени?

  37. Как использовать Spring AMQP для работы с асинхронной очередью сообщений?

  38. Как использовать Spring Cloud Stream для разработки приложений, работающих с потоками данных?

  39. Как использовать Spring Cloud Config для централизованного управления конфигурацией приложений?

  40. Как использовать Spring Cloud Netflix для разработки микросервисных приложений?

  41. Как использовать Spring Cloud Kubernetes для развертывания и управления контейнерами на Kubernetes?

  42. Как используются тесты в Spring Framework?

  43. Какие типы тестов вы знаете? Какие инструменты для тестирования вам известны?

  44. Как тестировать RESTful сервисы в Spring Framework?

  45. Как тестировать JPA репозитории в Spring Framework?

  46. Как использовать Mockito для создания заглушек?

  47. Какие фреймворки и библиотеки используются в экосистеме Spring?

  48. Что такое Spring Boot Starter? Как использовать Starter в своем приложении?

  49. Как настроить и использовать Spring Cloud Config Server для управления конфигурацией в приложении?

  50. Какие принципы SOLID вы знаете и как они связаны с Spring Framework?

  51. Как использовать Spring Test в тестировании приложений?

  52. Какие подходы к реализации микросервисных архитектур вы знаете? Как их можно реализовать в Spring Framework?

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

  54. Как организовать логирование в приложении, используя Spring Framework?

  55. Как использовать Spring Security для аутентификации и авторизации пользователей в приложении?

  56. Какие типы кэширования доступны в Spring Framework? Как их использовать?

  57. Какие инструменты для профилирования и отладки приложений вы знаете? Как их использовать в разработке приложений на Spring Framework?

  58. Что такое Spring Data REST? Как использовать Spring Data REST для создания RESTful API?

  59. Как настроить и использовать Spring Cloud Netflix Eureka для реализации сервисной реестра в микросервисной архитектуре?

  60. Какие виды транзакций поддерживает Spring Framework? Какие аннотации используются для управления транзакциями?

  61. Что такое Spring WebFlux? Какие преимущества он предоставляет по сравнению с Spring MVC?

  62. Как использовать Spring Cloud Gateway для реализации шлюза API?

  63. Как работать с WebSocket в Spring Boot?

  64. Как использовать Spring Cloud Sleuth для трассировки запросов в распределенных системах?

  65. Какие инструменты доступны в экосистеме Spring для обмена сообщениями между сервисами?

  66. Какие инструменты доступны в Spring Framework для работы с базами данных NoSQL?

  67. Как использовать Spring Security для защиты приложения от атак на основе CSRF и XSS?

  68. Как использовать Spring Batch для выполнения задач, выполняющихся в фоновом режиме?

  69. Как работать с GraphQL в Spring Framework?

  70. В чем преимущества Spring?

  71. Что такое файл конфигурации для Spring?

  72. Каковы различные модули Spring Framework?

  73. Каковы различные компоненты приложения Spring?

  74. Что такое внедрение зависимостей?

  75. Что такое Spring IoK контейнер?

  76. Каковы типы IoK?

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

  78. Что такое Spring Bean?

  79. Каковы общие реализации ApplicationContext?

  80. В чем разница между BeanFactory и ApplicationContext?

  81. Как вы добавляете компонент в Spring?

  82. Какие области Beans поддерживает Spring?

  83. Каковы этапы жизненного цикла компонента?

  84. Объясните внутреннюю часть bean в Spring

  85. Что такое автоматическая проводка bean?

  86. Как вы можете внедрить коллекции Java в Spring?

  87. Что такое Joinpoint?

  88. Что такое Advice в Spring?

  89. Каковы типы рекомендаций для структуры Spring?

  90. Что такое Weaving?

  91. Опишите поддержку Spring DAO

  92. Что такое JDBC? Какие классы присутствуют в API Spring JDBC?

  93. Что такое платформа Spring Model-View-Controller (MVC)?

  94. Каковы части фреймворка Spring MVC?

  95. Каковы различные части DispatcherServlet?

  96. Как можно включить проводку аннотаций в Spring?

  97. Что такое Spring Boot?

  98. Что такое Реактивное программирование?

  99. Что такое Spring webflux?

  100. В чем разница между веб-клиентом и клиентом веб-тестирования?

  101. Может ли Spring Boot разрешить Spring MVC или Spring Web Flux в одном и том же приложении?

  102. Может ли Spring 5 интегрироваться с модульностью Jdk9?

  103. Расскажите о Spring Framework.

  104. Какие некоторые из важных особенностей и преимуществ Spring Framework?

  105. Что вы понимаете под Dependency Injection (DI)?

  106. Как реализуется DI в Spring Framework?

  107. Какие преимущества использования Spring Tool Suite?

  108. Приведите названия некоторых важных Spring модулей.

  109. Что вы понимаете под аспектно-ориентированным программированием (Aspect Oriented Programming — AOP)?

  110. Что такое Aspect, Advice, Pointcut, JoinPoint и Advice Arguments в АОП?

  111. В чем разница между Spring AOP и AspectJ АОП?

  112. Что такое IoC контейнер Spring?

  113. Что такое Spring бин?

  114. Какое значение имеет конфигурационный файл Spring Bean?

  115. Какие различные способы настроить класс как Spring Bean?

  116. Какие вы знаете различные scope у Spring Bean?

  117. Что такое жизненный цикл Spring Bean?

  118. Как получить объекты ServletContext и ServletConfig внутри Spring Bean?

  119. Что такое связывание в Spring и расскажите об аннотации @Autowired?

  120. Какие различные типы автоматического связывания в Spring?

  121. Является ли Spring бин потокобезопасным?

  122. Что такое контроллер в Spring MVC?

  123. Какая разница между аннотациями @Component, @Repository и @Service в Spring?

  124. Расскажите, что вы знаете о DispatcherServlet и ContextLoaderListener.

  125. Что такое ViewResolver в Spring?

  126. Что такое MultipartResolver и когда его использовать?

  127. Как обрабатывать исключения в Spring MVC Framework?

  128. Как создать ApplicationContext в программе Java?

  129. Можем ли мы иметь несколько файлов конфигурации Spring?

  130. Какие минимальные настройки, чтобы создать приложение Spring MVC?

  131. Как бы вы связали Spring MVC Framework и архитектуру MVC?

  132. Как добиться локализации в приложениях Spring MVC?

  133. Как мы можем использовать Spring для создания веб-службы RESTful, возвращающей JSON?

  134. Приведите пример часто используемых аннотаций Spring.

  135. Можем ли мы послать объект как ответ метода обработчика контроллера?

  136. Как загрузить файл в Spring MVC?

  137. Как проверить (валидировать) данные формы в Spring Web MVC Framework?

  138. Что вы знаете Spring MVC Interceptor и как он используется?

  139. Spring JdbcTemplate класс и его применение.

  140. Как использовать Tomcat JNDI DataSource в веб-приложении Spring?

  141. Каким образом можно управлять транзакциями в Spring?

  142. Расскажите о Spring DAO.

  143. Как интегрировать Spring и Hibernate?

  144. Расскажите о Spring Security.

  145. Как внедрить java.util.Properties в Spring Bean?

  146. Назовите некоторые из шаблонов проектирования, используемых в Spring Framework?

  147. Best Practices в Spring Framework.

Вопросы для собеседования

Про транзакции подробно: http://akorsa.ru/2016/08/kak-na-samom-dele-rabotaet-transactional-spring/

SPRING

Что такое Spring

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

Основные фраймворки внутри Springa:

  • Основной контейнер (Beans, Core, Context, SpEL) - предоставляет основной функционал Spring, управляющий процессом создания и настройки компонентов приложения. Beans отвечает за BeanFactory которая является сложной реализацией паттерна Фабрика (GoF). Модуль Core обеспечивает ключевые части фреймворка, включая свойства IoC и DI. Context построен на основе Beans и Core и позволяет получить доступ к любому объекту, который определён в настройках. Ключевым элементом модуля Context является интерфейс ApplicationContext. Модуль SpEL обеспечивает мощный язык выражений для манипулирования объектами во время исполнения. В нем есть тернатрные, арефметические, логические операторы. Может получить доступ к элементам коллекций.
  • Spring AOP (AOP, Aspects) - отвечает за интеграцию аспектно-ориентированного программирования во фреймворк. Spring AOP обеспечивает сервис управления транзакциями для Spring-приложения.
  • Spring Data — дополнительный удобный механизм для взаимодействия с сущностями базы данных, организации их в репозитории, извлечение данных, изменение, в каких то случаях для этого будет достаточно объявить интерфейс и метод в нем, без имплементации. Например с использованием JPA. Состоит из JDBC, ORM, OXM, JMS и модуля Transatcions. JDBC обеспечивает абстрактный слой JDBC и избавляет разработчика от необходимости вручную прописывать монотонный код, связанный с соединением с БД. ORM обеспечивает интеграцию с такими популярными ORM, как Hibernate, JDO, JPA и т.д. Модуль OXM отвечает за связь Объект/XML – XMLBeans, JAXB и т.д. Модуль JMS (Java Messaging Service) отвечает за создание, передачу и получение сообщений. Transactions поддерживает управление транзакциями для классов, которые реализуют определённые методы.
  • Spring Web module (Web, Servlet, Portlet, Struts) - Модуль Web обеспечивает такие функции, как загрузка файлов и т.д. Web-MVC содержит реализацию Spring MVC для веб-приложений. Web-Socket обеспечивает поддержку связи между клиентом и сервером, используя Web-Socket-ы в веб-приложениях. Web-Portlet обеспечивает реализацию MVC с среде портлетов.
  • Spring MVC framework - реализация паттерна MVC для построения Web-приложений.
  • Spring Integration - обеспечивает легкий обмен сообщениями в приложениях на базе Spring и поддерживает интеграцию с внешними системами через декларативные адаптеры. Эти адаптеры обеспечивают более высокий уровень абстракции по сравнению с поддержкой Spring для удаленного взаимодействия, обмена сообщениями и планирования. Основная цель Spring Integration - предоставить простую модель для построения корпоративных решений по интеграции, сохраняя при этом разделение задач, что важно для создания поддерживаемого, тестируемого кода.
  • Spring Cloud - инструменты для создания сложных топологий для потоковой и пакетной передачи данных.
  • Spring Batch - предоставляет многократно используемые функции, которые необходимы для обработки больших объемов записей, включая ведение журнала / трассировку, управление транзакциями, статистику обработки заданий, перезапуск заданий, пропуск и управление ресурсами. Он также предоставляет более продвинутые технические услуги и функции, которые позволят выполнять пакетные задания чрезвычайно большого объема и с высокой производительностью благодаря методам оптимизации и разделения. Простые и сложные пакетные задания большого объема могут использовать платформу с высокой степенью масштабируемости для обработки значительных объемов информации.
  • Spring Kafka - Проект Spring for Apache Kafka (spring-kafka) применяет основные концепции Spring для разработки решений для обмена сообщениями на основе Kafka. Он предоставляет «шаблон» в качестве высокоуровневой абстракции для отправки сообщений. Он также обеспечивает поддержку управляемых сообщениями POJO с @KafkaListener аннотациями и «контейнером слушателя». Эти библиотеки способствуют использованию инъекций зависимостей и декларативных. Во всех этих случаях вы увидите сходство с поддержкой JMS в Spring Framework и поддержкой RabbitMQ в Spring AMQP.
  • Spring Security - Фреймворк аутентификации и авторизации: конфигурируемый инструментарий процессов аутентификации и авторизации, поддерживающий много популярных и ставших индустриальными стандартами протоколов, инструментов, практик.
  • Тестирование - каркас, поддерживающий классы для написания модульных и интеграционных тестов.

к оглавлению

Особенности и преимущества Spring Framework?

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

Spring Framework, вероятно, наиболее известен как источник расширений (features), нужных для эффективной разработки сложных бизнес-приложений вне тяжеловесных программных моделей, которые исторически были доминирующими в промышленности. Ещё одно его достоинство в том, что он ввел ранее неиспользуемые функциональные возможности в сегодняшние господствующие методы разработки, даже вне платформы Java. Этот фреймворк предлагает последовательную модель и делает её применимой к большинству типов приложений, которые уже созданы на основе платформы Java. Считается, что Spring Framework реализует модель разработки, основанную на лучших стандартах индустрии, и делает её доступной во многих областях Java.

Таким образом к достоинствам Spring можно отнести:

  • Относительная легкость в изучении и применении фреймворка в разработке и поддержке приложения.
  • Внедрение зависимостей (DI) и инверсия управления (IoC) позволяют писать независимые друг от друга компоненты, что дает преимущества в командной разработке, переносимости модулей и т.д..
  • Spring IoC контейнер управляет жизненным циклом Spring Bean и настраивается наподобие JNDI lookup (поиска).
  • Проект Spring содержит в себе множество подпроектов, которые затрагивают важные части создания софта, такие как вебсервисы, веб программирование, работа с базами данных, загрузка файлов, обработка ошибок и многое другое. Всё это настраивается в едином формате и упрощает поддержку приложения.

Spring контейнеры

Container создаёт объекты, связывает их вместе, настраивает и управляет ими от создания до момента уничтожения. Spring Container получает инструкции какие объекты инстанциировать и как их конфигурировать через метаданные: XML, Аннотации или Java код .

Spring BeanFactory Container Это самый простой контейнер, который обеспечивает базовую поддержку DI и который основан на интерфейсе org.springframework.beans.factory.BeanFactory. Такие интерфейсы, как BeanFactoryAware и DisposableBean всё ещё присутствуют в Spring для обеспечения обратной совместимости.

Бины создаются при вызове метода getBean().

Наиболее часто используемая реализация интерфейса BeanFactory – XmlBeanFactory. XmlBeanFactory получает метаданные из конфигурационного XML файла и использует его для создания настроенного приложения или системы. BeanFactory обычно используется тогда, когда ресурсы ограничены (мобильные устройства). Поэтому, если ресурсы не сильно ограничены, то лучше использовать ApplicationContext.

Spring ApplicationContext Container ApplicationContext является более сложным и более продвинутым Spring Container-ом. Наследует BeanFactory и так же загружает бины, связывает их вместе и конфигурирует их определённым образом. Но кроме этого, ApplicationContext обладает дополнительной функциональностью: общий механизм работы с ресурсами, распознание текстовых сообщений из файлов настройки и отображение событий, которые происходят в приложении различными способами. Этот контейнер определяется интерфейсом org.springframework.context.ApplicationContext.

Бины создаются при "поднятии" контекста все сразу. Если не указана стратегия инициализации.

Чаще всего используются следующие реализации AppicationContext:

  • FileSystemXmlApplicationContext - Загружает данные о бине из XML файла. При использовании этой реализации в конструкторе необходимо указать полный адрес конфигурационного файла.

  • ClassPathXmlApplicationContext - Этот контейнер также получает данные о бине из XML файла. Но в отличие от FileSystemApplicationContext, в этом случае необходимо указать относительный адрес конфигурационного файла (CLASSPATH).

  • AnnotationConfigApplicationContext — метаданные конфигурируются с помощью аннотаций прямо на классах.

  • WebApplicationContext — для веб-приложений

  • GenericGroovyApplicationContext - эта конфигурация работает по сути так же, как и Xml, только с Groovy-файлами. К тому же, GroovyApplicationContext нормально работает и с Xml-файлом. Принимает на вход строку с конфигурацией контекста. Чтением контекста в данном случае занимается класс GroovyBeanDefinitionReader.

Groovy — объектно-ориентированный язык программирования разработанный для платформы Java как альтернатива языку Java с возможностями Python, Ruby и Smalltalk. Groovy использует Java-подобный синтаксис с динамической компиляцией в JVM байт-код и напрямую работает с другим Java кодом и библиотеками. Язык может использоваться в любом Java проекте или как скриптовый язык.

При этом мы можем указать несколько файлов конфигурации Spring.

Отличия ApplicationContext и BeanFactory

  1. ApplicationContext загружает все бины при запуске, а BeanFactory - по требованию.
  2. ApplicationContext расширяет BeanFactory и предоставляет функции, которые подходят для корпоративных приложений: a. поддержка внедрения зависимостей на основе аннотаций; b. удобный доступ к MessageSource (для использования в интернационализации); c. публикация ApplicationEvent - для бинов, реализующих интерфейс ApplicationListener, с помощью интерфейса ApplicationEventPublisher; d. простая интеграция с функциями Spring AOP.
  3. ApplicationContext поддерживает автоматическую регистрацию BeanPostProcessor и BeanFactoryPostProcessor. Поэтому всегда желательно использовать ApplicationContext, потому что Spring 2.0 (и выше) интенсивно использует BeanPostProcessor.
  4. ApplicationContext поддерживает практически все типы scope для бинов, а BeanFactory поддерживает только два - Singleton и Prototype.
  5. В BeanFactory не будут работать транзакции и Spring AOP. Это может привести к путанице, потому что конфигурация с виду будет корректной

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

  • Контейнер создается при запуске приложения
  • Контейнер считывает конфигурационные данные (парсинг XML, JavaConfig)
  • Из конфигурационных данных создается описание бинов (BeanDafinition) BeanDefenitionReader
  • BeanFactoryPostProcessors обрабатывают описание бина
  • Контейнер создает бины используя их описание
  • Бины инициализируются — значения свойств и зависимости внедряются в бин (настраиваются)
  • BeanPostProcessor запускают методы обратного вызова(callback methods)
  • Приложение запущено и работает
  • Инициализируется закрытие приложения
  • Контейнер закрывается
  • Вызываются callback methods

https://habr.com/ru/post/222579/

Как завершить работу контекста

Если это не веб-приложение, то есть 2 способа:

  • Регистрация shutdown-hook с помощью вызова метода registerShutdownHook(), он также реализован в классе AbstractApplicationContext. Это предпочтительный способ.
  • Можно вызвать метод close() из класса AbstractApplicationContext.

В Spring Boot приложении: Spring Boot самостоятельно зарегистрирует shutdown-hook за вас.

Bean

Бин (bean) — это не что иное, как самый обычный объект. Разница лишь в том, что бинами принято называть те объекты, которые управляются Spring-ом и живут внутри его DI-контейнера.

По умолчанию бин задается как синглтон в Spring. Таким образом все публичные переменные класса могут быть изменены одновременно из разных мест, а значит бин - не потокобезопасен. Однако поменяв область действия бина на request, prototype, session он станет потокобезопасным, но это скажется на производительности.

Конфигурационный файл спринг определяет все бины, которые будут инициализированы в Spring Context. При создании экземпляра Spring ApplicationContext будет прочитан конфигурационный xml файл и выполнены указанные в нем необходимые инициализации. Отдельно от базовой конфигурации, в файле могут содержаться описание перехватчиков (interceptors), view resolvers, настройки локализации и др.

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

В Spring Framework существуют такие свойства, определяющие бины:

  • class - Этот атрибут является обязательным и указывает конкретный класс Java-приложения, который будет использоваться для создания бина.

  • name - Уникальный идентификатор бина. В случае конфигурации с помощью xml-файла, вы можете использовать свойство “id” и/или “name” для идентификации бина. Атрибут name также может принимать массив String, что позволяет использовать несколько имен. Первый элемент массива будет являться именем и уникальным идентификатором бина, а остальные будут его псевдонимами.

  • scope - Это свойство определяет область видимости создаваемых объектов.

  • singleton - Определяет один единственный бин для каждого контейнера Spring IoC (используется по умолчанию);
  • prototype - контейнер Spring IoC создаёт новый экземпляр бина на каждый полученный запрос т.е. иметь любое количество экземпляров бина;
  • request - Создаётся один экземпляр бина на каждый HTTP запрос. Касается исключительно ApplicationContext;
  • session - Создаётся один экземпляр бина на каждую HTTP сессию. Касается исключительно ApplicationContext;
  • web soccet - Создаётся один экземпляр бина для определенного сокета.
  • application - Создаётся один экземпляр бина для жизненного цикла бина. Похоже на синглтон, но когда бобы ограничены областью приложения, значения, однажды установленное в applicationScopedBean, будет сохранено для всех последующих запросов, сеансов и даже для другого приложения сервлета, которое будет обращаться к этому Бобу, при условии, что оно выполняется в том же ServletContext. В то время как одноэлементные бобы ограничены только одним контекстом приложения.
  • constructor-arg - Определяет конструктор, использующийся для внедрения зависимости. Более подробно – далее.

  • properties - Определяет свойства внедрения зависимости. Более подробно рассмотрим далее.

  • initialization method - Здесь определяется метод инициализации бина

  • destruction method - Метод уничтожения бина, который будет использоваться при уничтожении контейнера, содержащего бин.

  • autowiring mode - Определяет режим автоматического связывания при внедрении зависимости. Более подробно рассмотрим далее.

  • lazy-initialization mode - Режим ленивой инициализации даёт контейнеру IoC команду создавать экземпляр бина при первом запросе, а не при запуске приложения.

Классы, аннотированные @Configuration, проксируются через CGLIB. Классы @Component или обычные классы не проксируются и не перехватывают вызовы методов с аннотациями @Bean, что означает, что вызовы не будут маршрутизироваться через контейнер и каждый раз будет возвращаться новый экземпляр бина.

CGLIB (Code Generation Library) - Это библиотека инструментария байтов, используемая во многих средах Java, таких как Hibernate или Spring. Инструментарий байт-кода позволяет манипулировать или создавать классы после фазы компиляции программы.

Жизненный цикл бинов:

  • Загрузка описаний бинов, создание графа зависимостей(между бинами)
  • Создание и запуск BeanFactoryPostProcessors
  • Создание бинов
  • Spring внедряет значения и зависимости в свойства бина
  • Если бин реализует метод setBeanName() из интерфейса NameBeanAware, то ID бина передается в метод
  • Если бин реализует BeanFactoryAware, то Spring устанавливает ссылку на bean factory через setBeanFactory() из этого интерфейса.
  • Если бин реализует интерфейс ApplicationContextAware, то Spring устанавливает ссылку на ApplicationContext через setApplicationContext().
  • BeanPostProcessor это специальный интерфейс, и Spring позволяет бинам имплементировать этот интерфейс. Реализуя метод postProcessBeforeInitialization(), можно изменить экземпляр бина перед его(бина) инициализацией(установка свойств и т.п.)
  • Если определены методы обратного вызова, то Spring вызывает их. Например, это метод, аннотированный @PostConstruct или метод initMethod из аннотации @Bean.
  • Теперь бин готов к использованию. Его можно получить с помощью метода ApplicationContext#getBean().
  • После того как контекст будет закрыт(метод close() из ApplicationContext), бин уничтожается.
  • Если в бине есть метод, аннотированный @PreDestroy, то перед уничтожением вызовется этот метод. Если бин имплементирует DisposibleBean, то Spring вызовет метод destroy(), чтобы очистить ресурсы или убить процессы в приложении. Если в аннотации @Bean определен метод destroyMethod, то вызовется и он.

Интерфейс BeanPostProcessor позволяют разработчику самому имплементировать некоторые методы бинов перед инициализацией и после уничтожения экземпляров бина. Имеется возможность настраивать несколько имлементаций BeanPostProcessor и определить порядок их выполнения. Данный интерфейс работает с экземплярами бинов, а это означает, что Spring IoC создаёт экземпляр бина, а затем BeanPostProcessor с ним работает. ApplicationContext автоматически обнаруживает любые бины, с реализацией BeanPostProcessor и помечает их как “post-processors” для того, чтобы создать их определённым способом.

Интерфейс BeanPostProcessor имеет всего два метода: postProcessBeforeInitialization и postProcessAfterInitialization

Жизненный цикл бинов

  1. Парсирование конфигурации и создание BeanDefinition

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

BeanDefinition содержат (среди прочего) следующие метаданные:

  • Имя класса с указанием пакета: обычно это фактический класс бина.
  • Элементы поведенческой конфигурации бина, которые определяют, как бин должен вести себя в контейнере (scope, обратные вызовы жизненного цикла и т.д.).
  • Ссылки на другие bean-компоненты, которые необходимы для его работы. Эти ссылки также называются зависимостями.
  • Другие параметры конфигурации для установки во вновь созданном объекте - например, ограничение размера пула или количество соединений, используемых в бине, который управляет пулом соединений.

Эти метаданные преобразуются в набор свойств, которые составляют каждое BeanDefinition. В следующей таблице описаны эти свойства:

При конфигурации через аннотации с указанием пакета для сканирования или JavaConfig используется класс AnnotationConfigApplicationContext. Регистрируются все классы с @Configuration для дальнейшего парсирования, затем регистрируется специальный BeanFactoryPostProcessor, а именно BeanDefinitionRegistryPostProcessor, который при помощи класса ConfigurationClassParser парсирует JavaConfig, загружает описания бинов (BeanDefinition), создаёт граф зависимостей (между бинами) и создаёт:

Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

в которой хранятся все описания бинов, обнаруженных в ходе парсинга конфигурации.

  1. Настройка созданных BeanDefinition

После первого этапа у нас имеется коллекция Map, в которой хранятся BeanDefinition-ы. BeanFactoryPostProcessor-ы на этапе создания BeanDefinition-ов могут их настроить как нам необходимо. BeanFactoryPostProcessor-ы могут даже настроить саму BeanFactory ещё до того, как она начнет работу по созданию бинов. В интерфейсе BeanFactoryPostProcessor всего один метод:

public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
  1. Создание кастомных FactoryBean (только для XML-конфигурации)

  2. Создание экземпляров бинов

Сначала BeanFactory из коллекции Map с объектами BeanDefinition достаёт те из них, из которых создаёт все BeanPostProcessor-ы, необходимые для настройки обычных бинов. Создаются экземпляры бинов через BeanFactory на основе ранее созданных BeanDefinition

  1. Настройка созданных бинов

На данном этапе бины уже созданы, мы можем лишь их донастроить.

Интерфейс BeanPostProcessor позволяет вклиниться в процесс настройки наших бинов до того, как они попадут в контейнер. ApplicationContext автоматически обнаруживает любые бины с реализацией BeanPostProcessor и помечает их как “post-processors” для того, чтобы создать их определенным способом. Например, в Spring есть реализации BeanPostProcessor-ов, которые обрабатывают аннотации @Autowired, @Inject, @Value и @Resource.

Интерфейс несет в себе два метода: postProcessBeforeInitialization(Object bean, String beanName) и postProcessAfterInitialization(Object bean, String beanName). У обоих методов параметры абсолютно одинаковые. Разница только в порядке их вызова. Первый вызывается до init-метода, второй - после.

Как правило, BeanPostProcessor-ы, которые заполняют бины через маркерные интерфейсы или тому подобное, реализовывают метод postProcessBeforeInitialization (Object bean, String beanName), тогда как BeanPostProcessor-ы, которые оборачивают бины в прокси, обычно реализуют postProcessAfterInitialization (Object bean, String beanName).

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

Есть два варианта создания этого класса:

  • либо он должен наследоваться от оригинального класса (CGLIB) и переопределять его методы, добавляя нужную логику;
  • либо он должен имплементировать те же самые интерфейсы, что и первый класс(Dynamic Proxy).

По конвенции спринга, если какой-то из BeanPostProcessor-ов меняет что-то в классе, то он должен это делать на этапе postProcessAfterInitialization(). Таким образом мы уверены, что initMethod у данного бина, работает на оригинальный метод, до того, как на него накрутился прокси.

Хронология событий:

  1. Сначала сработает метод postProcessBeforeInitialization() всех имеющихся BeanPostProcessor-ов.
  2. Затем, при наличии, будет вызван метод, аннотированный @PostConstruct.
  3. Если бин имплементирует InitializingBean, то Spring вызовет метод afterPropertiesSet() - не рекомендуется к использованию как устаревший.
  4. При наличии, будет вызван метод, указанный в параметре initMethod аннотации @Bean.
  5. В конце бины пройдут через postProcessAfterInitialization (Object bean, String beanName). Именно на данном этапе создаются прокси стандартными BeanPostProcessor-ами. Затем отработают наши кастомные BeanPostProcessor-ы и применят нашу логику к прокси-объектам. После чего все бины окажутся в контейнере, который будет обязательно обновлен методом refresh().
  6. Но даже после этого мы можем донастроить наши бины ApplicationListener-ами.
  7. Теперь всё

Image alt

  1. Бины готовы к использованию

Их можно получить с помощью метода ApplicationContext#getBean().

  1. Закрытие контекста

Когда контекст закрывается (метод close() из ApplicationContext), бин уничтожается.

Если в бине есть метод, аннотированный @PreDestroy, то перед уничтожением вызовется этот метод.

Если бин имплементирует DisposibleBean, то Spring вызовет метод destroy() - не рекомендуется к использованию как устаревший.

Если в аннотации @Bean определен метод destroyMethod, то будет вызван и он.

@PostConstruct

Spring вызывает методы, аннотированные @PostConstruct, только один раз, сразу после инициализации свойств компонента. За данную аннотацию отвечает один из BeanPostProcessor-ов.

Метод, аннотированный @PostConstruct, может иметь любой уровень доступа, может иметь любой тип возвращаемого значения (хотя тип возвращаемого значения игнорируется Spring-ом), метод не должен принимать аргументы. Он также может быть статическим, но преимуществ такого использования метода нет, т.к. доступ у него будет только к статическим полям/методам бина, и в таком случае смысл его использования для настройки бина пропадает.

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

@PreDestroy

Метод, аннотированный @PreDestroy, запускается только один раз, непосредственно перед тем, как Spring удаляет наш компонент из контекста приложения.

Как и в случае с @PostConstruct, методы, аннотированные @PreDestroy, могут иметь любой уровень доступа, но не могут быть статическими.

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

Обратите внимание, что аннотации @PostConstruct и @PreDestroy являются частью Java EE, а именно пакета javax.annotation модуля java.xml.ws.annotation. И поскольку Java EE устарела в Java 9, то с этой версии пакет считается устаревшим (Deprecated). С Java 11 данный пакет вообще удален, поэтому мы должны добавить дополнительную зависимость для использования этих аннотаций:

<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>

Как настроить класс как Spring Bean

  1. XML конфигурация
<bean name="myBean" class="project.spring.beans.MyBean"></bean>
  1. Java code - все настройки прописываются непосредственно в коде
@configuration
@ComponentScan(value="project.spring.main")
public class MyConfiguration [

    @Bean
    public MyService getService() {
        return new MyService();
    }
}

Для извлечения бина:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfiguration.class);
MyService service = ctx.getBean(MyService.class);
  1. Annotation - внутри кода используются аннотации @Component, @Service, @Repository, @Controller для указания классов как бины. Для их поиска и управления контейнером прописывается настройка в xml файле
<context:component-scan base-package="project.spring"/>

Статический Bean

Если в классе будет статический метод, то при инициализации впервую очередь создастся статический метод (из-за особенностей статических полей), а потом уже Bean, который "навешивается" на статический метод.

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

@Component
public class TestDataInit {
 @Autowired
 private static OrderItemService orderItemService; //будет null
}

@Component
public class TestDataInit {
    private static OrderItemService orderItemService;
    @Autowired
    public void setOrderItemService(OrderItemService orderItemService) {
        TestDataInit.orderItemService = orderItemService;
    }
}

к оглавлению

Inversion of Control

Центральной частью Spring является подход Inversion of Control, который позволяет конфигурировать и управлять объектами Java с помощью рефлексии. Вместо ручного внедрения зависимостей, фреймворк забирает ответственность за это посредством контейнера. Контейнер отвечает за управление жизненным циклом объекта: создание объектов, вызов методов инициализации и конфигурирование объектов путём связывания их между собой.

Объекты, создаваемые контейнером, также называются управляемыми объектами (beans). Обычно, конфигурирование контейнера, осуществляется путём внедрения аннотаций (начиная с 5 версии J2SE), но также, есть возможность, по старинке, загрузить XML-файлы, содержащие определение bean’ов и предоставляющие информацию, необходимую для создания bean’ов.

Плюсы такого подхода:

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

Объекты могут быть получены одним из двух способов:

Dependency Lookup Поиск зависимости — шаблон проектирования, в котором вызывающий объект запрашивает у объекта-контейнера экземпляр объекта с определённым именем или определённого типа.

Dependency Injection Внедрение зависимости — шаблон проектирования, в котором контейнер передает экземпляры объектов по их имени другим объектам с помощью конструктора, свойства или фабричного метода.

Dependency Injection (DI)

Под DI понимают то Dependency Inversion (инверсию зависимостей, то есть попытки не делать жестких связей между вашими модулями/классами, где один класс напрямую завязан на другой), то Dependency Injection (внедрение зависимостей, это когда объекты котиков создаете не вы в main-е и потом передаете их в свои методы, а за вас их создает спринг, а вы ему просто говорите что-то типа "хочу сюда получить котика" и он вам его передает в ваш метод). Мы чаще будем сталкиваться в дальнейших статьях со вторым.

Внедрение зависимости (Dependency injection, DI) — процесс, когда один объект реализует свой функционал через другой. Является специфичной формой «инверсии управления» (Inversion of control, IoC), когда она применяется к управлению зависимостями. В полном соответствии с принципом единой обязанности объект отдаёт заботу о построении требуемых ему зависимостей внешнему, специально предназначенному для этого общему механизму.

К достоинствам применения DI можно отнести:

  • Сокращение объема связующего кода. Одним из самых больших плюсов DI является возможность значительного сокращения объема кода, который должен быть написан для связывания вместе различных компонентов приложения. Зачастую этот код очень прост — при создании зависимости должен создаваться новый экземпляр соответствующего объекта.
  • Упрощенная конфигурация приложения. За счет применения DI процесс конфигурирования приложения значительно упрощается. Для конфигурирования классов, которые могут быть внедрены в другие классы, можно использовать аннотации или XML-файлы.
  • Возможность управления общими зависимостями в единственном репозитории. При традиционном подходе к управлению зависимостями в общих службах, к которым относятся, например, подключение к источнику данных, транзакция, удаленные службы и т.п., вы создаете экземпляры (или получаете их из определенных фабричных классов) зависимостей там, где они нужны — внутри зависимого класса. Это приводит к распространению зависимостей по множеству классов в приложении, что может затруднить их изменение. В случае использования DI вся информация об общих зависимостях содержится в единственном репозитории (в Spring есть возможность хранить эту информацию в XML-файлах или Java классах), что существенно упрощает управление зависимостями и снижает количество возможных ошибок.
  • Улучшенная возможность тестирования. Когда классы проектируются для DI, становится возможной простая замена зависимостей. Это особенно полезно при тестировании приложения.
  • Стимулирование качественных проектных решений для приложений. Вообще говоря, проектирование для DI означает проектирование с использованием интерфейсов. Используя Spring, вы получаете в свое распоряжение целый ряд средств DI и можете сосредоточиться на построении логики приложения, а не на поддерживающей DI платформе.

Как реализуется DI в Spring Framework?

Реализация DI в Spring основана на двух ключевых концепциях Java — компонентах JavaBean и интерфейсах. При использовании Spring в качестве поставщика DI вы получаете гибкость определения конфигурации зависимостей внутри своих приложений разнообразными путями (т.е. внешне в XML-файлах, с помощью конфигурационных Java классов Spring или посредством аннотаций Java в коде). Компоненты JavaBean (также называемые POJO (Plain Old Java Object — простой старый объект Java)) предоставляют стандартный механизм для создания ресурсов Java, которые являются конфигурируемыми множеством способов. За счет применения DI объем кода, который необходим при проектировании приложения на основе интерфейсов, снижается почти до нуля. Кроме того, с помощью интерфейсов можно получить максимальную отдачу от DI, потому что бины могут использовать любую реализацию интерфейса для удовлетворения их зависимости.

К типам реализации внедрения зависимостей в Spring относят:

Constructor Dependency Injection — это тип внедрения зависимостей, при котором зависимости компонента предоставляются ему в его конструкторе (или конструкторах). Рекомендуется как основной способ, т.к. даже без спринга внедрение зависимостей будет работать корректно.

public class ConstructorInjection {

private Dependency dependency;

  public ConstructorInjection(Dependency dependency) {
         this.dependency = dependency;
  }
}

Setter Dependency Injection — контейнер IoC внедряет зависимости компонента в компонент через методы установки в стиле JavaBean. В основном через сеттеры. При модификации не создает новые экземпляры, в отличии от конструктора. Он при каждой модификации создаёт новый экземпляр.

public class SetterInjection {
private Dependency dependency;
   public void setDependency(Dependency dependency) {
           this.dependency = dependency;
   }
}

Связывание и @Autowired

Процесс внедрения зависимостей в бины при инициализации называется Spring Bean Wiring. Считается хорошей практикой задавать явные связи между зависимостями, но в Spring предусмотрен дополнительный механизм связывания @Autowired. Аннотация может использоваться над конструктор, поле, сеттер-метод или метод конфигурации для связывания по типу. Если в контейнере не будет обнаружен необходимый для вставки бин, то будет выброшено исключение, либо можно указать @Autowired(required = false), означающее, что внедрение зависимости в данном месте не обязательно. Чтобы аннотация заработала, необходимо указать небольшие настройки в конфигурационном файле спринг с помощью элемента context:annotation-config/.

Типы связывания:

  • autowire byName,
  • autowire byType,
  • autowire by constructor,
  • autowiring by @Autowired and @Qualifier annotations

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

Мы также можем указать Spring предоставить все бины определенного типа из ApplicationContext, добавив аннотацию @Autowired в поле или метод с массивом или коллекцией этого типа, как показано в следующем примере:

@Autowired
private MovieCatalog[] movieCatalogs;
или:
@Autowired
private Set<MovieCatalog> movieCatalogs;
или:
@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}

Даже коллекции типа Map могут быть подключены автоматически, если тип ключа - String. Ключами будут имена бинов, а значениями - сами бины, как показано в следующем примере:

public class MovieRecommender {
private Map<String, MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs){
    this.movieCatalogs = movieCatalogs;
}
// ...
}

к оглавлению

MVC

Spring имеет собственную MVC-платформу веб-приложений, которая не была первоначально запланирована. Spring MVC является фреймворком, ориентированным на запросы. В нем определены стратегические интерфейсы для всех функций современной запросно-ориентированной системы. Цель каждого интерфейса — быть простым и ясным, чтобы пользователям было легко его заново имплементировать, если они того пожелают. MVC прокладывает путь к более чистому front-end-коду. Все интерфейсы тесно связаны с Servlet API. Эта связь рассматривается некоторыми как неспособность разработчиков Spring предложить для веб-приложений абстракцию более высокого уровня. Однако эта связь оставляет особенности Servlet API доступными для разработчиков, облегчая все же работу с ним. Наиболее важные интерфейсы, определенные Spring MVC, перечислены ниже:

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

HandlerAdapter: вызов и выполнение выбранного метода обработки входящего запроса.

Controller: включен между Моделью (Model) и Представлением (View). Управляет процессом преобразования входящих запросов в адекватные ответы. Действует как ворота, направляющие всю поступающую информацию. Переключает поток информации из модели в представление и обратно.

Класс DispatcherServlet является главным контроллером, которые получает запросы и распределяет их между другими контроллерами. @RequestsMapping указывает, какие именно запросы будут обрабатываться в конкретном контроллере. Может быть несколько экземпляров DispatcherServlet, отвечающих за разные задачи (обработка запросов пользовательского интерфейса, REST служб и т.д.). Каждый экземпляр DispatcherServlet имеет собственную конфигурацию WebApplicationContext, которая определяет характеристики уровня сервлета, такие как контроллеры, поддерживающие сервлет, отображение обработчиков, распознавание представлений, интернационализация, оформление темами, проверка достоверности, преобразование типов и форматирование и т.п.

ContextLoaderListener - слушатель при старте и завершении корневого класса Spring WebApplicationContext. Основным назначением является связывание жизненного цикла ApplicationContext и ServletContext, а так же автоматического создания ApplicationContext. Можно использовать этот класс для доступа к бинам из различных контекстов спринг. Настраивается в web.xml

Model: Этот блок инкапсулирует (объединяет) данные приложения. На практике это POJO-классы.

View: ответственно за возвращение ответа клиенту в виде текстов и изображений. Некоторые запросы могут идти прямо во View, не заходя в Model; другие проходят через все три слоя.

ViewResolver: выбор, какое именно View должно быть показано клиенту. Поддерживает распознавание представлений на основе логического имени, возвращаемого контроллером. Для поддержки различных механизмов распознавания представлений предусмотрено множество классов реализации. Например, класс UrlBasedViewResolver поддерживает прямое преобразование логических имен в URL.

Класс ContentNegotiatingViewResolver поддерживает динамическое распознавание представлений в зависимости от типа медиа, поддерживаемого клиентом (XML, PDF, JSON и т.д.). Существует также несколько реализаций для интеграции с различными технологиями представлений, такими как FreeMarker (FreeMarkerViewResolver), Velocity (VelocityViewResolver) и JasperReports (JasperReportsViewResolver).

HandlerInterceptor: перехват входящих запросов. Сопоставим, но не эквивалентен сервлет-фильтрам (использование не является обязательным и не контролируется DispatcherServlet-ом).

LocaleResolver: получение и, возможно, сохранение локальных настроек (язык, страна, часовой пояс) пользователя.

MultipartResolver: обеспечивает Upload — загрузку на сервер локальных файлов клиента. По умолчанию этот интерфейс не включается в приложении и необходимо указывать его в файле конфигурации. После настройки любой запрос о загрузке будет отправляться этому интерфейсу.

Spring MVC предоставляет разработчику следующие возможности:

  • Ясное и прозрачное разделение между слоями в MVC и запросах.
  • Стратегия интерфейсов — каждый интерфейс делает только свою часть работы.
  • Интерфейс всегда может быть заменен альтернативной реализацией.
  • Интерфейсы тесно связаны с Servlet API.
  • Высокий уровень абстракции для веб-приложений.
  • В веб-приложениях можно использовать различные части Spring, а не только Spring MVC.

Image alt

Шаблон проектирования Front Controller

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

В Spring в качестве Front Controller выступает DispatcherServlet, все действия проходят через него. Как правило в приложении задаётся только один DispatcherServlet с маппингом “/”, который перехватывает все запросы. Это и есть реализация паттерна Front Controller.

Однако иногда необходимо определить два и более DispatcherServlet-а, которые будут отвечать за свой собственный функционал. Например, чтобы один обрабатывал REST-запросы с маппингом “/api”, а другой обычные запросы с маппингом “/default”. Spring предоставляет нам такую возможность, и для начала нужно понять, что:

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

Каждый DispatcherServlet имеет свой дочерний контекст приложения. DispatcherServlet по сути является сервлетом(он расширяет HttpServlet), основной целью которого является обработка входящих веб�запросов, соответствующих настроенному шаблону URL. Он принимает входящий URI и находит правильную комбинацию контроллера и вида. Веб-приложение может определять любое количество DispatcherServlet-ов. Каждый из них будет работать в своем собственном пространстве имен, загружая свой собственный дочерний WebApplicationContext (на рисунке - Servlet WebApplicationContext) с вьюшками, контроллерами и т.д. Например, когда нам нужно в одном Servlet WebApplicationContext определить обычные контроллеры, а в другом REST-контроллеры.

Image alt

WebApplicationContext расширяет ApplicationContext (создаёт и управляет бинами и т.д.), но помимо этого он имеет дополнительный метод getServletContext(), через который у него есть возможность получать доступ к ServletContext-у.

ContextLoaderListener создает корневой контекст приложения (на рисунке - Root WebApplicationContext) и будет использоваться всеми дочерними контекстами, созданными всеми DispatcherServlet. Напомню, что корневой контекст приложения будет общим и может быть только один. Root WebApplicationContext содержит компоненты, которые видны всем дочерним контекстам, такие как сервисы, репозитории, компоненты инфраструктуры и т.д. После создания корневого контекста приложения он сохраняется в ServletContext как атрибут, имя которого:

WebApplicationContext.class.getName() + ".ROOT"

Чтобы из контроллера любого дочернего контекста обратиться к корневому контексту приложения, мы можем использовать класс WebApplicationContextUtils, содержащий статические методы:

@Autowired
ServletContext context;
ApplicationContext ac =WebApplicationContextUtils.getWebApplicationContext(context);
if(ac == null){
    return "root application context is null";
}

ContextLoaderListener vs DispatcherServlet

  1. ContextLoaderListener создает корневой контекст приложения.
  2. Каждый DispatcherServlet создаёт себе один дочерний контекст.
  3. Дочерние контексты могут обращаться к бинам, определенным в корневом контексте.
  4. Бины в корневом контексте не могут получить доступ к бинам в дочерних контекстах (напрямую).
  5. Все контексты добавляются в ServletContext.
  6. Мы можем получить доступ к корневому контексту, используя класс WebApplicationContextUtils.

Image alt

В чем разница между Filters, Listeners и Interceptors?

Filter

Это интерфейс из пакета javax.servlet, имплементации которого выполняют задачи фильтрации либо по пути запроса к ресурсу (сервлету, либо по статическому контенту), либо по пути ответа от ресурса, либо в обоих направлениях.

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

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

Когда вызывается метод doFilter(ServletRequest request, ServletResponse response, FilterChain chain) первого фильтра, веб-сервер создает объект FilterChain, представляющий цепочку фильтров, и передаёт её в метод.

Image alt

Interceptor

Это интерфейс из пакета org.aopalliance.intercept, предназначенный для аспектно�ориентированного программирования. В Spring, когда запрос отправляется в Controller, перед тем как он в него попадёт, он может пройти через перехватчики Interceptor (0 или более). Это одна из реализаций АОП в Spring. Вы можете использовать Interceptor для выполнения таких задач, как запись в Log, добавление или обновление конфигурации перед тем, как запрос обработается Controller-ом.

Стек перехватчиков: он предназначен для связывания перехватчиков в цепочку в определенном порядке. При доступе к перехваченному методу или полю перехватчик в цепочке перехватчиков вызывается в том порядке, в котором он был определен.

Image alt

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

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

  • public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) - вызывается после того, как HandlerMapping определил соответствующий контроллер, но до того, как HandlerAdapter вызовет метод контроллера. С помощью этого метода каждый перехватчик может решить, прервать цепочку выполнения или направить запрос на испольнение дальше по цепочке перехватчиков до метода контроллера. Если этот метод возвращает true, то запрос отправляется следующему перехватчику или в контроллер. Если метод возвращает false, то исполнение запроса прекращается, обычно отправляя ошибку HTTP или записывая собственный ответ в response.

  • public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) - отработает после контроллера, но перед формированием представления. Мы можем использовать этот метод для добавления дополнительных атрибутов в ModelAndView или для определения времени, затрачиваемого методом-обработчиком на обработку запроса клиента. Вы можете добавить больше объектов модели в представление, но вы не можете изменить HttpServletResponse, так как он уже зафиксирован.

  • public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) - отработает после формирования представления. Вызывается только в том случае, если метод preHandle этого перехватчика успешно завершен и вернул true!

Image alt

Следует знать, что HandlerInterceptor связан с бином DefaultAnnotationHandlerMapping, который отвечает за применение перехватчиков к любому классу, помеченному аннотацией @Controller.

Чтобы добавить наши перехватчики в конфигурацию Spring, нам нужно переопределить метод addInterceptors () внутри класса, который реализует WebMvcConfigurer:

@Override
public void addInterceptors(InterceptorRegistry registry) {
// LogInterceptor applies to all URLs.
registry.addInterceptor(new LogInterceptor());
// This interceptor applies to URL /admin/oldLogin.
// Using OldURLInterceptor to redirect to new URL.
registry.addInterceptor(new OldLoginInterceptor())
.addPathPatterns("/admin/oldLogin");
// This interceptor applies to URLs like /admin/*
// Exclude /admin/oldLogin
registry.addInterceptor(new AdminInterceptor())
.addPathPatterns("/admin/*")//
.excludePathPatterns("/admin/oldLogin");
}

Filter vs. Interceptor

  • Перехватчик основан на механизме Reflection, а фильтр основан на обратном вызове функции.
  • Фильтр зависит от контейнера сервлета, тогда как перехватчик не зависит от него.
  • Перехватчики могут работать только с запросами к контроллерам, в то время как фильтры могут работать почти со всеми запросами (например, js, .css и т.д.).
  • Перехватчики в отличии от фильтров могут обращаться к объектам в контейнере Spring, что даёт им более изощренный функционал.

Порядок работы:

  1. Фильтры до;
  2. Перехватчики до;
  3. Метод контроллера;
  4. Перехватчики после;
  5. Фильтры после.

HandlerInterceptor в основном похож на Servlet Filter, но в отличие от последнего он просто позволяет настраивать предварительную обработку с возможностью запретить выполнение самого обработчика и настраивать постобработку.

Согласно документации Spring, фильтры более мощные, например, они позволяют обмениваться объектами запроса и ответа, которые передаются по цепочке. Это означает, что фильтры работают больше в области запроса/ответа, в то время как HandlerInterceptors являются бинами и могут обращаться к другим компонентам в приложении. Обратите внимание, что фильтр настраивается в web.xml, а HandlerInterceptor в контексте приложения.

Java Listener

Listener (Слушатель) - это класс, который реализует интерфейс javax.servlet.ServletContextListener. Он инициализируется только один раз при запуске веб�приложения и уничтожается при остановке веб-приложения. Слушатель сидит и ждет, когда произойдет указанное событие, затем «перехватывает» событие и запускает собственное событие. Например, мы хотим инициализировать пул соединений с базой данных до запуска веб-приложения. ServletContextListener - это то, что нам нужно, он будет запускать наш код до запуска веб-приложения.

Все ServletContextListeners уведомляются об инициализации контекста до инициализации любых фильтров или сервлетов в веб-приложении.

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

Чтобы создать свой Listener нам достаточно создать класс, имплементирующий интерфейс ServletContextListener и поставить над ним аннотацию @WebListener:

@WebListener
public class MyAppServletContextListener
implements ServletContextListener{
//Run this before web application is started
@Override
public void contextInitialized(ServletContextEvent arg0) {
    System.out.println("ServletContextListener started");
}
@Override
public void contextDestroyed(ServletContextEvent arg0) {
    System.out.println("ServletContextListener destroyed");
}
}

Связывание форм

@ModelAttribute - связывает параметр метода или возвращаемое значение метода с именованным атрибутом модели, а затем возвращает его view веб-представлению.

Когда аннатоцаия используется над методом, она указывает, что целью этого метода является добавление одного или нескольких атрибутов в модель. При этом Spring-MVC всегда будет сначала вызывать этот метод, прежде чем вызывать какие-либо методы обработчика запросов. То есть, методы @ModelAttribute вызываются до того, как вызываются методы контроллера, аннотированные @RequestMapping.

@ModelAttribute
public void addAttributes(Model model) {
    model.addAttribute("msg", "Welcome to the Netherlands!");
}

Также важно, чтобы соответствующий класс был помечен как @ControllerAdvice. Таким образом, Вы можете добавить в модель значения, которые будут определены как глобальные. Это фактически означает, что для каждого запроса существует значение по умолчанию, для каждого метода в части ответа.

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

@RequestMapping(value = "/addEmployee", method = RequestMethod.POST)
public String submit(@ModelAttribute("employee") Employee employee) {
    // Code that uses the employee object
    return "employeeView";
}

Атрибут модели сотрудника заполняется данными из формы, отправленной в конечную точку addEmployee. Spring MVC делает это за кулисами перед вызовом метода submit. Таким образом, он связывает данные формы с Bean. Контроллер с аннотацией @RequestMapping может иметь пользовательские аргументы класса с аннотацией @ModelAttribute. Это то, что обычно называют привязкой данных в Spring-MVC, общий механизм, который избавляет вас от необходимости анализировать каждое поле формы по отдельности.

Исключения в Spring MVC

В Spring MVC интерфейс HandlerExceptionResolver (из пакета org.springframework.web.servlet) предназначен для работы с непредвиденными исключениями, возникающими во время выполнения обработчиков. По умолчанию DispatcherServlet регистрирует класс DefaultHandlerExceptionResolver (из пакета org.springframework.web.servlet.mvc.support). Этот распознаватель обрабатывает определенные стандартные исключения Spring MVC, устанавливая специальный код состояния ответа. Можно также реализовать собственный обработчик исключений, аннотировав метод контроллера с помощью аннотации @ExceptionHandler и передав ей в качестве атрибута тип исключения.

В общем случае обработку исключений можно описать таким образом:

  • @ExceptionHandler - указать методы для обработки исключения в классе контроллере. Принимает в себя имя класса обрабатываемого исключения (можно несколько).
  • @ControllerAdvice - для глобальной обработки ошибок в приложении Spring MVC. Ставится над классом-контроллером, отлавливает все исключения с методов. Он также имеет полный контроль над телом ответа и кодом состояния.
  • HandlerExceptionResolver implementation – позволяет задать глобального обработчика исключений. Реализацию этого интерфейса можно использовать для создания собственных глобальных обработчиков исключений в приложении.

Локализация в приложениях Spring MVC

Spring MVC предоставляет очень простую и удобную возможность локализации приложения. Для этого необходимо сделать следующее:

  • Создать файл resource bundle, в котором будут заданы различные варианты локализированной информации.
  • Определить messageSource в конфигурации Spring используя классы ResourceBundleMessageSource или ResourceBundleMessageSource.
  • Определить localceResolver класса CookieLocaleResolver для включения возможности переключения локали.
  • С помощью элемента spring:message DispatcherServlet будет определять в каком месте необходимо подставлять локализированное сообщение в ответе.

Spring Interceptor

Перехватчики в Spring (Spring Interceptor) являются аналогом Servlet Filter и позволяют перехватывать запросы клиента и обрабатывать их. Перехватить запрос клиента можно в трех местах: preHandle, postHandle и afterCompletion.

  • preHandle — метод используется для обработки запросов, которые еще не были переданы в метода обработчик контроллера. Должен вернуть true для передачи следующему перехватчику или в handler method. False укажет на обработку запроса самим обработчиком и отсутствию необходимости передавать его дальше. Метод имеет возможность выкидывать исключения и пересылать ошибки к представлению.
  • postHandle — вызывается после handler method, но до обработки DispatcherServlet для передачи представлению. Может использоваться для добавления параметров в объект ModelAndView.
  • afterCompletion — вызывается после отрисовки представления.

Для создания обработчика необходимо расширить абстрактный класс HandlerInterceptorAdapter или реализовать интерфейс HandlerInterceptor. Так же нужно указать перехватчики в конфигурационном файле Spring.

к оглавлению

CommandLineRunner и ApplicationRunner

Эти интрефейсы используются для запуска логики при запуске приложения, после создания экземпляра контекста приложения Spring.

ApplicationRunner.run() и CommandLineRunner.run() выполнятся сразу после создания applicationcontext и до запуска приложения. Оба они обеспечивают одинаковую функциональность, и единственное различие между CommandLineRunner и ApplicationRunner состоит в том, что CommandLineRunner.run() принимает String array[], тогда как ApplicationRunner.run() принимает ApplicationArguments в качестве аргумента.

@Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
    private static final Logger LOG =
      LoggerFactory.getLogger(CommandLineAppStartupRunner.class);

    public static int counter;

    @Override
    public void run(String...args) throws Exception {
        LOG.info("Increment counter");
        counter++;
    }
}

Можно запускать несколько CommandLineRunner одновременно, например чтобы распаралелить сложную логику. Управлять их порядком через @Order. Каждый Runner может иметь свои собственные зависимости

Реактивное программирование

Реактивное программирование — это программирование в многопоточной среде.

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

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

Мы получаем эти cгенерированные события асинхронно, всегда. Согласно идеологии реактивного программирования существуют три вида функций: те, которые должны выполняться, когда некоторые конкретные данные будут отправлены, функции обработки ошибок и другие функции с сигналами о завершении работы программы. Иногда последнее два пункта можно опустить и сосредоточится на определении функций для обработки значений. Слушать(listening) поток означает подписаться(subscribing) на него. То есть функции, которые мы определили это наблюдатели(observers). А поток является субъектом который наблюдают.

Критерии реактивного приложения: Responsive. Разрабатываемая система должна отвечать быстро и за определенное заранее заданное время. Кроме того система должна быть достаточно гибкой для самодиагностики и починки.

Что это значит на практикте? Традиционно при запросе некоторого сервиса мы идем в базу данных, вынимаем необходимый объем информации и отдаем ее пользователю. Здесь все хорошо, если наша система достаточно быстрая и база данных не очень большая. Но что, если время формирования ответа гораздно больше ожидаемого? Кроме того, у пользователя мог пропасть интернет на несколько миллисекунд. Тогда все усилия по выборке данных и формированию ответа пропадают. Вспомните gmail или facebook. Когда у вас плохой интернет, вы не получаете ошибку, а просто ждете результат больше обычного. Кроме того, этот пункт говорит нам о том, что ответы и запросы должны быть упорядочены и последовательны.

Resilient. Система остается в рабочем состоянии даже, если один из компонентов отказал.

Другими словами, компоненты нашей системы должны быть досточно гибкими и изолированными друг от друга. Достигается это путем репликаций. Если, например, одна реплика PostgreSQL отказала, необходимо сделать так, чтобы всегда была доступна другая. Кроме того, наше приложение должно работать во множестве экземпляров.

Elastic. Система должна занимать оптимальное количество ресурсов в каждый промежуток времени. Если у нас высокая нагрузка, то необходимо увеличить количество экзепляров приложения. В случае малой нагрузки ресурсы свободных машин должны быть очищены. Типичный инструменты реализации данного принципа: Kubernetes.

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

к оглавлению

Паттерны в Spring Framework

Вот некоторые известные паттерны, используемые в Spring Framework:

  • Chain of Responsibility - это поведенческий паттерн проектирования, который позволяет передавать запросы последовательно по цепочке обработчиков. Каждый последующий обработчик решает, может ли он обработать запрос сам и стоит ли передавать запрос дальше по цепи. Ему Spring Security
  • Singleton (Одиночка) - Паттерн Singleton гарантирует, что в памяти будет существовать только один экземпляр объекта, который будет предоставлять сервисы. Spring область видимости бина (scope) по умолчанию равна singleton и IoC-контейнер создаёт ровно один экземпляр объекта на Spring IoC-контейнер. Spring-контейнер будет хранить этот единственный экземпляр в кэше синглтон-бинов, и все последующие запросы и ссылки для этого бина получат кэшированный объект. Рекомендуется использовать область видимости singleton для бинов без состояния. Область видимости бина можно определить как singleton или как prototype (создаётся новый экземпляр при каждом запросе бина).
  • Model View Controller (Модель-Представление-Контроллер) - Преимущество Spring MVC в том, что ваши контроллеры являются POJO, а не сервлетами. Это облегчает тестирование контроллеров. Стоит отметить, что от контроллеров требуется только вернуть логическое имя представления, а выбор представления остаётся за ViewResolver. Это облегчает повторное использование контроллеров при различных вариантах представления.
  • Front Controller (Контроллер запросов) - Spring предоставляет DispatcherServlet, чтобы гарантировать, что входящий запрос будет отправлен вашим контроллерам.Паттерн Front Controller используется для обеспечения централизованного механизма обработки запросов, так что все запросы обрабатываются одним обработчиком. Этот обработчик может выполнить аутентификацию, авторизацию, регистрацию или отслеживание запроса, а затем передать запрос соответствующему контроллеру. View Helper отделяет статическое содержимое в представлении, такое как JSP, от обработки бизнес-логики.
  • Dependency injection и Inversion of control (IoC) (Внедрение зависимостей и инверсия управления) - IoC-контейнер в Spring, отвечает за создание объекта, связывание объектов вместе, конфигурирование объектов и обработку всего их жизненного цикла от создания до полного уничтожения. В контейнере Spring используется инъекция зависимостей (Dependency Injection, DI) для управления компонентами приложения. Эти компоненты называются "Spring-бины" (Spring Beans).
  • Service Locator (Локатор служб) - ServiceLocatorFactoryBean сохраняет информацию обо всех бинах в контексте. Когда клиентский код запрашивает сервис (бин) по имени, он просто находит этот компонент в контексте и возвращает его. Клиентскому коду не нужно писать код, связанный со Spring, чтобы найти бин. Паттерн Service Locator используется, когда мы хотим найти различные сервисы, используя JNDI. Учитывая высокую стоимость поиска сервисов в JNDI, Service Locator использует кеширование. При запросе сервиса первый раз Service Locator ищет его в JNDI и кэширует объект. Дальнейший поиск этого же сервиса через Service Locator выполняется в кэше, что значительно улучшает производительность приложения.
  • Observer-Observable (Наблюдатель) - Используется в механизме событий ApplicationContext. Определяет зависимость "один-ко-многим" между объектами, чтобы при изменении состояния одного объекта все его подписчики уведомлялись и обновлялись автоматически.
  • Context Object (Контекстный объект) - Паттерн Context Object, инкапсулирует системные данные в объекте-контексте для совместного использования другими частями приложения без привязки приложения к конкретному протоколу. ApplicationContext является центральным интерфейсом в приложении Spring для предоставления информации о конфигурации приложения.
  • Proxy (Заместитель) - позволяет подставлять вместо реальных объектов специальные объекты-заменители. Эти объекты перехватывают вызовы к оригинальному объекту, позволяя сделать что-то до или после передачи вызова оригиналу.
  • Factory (Фабрика) - определяет общий интерфейс для создания объектов в суперклассе, позволяя подклассам изменять тип создаваемых объектов.
  • Template (Шаблон) - Этот паттерн широко используется для работы с повторяющимся бойлерплейт кодом (таким как, закрытие соединений и т. п.).

к оглавлению

AOP и составные части

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

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

Аспект (Aspect) - Это модуль, который имеет набор программных интерфейсов, которые обеспечивают сквозные требования. К примеру, модуль логирования будет вызывать АОП аспект для логирования. В зависимости от требований, приложение может иметь любое количество аспектов.

Объединённая точка (Join point) - Это такая точка в приложении, где мы можем подключить аспект. Другими словами, это место, где начинаются определённые действия модуля АОП в Spring.

Совет (Advice) - Это фактическое действие, которое должно быть предпринято до и/или после выполнения метода. Это конкретный код, который вызывается во время выполнения программы.

  • before - Запускает совет перед выполнением метода.
  • after - Запускает совет после выполнения метода, независимо от результата его работы (кроме случая остановки работы JVM).
  • after-returning - Запускает совет после выполнения метода, только в случае его успешного выполнения.
  • after-throwing - Запускает совет после выполнения метода, только в случае, когда этот метод “бросает” исключение.
  • around - Запускает совет до и после выполнения метода. При этом инпоинты видят только начало и конец метода. Например, если метод выполняет транзакцию и где-то в середине кода try/catch поймал exception, транзакция все равно будет свершена, rollback не произойдет. В этом случае нужно пробрасывать ошибку за пределы метода.

Срез точек (Pointcut) - Срезом называется несколько объединённых точек (join points), в котором должен быть выполнен совет.

Введение (Introduction) - Это сущность, которая помогает нам добавлять новые атрибуты и/или методы в уже существующие классы.

Целевой объект (Target object) - Это объект на который направлены один или несколько аспектов.

Плетение (Weaving) - Это процесс связывания аспектов с другими объектами приложения для создания совета. Может быть вызван во время компиляции, загрузки или выполнения приложения.

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

Spring AOP vs ASPECTJ

AspectJ де-факто является стандартом реализации АОП. Реализация АОП от Spring имеет некоторые отличия:

  • Spring AOP немного проще, т.к. нет необходимости следить за процессом связывания.
  • Spring AOP поддерживает аннотации AspectJ, таким образом мы можем работать в спринг проекте похожим образом с AspectJ проектом.Spring + AOP поддерживает только proxy-based АОП и может использовать только один тип точек соединения - Method Invocation. AspectJ поддерживает все виды точек соединения.
  • Недостатком Spring AOP является работа только со своими бинами, которые существуют в Spring Context.

к оглавлению

Некоторые частые аннотации Spring

  • @Autowired - используется для автоматического связывания зависимостей в spring beans.
  • @Bean - В классах конфигурации Spring, @Bean используется для для непосредственного создания бина.
  • @Controller - класс фронт контроллера в проекте Spring MVC.
  • @ConditionalOn* - Создает бин если выполняется условие. Condition - функциональный интерфейс, который содержит метод boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)
  • @Sheduler - Таймер. Раз в сколько-то секунд обрабатывать.
  • @Resource - Java аннотация, которой можно внедрить зависимость.
  • @Requared - применяется к методам-сеттерам и означает, что значение метода должно быть установлено в XML-файле. Если этого не будет сделано, то мы получим BeanInitializationException.
  • @RequestMapping - используется для мапинга (связывания) с URL для всего класса или для конкретного метода обработчика.
  • @ResponseBody - позволяет отправлять Object в ответе. Обычно используется для отправки данных формата XML или JSON.
  • @ResponseEntity - используется для формирования ответа HTTP с пользовательскими параметрами (заголовки, http-код и т.д.). ResponseEntity необходим, только если мы хотим кастомизировать ответ, добавив к нему статус ответа. Во всех остальных случаях будем использовать @ResponseBody.
  • @PathVariable - задает динамический маппинг значений из URI внутри аргументов метода обработчика, т.е. позволяет вводить в URI переменную пути в качестве параметра
  • @Qualifier - используется совместно с @Autowired для уточнения данных связывания, когда возможны коллизии (например одинаковых имен\типов).
  • @Service - указывает что класс осуществляет сервисные функции.
  • @Scope - указывает scope у spring bean.
  • @Configuration, @ComponentScan и @Bean - для java based configurations.
  • AspectJ аннотации для настройки aspects и advices, @Aspect, @Before, @After,@Around, @Pointcut и др.
  • @PageableDefault - устанавливает значение по умолчанию для параметра разбиения на страницы

Различия @Component, @Service, @Repository, @Controller

Они все служат для обозначения класса как Бин.

  • @Component - Spring определяет этот класс как кандидата для создания bean.
  • @Service - класс содержит бизнес-логику и вызывает методы на уровне хранилища. Ничем не отличается от классов с @Component.
  • @Repository - указывает, что класс выполняет роль хранилища (объект доступа к DAO). При этом отлавливает определенные исключения персистентности и пробрасывает их как одно непроверенное исключение Spring Framework. Для этого Spring оборачивает эти классы в прокси, и в контекст должен быть добавлен класс PersistenceExceptionTranslationPostProcessor
  • @Controller - указывает, что класс выполняет роль контроллера MVC. Диспетчер сервлетов просматривает такие классы для поиска @RequestMapping.

Различия @Controller и @RestController

@Controller помечает класс как контроллер HTTP запросов.

В Spring 4.0 была представлена аннотация @RestController. Применив ее к контроллеру автоматически добавляются аннотации @Controller, а так же @ResponseBody применяется ко всем методам.

@Qualifier and @Primary

Если есть два одинаковых бина (по типу и имени) спринг не знает какой именно использовать и выдаёт exeption. Если над одним из этих бинов установленна @Primary, то его использовать предпочтительнее. Но если нам нужно использовать в работе оба этих бина, можно над каждым поставить @Qualifier и задать имя, для идентификации этих бинов.

@Profile

Используя аннотацию @Profile - мы сопоставляем bean-компонент с этим конкретным профилем; аннотация просто берет имена одного (или нескольких) профилей. Отвечает за то - какие бины буду создаваться, в зависимости от профайла. Фактически реализована с помощью гораздо более гибкой аннотации @Conditional.

Рассмотрим базовый сценарий - у нас есть компонент, который должен быть активным только во время разработки, но не должен использоваться в производстве. Мы аннотируем этот компонент с профилем «dev», и он будет присутствовать в контейнере только во время разработки - в производственном процессе dev просто не будет активен.

Или можно задать @Profile("postgres") и @Profile("mysql"), а в application.properties указать, бин с каким профилем использовать = spring.profiles.active = mysql

По умолчанию, если профиль бина не определен, то он относится к профилю “default”. Spring также предоставляет способ установить профиль по умолчанию, когда другой профиль не активен, используя свойство «spring.profiles.default». к оглавлению

@LookUp

Используется для внедрения prototype bean в singleton bean.

ПРИМЕР - Обычно бины в приложении Spring являтся синглтонами, и для внедрения зависимостей мы используем конструктор или сеттер. Но бывает и другая ситуация: имеется бин Car – синглтон (singleton bean), и ему требуется каждый раз новый экземпляр бина Passenger. То есть Car – синглтон, а Passenger – так называемый прототипный бин (prototype bean). Жизненные циклы бинов разные. Бин Car создается контейнером только раз, а бин Passenger создается каждый раз новый – допустим, это происходит каждый раз при вызове какого-то метода бина Car.Вот здесь то и пригодится внедрение бина с помощью Lookup метода. Оно происходит не при инициализации контейнера, а позднее: каждый раз, когда вызывается метод.

@Component
public class Car {
 @Lookup
 public Passenger createPassenger() {
    return null;
 }
 public String drive(String name) {
    Passenger passenger = createPassenger();
    passenger.setName(name);
    return "car with " + passenger.getName();
 }
}

Суть в том, что вы создаете метод-заглушку в бине Car и помечаете его специальным образом – аннотацией @Lookup. Этот метод должен возвращать бин Passenger, каждый раз новый. Контейнер Spring под капотом создаст подкласс и переопределит этот метод и будет вам выдавать новый экземпляр бина Passenger при каждом вызове аннотированного метода. Даже если в вашей заглушке он возвращает null (а так и надо делать, все равно этот метод будет переопределен).

@Component
@Scope("prototype")
public class Passenger {
 private String name;
 public String getName() {
    return name;
 }
 public void setName(String name) {
    this.name = name;
 }
}

Теперь при вызове метода drive() мы можем везти каждый раз нового пассажира. Имя его передаётся в аргументе метода drive(), и затем задается сеттером во вновь созданном экземпляре пассажира.

к оглавлению

@Target и @Retention

@Retention - указаываем, в какой момент жизни программного кода будет доступна аннотация

  • SOURCE - аннотация доступна только в исходном коде и сбрасывается во время создания .class файла;
  • CLASS - аннотация хранится в .class файле, но недоступна во время выполнения программы;
  • RUNTIME - аннотация хранится в .class файле и доступна во время выполнения программы.

@Target - указывается, какой элемент программы будет использоваться аннотацией

  • PACKAGE - назначением является целый пакет (package);
  • TYPE - класс, интерфейс, enum или другая аннотация:
  • METHOD - метод класса, но не конструктор (для конструкторов есть отдельный тип CONSTRUCTOR);
  • PARAMETER - параметр метода;
  • CONSTRUCTOR - конструктор;
  • FIELD - поля-свойства класса;
  • LOCAL_VARIABLE - локальная переменная (обратите внимание, что аннотация не может быть прочитана во время выполнения программы, то есть, данный тип аннотации может использоваться только на уровне компиляции как, например, аннотация @SuppressWarnings);
  • ANNOTATION_TYPE - другая аннотация.

к оглавлению

@Resource

Java-аннотация @Resource может применяться к классам, полям и методам. Она пытается получить зависимость: сначала по имени, затем по типу, затем по описанию (Qualifier). Имя извлекается из имени аннотируемого сеттера или поля, либо берется из параметра name. При аннотировании классов имя не извлекается из имени класса по умолчанию, поэтому оно должно быть указано явно.

Указав данную аннотацию у полей или методов с аргументом name, в контейнере будет произведен поиск компонентов с данным именем, и в контейнере должен быть бин с таким именем:

@Resource(name="namedFile")
private File defaultFile;

Если указать её без аргументов, то Spring Framework поможет найти бин по типу. Если в контейнере несколько бинов-кандидатов на внедрение, то нужно использовать аннотацию @Qualifier:

@Resource
@Qualifier("defaultFile")
private File dependency1;
@Resource
@Qualifier("namedFile")
private File dependency2;

Разница с @Autowired:

  • ищет бин сначала по имени, а потом по типу;
  • не нужна дополнительная аннотация для указания имени конкретного бина;
  • @Autowired позволяет отметить место вставки бина как необязательное @Autowired(required = false);
  • при замене Spring Framework на другой фреймворк, менять аннотацию @Resource не нужно

к оглавлению

@Inject

Размещается над полями, методами, и конструкторами с аргументами. @Inject как и @Autowired в первую очередь пытается подключить зависимость по типу, затем по описанию и только потом по имени. Это означает, что даже если имя переменной ссылки на класс отличается от имени компонента, но они одинакового типа, зависимость все равно будет разрешена:

@Inject
private ArbitraryDependency fieldInjectDependency;
//fieldInjectDependency - отличается от имени компонента, настроенного в контексте приложения:

@Bean
public ArbitraryDependency injectDependency() {
ArbitraryDependency injectDependency = new ArbitraryDependency();
return injectDependency;
}

Разность имён injectDependency и fieldInjectDependency не имеет значения, зависимость будет подобрана по типу ArbitraryDependency. Если в контейнере несколько бинов-кандидатов на внедрение, то нужно использовать аннотацию @Qualifier:

@Inject
@Qualifier("defaultFile")
private ArbitraryDependency defaultDependency;

@Inject
@Qualifier("namedFile")
private ArbitraryDependency namedDependency;

//При использовании конкретного имени (Id) бина используем @Named:
@Inject
@Named("yetAnotherFieldInjectDependency")
private ArbitraryDependency yetAnotherFieldInjectDependency

@Autowired vs @Resource vs @Inject

Аннотации для внедрения зависимостей.

@Resource (java) пытается получить зависимость: по имени, по типу, затем по описанию. Имя извлекается из имени аннотируемого сеттера или поля, либо берется из параметра name.

@Inject (java) или @Autowired (spring) в первую очередь пытается подключить зависимость по типу, затем по описанию и только потом по имени.

@Conditional

Часто бывает полезно включить или отключить весь класс @Configuration, @Component или отдельные методы @Bean в зависимости от каких-либо условий.

Аннотация @Conditional указывает, что компонент имеет право на регистрацию в контексте только тогда, когда все условия соответствуют. Может применяться:

  • над классами прямо или косвенно аннотированными @Component, включая классы @Configuration;
  • над методами @Bean;
  • как мета-аннотация при создании наших собственных аннотаций-условий.

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

Условия мы определяем в специально создаваемых нами классах, которые должны имплементировать функциональный интерфейс Condition с одним единственным методом, возвращающим true или false:

boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)

Создав свой класс и переопределив в нем метод matches() с нашей логикой, мы должны передать этот класс в аннотацию @Conditional в качестве параметра:

@Configuration
@Conditional(OurConditionClass.class)
class MySQLAutoconfiguration {
//...
}
//Для того, чтобы проверить несколько условий, можно передать в @Conditional несколько классов с условиями:
@Bean
@Conditional(HibernateCondition.class, OurConditionClass.class)
Properties additionalProperties() {
//...
}

Если класс @Configuration помечен как @Conditional, то на все методы @Bean, аннотации @Import и аннотации @ComponentScan, связанные с этим классом, также будут распространяться указанные условия.

Для более детальной настройки классов, аннотированных @Configuration, предлагается использовать интерфейс ConfigurationCondition.

В одном классе - одно условие. Для создания более сложных условий можно использовать классы AnyNestedCondition, AllNestedConditions и NoneNestedConditions.

В Spring Framework имеется множество готовых аннотаций (и связанных с ними склассами-условиями, имплементирующими интерфейс Condition), которые можно применять совместно над одним определением бина:

ConditionalOnBean Условие выполняется, в случае если присутствует нужный бин в BeanFactory. ConditionalOnClass Условие выполняется, если нужный класс есть в classpath. ConditionalOnCloudPlatform Условие выполняется, когда активна определенная платформа. ConditionalOnExpression Условие выполняется, когда SpEL выражение вернуло положительное значение. ConditionalOnJava Условие выполняется, когда приложение запущено с определенной версией JVM. ConditionalOnJndi Условие выполняется, только если через JNDI доступен определенный ресурс. ConditionalOnMissingBean Условие выполняется, в случае если нужный бин отсутствует в контейнере. ConditionalOnMissingClass Условие выполняется, если нужный класс отсутствует в classpath. ConditionalOnNotWebApplication Условие выполняется, если контекст приложения не является веб контекстом. ConditionalOnProperty Условие выполняется, если в файле настроек заданы нужные параметры. ConditionalOnResource Условие выполняется, если присутствует нужный ресурс в classpath. ConditionalOnSingleCandidate Условие выполняется, если bean-компонент указанного класса уже содержится в контейнере и он единственный. ConditionalOnWebApplication Условие выполняется, если контекст приложения является веб контекстом.

Как управлять транзакциями в Spring

Spring поддерживает два типа управления транзакциями:

  • Программное управление транзакциями: Вы должны управлять транзакциями с помощью программирования. Это способ достаточно гибкий, но его сложно поддерживать. Либо через использование TransactionTemplate, либо через реализацию PlatformTransactionManager напрямую. Используется, если нужно работать с небольшим количеством транзакций.
  • Декларативное управление транзакциями: Вы отделяете управление транзакциями от бизнес-логики. Вы используете только аннотации @Transactional и конфигурацией на основе XML для управления транзакциями. Наиболее предпочтительный способ.

Простая реализация PlatformTransactionManager это DataSourceTransactionManager, который на каждую транзакцию в БД будет создавать Connection.

DefaultTransactionDefinition definition = new DefaultTransactionDefinition(); //описание транзакции, можно задавать параметры
TransactionStatus status = transactionManager.getTransaction(); //статус транзакции

try {
    fooRepository.insertFoo("name");
    transactionManager.commit(status);
} catch (RuntimeException e) {
    transactionManager.rollback(status);
}

новая инфа

Для включения возможности управления транзакциями первым делом нужно разместить аннотацию @EnableTransactionManagement у класса-конфигурации @Configuration.

Аннотация @EnableTransactionManagement означает, что классы, помеченные @Transactional, должны быть обернуты аспектом транзакций. Однако, если мы используем Spring Boot и имеем зависимости spring-data-* или spring-tx, то управление транзакциями будет включено по умолчанию.

@EnableTransactionManagement отвечает за регистрацию необходимых компонентов Spring, таких как TransactionInterceptor и советы прокси (proxy advices- набор инструкций, выполняемых на точках среза - Pointcut). Регистрируемые компоненты помещают перехватчик в стек вызовов при вызове методов @Transactional.

Spring создает прокси для всех классов, помеченных @Transactional (либо если любой из методов класса помечен этой аннотацией). Прокси-объекты позволяют Spring Framework вводить транзакционную логику до и после вызываемого метода -главным образом для запуска и коммита/отката транзакции.

Если мы разместим аннотацию @Transactional над классом @Service, то все его методы станут транзакционными. Так, при вызове, например, метода save() произойдет примерно следующее:

  1. Вначале мы имеем:
    • класс TransactionInterceptor, у которого основной метод invoke(...), внутри которого вызывается метод класса-родителя TransactionAspectSupport:invokeWithinTransaction(...), в рамках которого происходит магия транзакций.
    • TransactionManager: решает, создавать ли новый EntityManager и/или транзакцию.
    • EntityManager proxy: EntityManager - это интерфейс, и то, что внедряется в бин в слое DAO на самом деле не является реализацией EntityManager. В это поле внедряется EntityManager proxy, который будет перехватывать обращение к полю EntityManager и делегировать выполнение конкретному EntityManager в рантайме. Обычно EntityManager proxy представлен классом SharedEntityManagerInvocationHandler.
  2. Transaction Interceptor

В TransactionInterceptor отработает код до работы метода save(), в котором будет определено, выполнить ли метод save() в пределах уже существующей транзакции БД или должна стартовать новая отдельная транзакция. TransactionInterceptor сам не содержит логики по принятию решения, решение начать новую транзакцию, если это нужно, делегируется TransactionManager. Грубо говоря, на данном этапе наш метод будет обёрнут в try-catch и будет добавлена логика до его вызова и после:

try {
   transaction.begin();
   // логика до
   service.save();
   // логика после
   transaction.commit();
   } catch(Exception ex) {
   transaction.rollback();
   throw ex;
   }
  1. TransactionManager

Менеджер транзакций должен предоставить ответ на два вопроса:

  • Должен ли создаться новый EntityManager?
  • Должна ли стартовать новая транзакция БД? TransactionManager принимает решение, основываясь на следующих фактах:
  • выполняется ли хоть одна транзакция в текущий момент или нет;
  • атрибута «propagation» у метода, аннотированного @Transactional (для примера, значение REQUIRES_NEW всегда стартует новую транзакцию).

Если TransactionManager решил создать новую транзакцию, тогда:

  • Создается новый EntityManager;
  • EntityManager «привязывается» к текущему потоку (Thread);
  • «Получается» соединение из пула соединений БД;
  • Соединение «привязывается» к текущему потоку.

И EntityManager и это соединение привязываются к текущему потоку, используя переменные ThreadLocal. 4. EntityManager proxy Когда метод save() слоя Service делает вызов метода save() слоя DAO, внутри которого вызывается, например, entityManager.persist(), то не происходит вызов метода persist()напрямую у EntityManager, записанного в поле класса DAO. Вместо этого метод вызывает EntityManager proxy, который достает текущий EntityManager для нашего потока, и у него вызывается метод persist(). 5. Отрабатывает DAO-метод save(). 6. TransactionInterceptor Отработает код после работы метода save(), а именно будет принято решение по коммиту/откату транзакции.

Кроме того, если мы в рамках одного метода сервиса обращаемся не только к методу save(), а к разным методам Service и DAO, то все они буду работать в рамках одной транзакции, которая оборачивает этот метод сервиса.

Вся работа происходит через прокси-объекты разных классов. Представим, что у нас в классе сервиса только один метод с аннотацией @Transactional, а остальные нет. Если мы вызовем метод с @Transactional, из которого вызовем метод без @Transactional, то оба будут отработаны в рамках прокси и будут обернуты в нашу транзакционную логику. Однако, если мы вызовем метод без @Transactional, из которого вызовем метод с @Transactional, то они уже не будут работать в рамках прокси и не будут обернуты в нашу транзакционную логику.


старая инфа

Аннотация сама по себе определяет область действия одной транзакции БД. Транзакция БД происходит внутри области действий persistence context.

Persistence контекстом в JPA является EntityManager, который использует внутри класс Session ORM-фреймворка Hibernate (при использовании Hibernate как persistence провайдера). Persistence контекст это объект-синхронайзер, который отслеживает состояния ограниченного набора Java объектов и синхронизирует изменения состояний этих объектов с состоянием соответствующих записей в БД.

Один объект Entity Manager не всегда соответствует одной транзакции БД. Один объект Entity Manager может быть использован несколькими транзакциями БД. Самый частый случай такого использования - когда приложение использует шаблон «Open Session in View» для предотвращения исключений «ленивой» инициализации. В этом случае запросы, которые могли быть выполнены одной транзакцией при вызове из слоя сервиса, выполняются в отдельных транзакциях в слое View, но они совершаются через один и тот же Entity Manager.

При этом @PersistenceContext не может внедрить entity manager напрямую. Entity Manager это интерфейс, и то что внедряется в бин не является самим по себе entity менеджером, это context aware proxy, который будет делегировать к конкретному entity менеджеру в рантайме.

Но прокси persistence контекста, которое имлементирует EntityManager не является достаточным набором компонентов для осуществления декларативного управления транзакциями. На самом деле нужно три компонента:

  • Прокси Entity менеджера
  • Аспект транзакций
  • Менеджер транзакций

Аспект транзакций — «around» аспект, который вызывается и до и после выполнения аннотированного бизнес метода. Конкретный класс для имплементации этого аспекта это TransactionInterceptor.

Аспект транзакций имеет две главные функции:

  • В момент «до» аспект определяет выполнить ли выполняемый метод в пределах уже сущестувующей транзакции БД или должна стартовать новая отдельная транзакция. В момент «до» аспект сам не содержит логики по принятию решения, решение начать новую транзакцию, если это нужно, делегируется Transaction менеджеру.
  • В момент «после» аспект решает что делать с транзакцией, делать коммит, откат или оставить незакрытой.

Transaction менеджер Менеджер транзакций должен предоствить ответ на два вопроса: Должен ли создаться новый Entity Manager? Должна ли стартовать новая транзакция БД?

Ответы необходимы предоставить в момент когда вызывается логика аспекта транзакций в момент «до». Менеджер транзакций принимает решение, основываясь на следующих фактах: выполняется ли хоть одна транзакция в текущий момент; нет ли атрибута «propagation» у метода, аннотированного @Transactional (для примера, REQUIRES_NEW всегда стартует новую транзакцию).

Если менеджер решил создать новую транзакцию, тогда: Создается новый entity менеджер

«Привязка» entity менеджера к текущему потоку (Thread)

«Взятие» соединения из пула соединений БД

«Привязка» соединения к текущему потоку

И entity менеджер и это соединение привязываются к текущему потоку, используя переменные ThreadLocal. Они хранятся в потоке, пока выполняется транзакция, и затем передаются менеджеру транзакций для очистки, когда они уже будут не нужны. Любая часть программы, которой нужен текущий entity manager или соединение, может заполучить их из потока. Этим компонентом программы, который делает именно так является Entity Manager Proxy.

EntityManager proxy ПКогда бизнес метод делает вызов, например, entityManager.persist(), этот вызов не вызывается напрямую у entity менеджера. Вместо этого бизнес метод вызывает прокси, который достает текущий entity менеджер из потока, в который его положил менеджер транзакций.

Как использовать:

  1. В файле конфигурации нужно определить менеджер транзакций transactionManager для DataSource.
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource">
</property></bean>
  1. Включить поддержку аннотаций, добавив запись в контекстном xml файле вашего spring-приложения ИЛИ добавьте @EnableTransactionManagement в ваш конфигурационный файл

  2. Добавить аннотацию @Transactional в класс (метод класса) или интерфейс (метод интерфейса).

У @Transactional есть ряд параметров:

  • @Transactional (isolation=Isolation.READ_COMMITTED) - уровень изоляции.
  • @Transactional(timeout=60) - По умолчанию используется таймаут, установленный по умолчанию для базовой транзакционной системы. Сообщает менеджеру tx о продолжительности времени, чтобы дождаться простоя tx, прежде чем принять решение об откате не отвечающих транзакций.
  • @Transactional(propagation=Propagation.REQUIRED_NEW) - (Если не указано, распространяющееся поведение по умолчанию — REQUIRED.)

Когда вызывается метод с @Transactional происходит особая уличная магия: proxy, который создал Spring, создаёт persistence context (или соединение с базой), открывает в нём транзакцию и сохраняет всё это в контексте нити исполнения (натурально, в ThreadLocal). По мере надобности всё сохранённое достаётся и внедряется в бины. Привязка транзакций к нитям (threads) позволяет использовать семантику серверов приложений J2EE, в которой гарантируется, что каждый запрос получает свою собственную нить.

Таким образом, если в вашем коде есть несколько параллельных нитей, у вас будет и несколько параллельных транзакций, которые будут взаимодействовать друг с другом согласно уровням изоляции. Но что произойдёт, если один метод с @Transactional вызовет другой метод с @Transactional? В Spring можно задать несколько вариантов поведения, которые называются правилами распространения.

REQUIRES - При входе в @Transactional метод будет использована уже существующая транзакция или создана новая транзакция, если никакой ещё нет

REQUIRES_NEW - Транзакция всегда создаётся при входе метод с Propagation.REQUIRES_NEW, ранее созданные транзакции приостанавливаются до момента возврата из метода.

NESTED — корректно работает только с базами данных, которые умеют savepoints (Postgres в том числе). Savepoints — транзакции внутри транзакций. Savepoint позволяет сохранить какое-либо состояние внутри транзакции и, при необходимости, откатиться к нему, не откатывая всю транзакцию. При входе в метод в уже существующей транзакции создаётся savepoint, который по результатам выполнения метода будет либо сохранён, либо откачен. Все изменения, внесённые методом, подтвердятся только поздее, с подтверждением всей транзакции. Если текущей транзакции не существует, будет создана новая.

MANDATORY - обратный по отношению к REQUIRES_NEW: всегда используется существующая транзакция и кидается исключение, если текущей транзакции нет.

SUPPORTS - метод с этим правилом будет использовать текущую транзакцию, если она есть, либо будет исполнятся без транзакции, если её нет. Методы, которые извлекают данные, являются лучшими кандидатами для этой опции.

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

NEVER - явно запрещает исполнение в контексте транзакции. Если при входе в метод будет существовать транзакция, будет выброшено исключение. Этот вариант в большинстве случаев не используется в проектах.

  • @Transactional (rollbackFor=Exception.class) - Значение по умолчанию: rollbackFor=RunTimeException.class В Spring все классы API бросают RuntimeException, это означает, что если какой-либо метод не выполняется, контейнер всегда откатывает текущую транзакцию. Проблема заключается только в проверенных исключениях - при них транзакция пройдет в БД даже при ошибке. С этим параметром проверяемые исключения тоже будут откатываться при ошибке
  • @Transactional (noRollbackFor=IllegalStateException.class) - Указывает, что откат не должен происходить, если целевой метод вызывает это исключение. Если внутри метода с @Transactional есть другой метод с аннотацией @Transactional (вложенная транзакция), то отработает только первая (в которую вложенна). Из-за особенностей создания proxy. Но у аннотации @Transactional можно указать параметры.

Куда же ставить @Transactional? Классическое приложение обычно имеет многослойную архитектуру:

контроллеры > слой логики > слой доступа к данным > слой ORM

Где здесь место для @Transactional? Слой ORM обычно никто не пишет сам и использует какое-либо стандартное решение, в которое аннотации не вставишь.

Слой доступа к данным обычно представляет собой набор классов, методы которых реализуют тот или иной запрос. Получается, что если каждый метод аннотировать @Transactional, то, с одной стороны, работать это конечно будет, а с другой стороны теряется смысл транзакций, как логического объединения нескольких запросов в одну единицу работы. Ведь в таком случае у каждого метода, то есть у каждого запроса, будет своя, собственная, транзакция.

Слой логики представляется идеальным местом для @Transactional: именно здесь набор запросов к базе оформляется в единую осмысленную операцию в приложении. Зная, что делает ваше приложение, вы можете четко разграничить логические единицы работы в нём и расставить границы транзакций.

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

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

к оглавлению

Как Spring работает с DAO

Spring DAO предоставляет возможность работы с доступом к данным с помощью технологий вроде JDBC, Hibernate в удобном виде. Существуют специальные классы: JdbcDaoSupport, HibernateDaoSupport, JdoDaoSupport, JpaDaoSupport.

Класс HibernateDaoSupport является подходящим суперклассом для Hibernate DAO. Он содержит методы для получения сессии или фабрики сессий. Самый популярный метод - getHibernateTemplate(), который возвращает HibernateTemplate. Этот темплейт оборачивает checked-исключения Hibernate в runtime-исключения, позволяя вашим DAO оставаться независимыми от исключений Hibernate.

к оглавлению

Model vs ModelMap vs ModelAndView

Интерфейс Model инкапсулирует (объединяет) данные приложения. ModelMap реализует этот интерфейс, с возможностью передавать коллекцию значений. Затем он обрабатывает эти значения, как если бы они были внутри Map. Следует отметить, что в Model и ModelMap мы можем хранить только данные. Мы помещаем данные и возвращаем имя представления.

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

к оглавлению

В чем разница между model.put() и model.addAttribute()?

Метод addAttribute отделяет нас от работы с базовой структурой hashmap. По сути addAttribute это обертка над put, где делается дополнительная проверка на null. Метод addAttribute в отличии от put возвращает modelmap.

к оглавлению

PreparedStatementCreator

PreparedStatement - нужен для защиты от SQL-инъекций в запросах, а PreparedStatementCreator - для создания возврата PreparedStatement из connection. При этом он автоматически обрабатывает все исключения (кроме SQLExeption).

к оглавлению

SOAP vs REST

SOAP – это целое семейство протоколов и стандартов, для обмена структурированными сообщениями. Это более тяжеловесный и сложный вариант с точки зрения машинной обработки. Поэтому REST работает быстрее.

REST - это не протокол и не стандарт, а архитектурный стиль. У этого стиля есть свои принципы:

Give every “thing” an ID;

Link things together - Например, в страницу (представление) о Mercedes C218 хорошо бы добавить ссылку на страницу конкретно о двигателе данной модели, чтобы желающие могли сразу туда перейти, а не тратить время на поиск этой самой страницы;

Use standard methods - Имеется в виду, экономьте свои силы и деньги заказчика, используйте стандартные методы HTTP, например GET http://www.example.com/cars/00345 для получения данных вместо определения собственных методов вроде getCar?id=00345;

Resources can have multiple representations - Одни и те же данные можно вернуть в XML или JSON для программной обработки или обернутыми в красивый дизайн для просмотра человеком;

Communicate statelessly - Да, RESTful сервис должен быть как идеальный суд – его не должно интересовать ни прошлое подсудимого (клиента), ни будущее – он просто выносит приговор (отвечает на запрос).

Термин RESTful (веб-)сервис всего лишь означает сервис, реализованный с использованием принципов REST.

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

к оглавлению

Spring Data

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

  • CrudRepository - обеспечивает функции CRUD
  • PagingAndSortingRepository - предоставляет методы для разбивки на страницы и сортировки записей
  • JpaRepository - предоставляет связанные с JPA методы. При этом JpaRepository содержит полный API CrudRepository и PagingAndSortingRepository

Основное понятие в Spring Data — это репозиторий. Это несколько интерфейсов которые используют JPA Entity для взаимодействия с ней. Так например интерфейс ( public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID> ) обеспечивает основные операции по поиску, сохранения, удалению данных (CRUD операции).

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

Понятно что этого перечня, скорее всего не хватит для взаимодействия с сущностью, и тут можно расширить свой интерфейс дополнительными методами запросов. Запросы к сущности можно строить прямо из имени метода. Для этого используется механизм префиксов find…By, read…By, query…By, count…By, и get…By, далее от префикса метода начинает разбор остальной части. Вводное предложение может содержать дополнительные выражения, например, Distinct. Далее первый By действует как разделитель, чтобы указать начало фактических критериев. Можно определить условия для свойств сущностей и объединить их с помощью And и Or.

Если нужен специфичный метод или его реализация, которую нельзя описать через имя метода, то это можно сделать через некоторый Customized интерфейс ( CustomizedEmployees) и сделать реализацию вычисления. А можно пойти другим путем, через указание запроса (HQL или SQL), как вычислить данную функцию. Отметив запрос аннотацией @Query.

Нативный запрос можно написать так:

public interface RoleRepository extends JpaRepository<Role, Long>{
    Optional<Role> findRoleByRoleName(String name);

    @Modifying
    @Transactional
    @Query(value = "INSERT INTO user_roles (user_id, role_id) VALUE (:user_id, :role_id)", nativeQuery = true)
    void insertRoles(@Param("user_id) Long user_id, @Param("role_id") Long role_id);

    //можно использовать с EntityGraph, обычным, не NamedEntityGraph
    @EntityGraph(value = "customer.products")
    List<Customer> findAll(@Nullable Specification<Customer> specification)

Конфигурация Spring Data

Поскольку мы используем JPA, нам нужно определить свойства для подключения к базе данных в файле persistence.xml, а не в hibernate.cfg.xml. Создайте новый каталог с именем META-INF в исходной папке проекта, чтобы поместить в него файл persistence.xml.

Затем прописать в вайле свойства для подключения к базе, например:

<persistence-unit name="DataBaseName">
        <properties>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/sales" />
            <property name="javax.persistence.jdbc.user" value="root" />
            <property name="javax.persistence.jdbc.password" value="root" />
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.format_sql" value="true" />
        </properties>
    </persistence-unit>

Для работы с Spring Data JPA нам надо создать два beans-компонента: EntityManagerFactory и JpaTransactionManager. Поэтому создадим другой конфигурационный класс JpaConfig:

@Configuration
@EnableJpaRepositories(basePackages = {"net.codejava.customer"})
@EnableTransactionManagement
public class JpaConfig {
    @Bean
    public LocalEntityManagerFactoryBean entityManagerFactory() {
        LocalEntityManagerFactoryBean factoryBean = new LocalEntityManagerFactoryBean();
        factoryBean.setPersistenceUnitName("SalesDB");

        return factoryBean;
    }

    @Bean
    public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);

        return transactionManager;
    }
}

@EnableJpaRepositories: сообщает Spring Data JPA, что нужно искать классы репозитория в указанном пакете (net.codejava) для внедрения соответсвующего кода во время выполнения.

@EnableTransactionManagement: сообщает Spring Data JPA, чтобы тот генерировал код для управления транзакциями во время выполнения.

В этом классе первый метод создаёт экземпляр EntityManagerFactory для управления Persistence Unit нашей SalesDB (это имя указано выше в persistence.xml).

Последний метод создаёт экземпляр JpaTransactionManager для EntityManagerFactory, созданный методом ранее.

Это минимальная необходимая конфигурация для использования Spring Data JPA.

Spring Security

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

Spring Security - это список фильтров в виде класса FilterChainProxy, интегрированного в контейнер сервлетов, и в котором есть поле List. Каждый фильтр реализует какой-то механизм безопасности. Важна последовательность фильтров в цепочке.

Image alt

Когда мы добавляем аннотацию @EnableWebSecurity добавляется DelegatingFilterProxy, его задача заключается в том, чтобы вызвать цепочку фильтров (FilterChainProxy) из Spring Security.

В Java-based конфигурации цепочка фильтров создается неявно.

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

@Override
protected void configure(HttpSecurity http) throws Exception {
   http
   .csrf().disable()
   .authorizeRequests();
}

Именно этот метод конфигурирует цепочку фильтров Spring Security и логика, указанная в этом методе, настроит цепочку фильтров.

Основные классы и интерфейсы

SecurityContext - интерфейс, отражающий контекст безопасности для текущего потока. Является контейнером для объекта типа Authentication. (Аналог - ApplicationContext, в котором лежат бины).

По умолчанию на каждый поток создается один SecurityContext. SecurityContext-ы хранятся в SecurityContextHolder.

Имеет только два метода: getAuthentication() и setAuthentication(Authentication authentication). SecurityContextHolder - это место, где Spring Security хранит информацию о том, кто аутентифицирован. Класс, хранящий в ThreadLocal SecurityContext-ы для каждого потока, и содержащий статические методы для работы с SecurityContext-ами, а через них с текущим объектом Authentication, привязанным к нашему веб-запросу. Image alt

Authentication - объект, отражающий информацию о текущем пользователе и его привилегиях. Вся работа Spring Security будет заключаться в том, что различные фильтры и обработчики будут брать и класть объект Authentication для каждого посетителя. Кстати объект Authentication можно достать в Spring MVC контроллере командой SecurityContextHolder.getContext().getAuthentication(). Authentication имеет реализацию по умолчанию - класс UsernamePasswordAuthenticationToken, предназначенный для хранения логина, пароля и коллекции Authorities. Principal - интерфейс из пакета java.security, отражающий учетную запись пользователя. В терминах логин-пароль это логин. В интерфейсе Authentication есть метод getPrincipal(), возвращающий Object. При аутентификации с использованием имени пользователя/пароля Principal реализуется объектом типа UserDetails. Credentials - любой Object; то, что подтверждает учетную запись пользователя, как правило пароль (отпечатки пальцев, пин - всё это Credentials, а владелец отпечатков и пина - Principal). GrantedAuthority - полномочия, предоставленные пользователю, например, роли или уровни доступа. UserDetails - интерфейс, представляющий учетную запись пользователя. Как правило модель нашего пользователя должна имплементировать его. Она просто хранит пользовательскую информацию в виде логина, пароля и флагов isAccountNonExpired, isAccountNonLocked, isCredentialsNonExpired, isEnabled, а также коллекции прав (ролей)пользователя. Данная информация позже инкапсулируется в объекты Authentication. UserDetailsService - интерфейс объекта, реализующего загрузку пользовательских данных из хранилища. Созданный нами объект с этим интерфейсом должен обращаться к БД и получать оттуда юзеров. используется чтобы создать UserDetails объект путем реализации единственного метода этого интерфейса

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

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

public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}

AuthenticationManager может сделать одну из 3 вещей в своем методе authenticate():

  1. вернуть Authentication (с authenticated=true), если предполагается, что вход осуществляет корректный пользователь.
  2. бросить AuthenticationException, если предполагается, что вход осуществляет некорректный пользователь.
  3. вернуть null, если принять решение не представляется возможным.

Наиболее часто используемая реализация AuthenticationManager - родной класс ProviderManager, который содержит поле private Listproviders со списком AuthenticationProvider-ов и итерирует запрос аутентификации по этому списку AuthenticationProvider-ов. Идея такого разделения - поддержка различных механизмов аутентификации на сайтах.

AuthenticationProvider - интерфейс объекта, выполняющего аутентификацию. Имеет массу готовых реализаций. Также можем задать свой тип аутентификации. Как правило в небольших проектах одна логика аутентификации - по логину и паролю. В проектах побольше логик может быть несколько: Google-аутентификация и т.д., и для каждой из них создается свой объект AuthenticationProvider.

AuthenticationProvider немного похож на AuthenticationManager, но у него есть дополнительный метод, позволяющий вызывающей стороне спрашивать, поддерживает ли он переданный ему объект Authentication, возможно этот AuthenticationProvider может поддерживать только аутентификацию по логину и паролю, но не поддерживать Google�аутентификацию:

boolean supports(java.lang.Class<?> authentication)

PasswordEncoder - интерфейс для шифрования/расшифровывания паролей. Одна из популярных реализаций - BCryptPasswordEncoder.

В случае, если нам необходимо добавить логику при успешной/неудачной аутентификации, мы можем создать класс и имплементировать интерфейсы AuthenticationSuccessHandler и AuthenticationFailureHandler соответственно, переопределив их методы.

Как это работает с формой логина и UserDetailsService:

  • Пользователь вводит в форму и отправляет логин и пароль.
  • UsernamePasswordAuthenticationFilter создает объект Authentication - UsernamePasswordAuthenticationToken, где в качестве Principal - логин, а в качестве Credentials - пароль.
  • Затем UsernamePasswordAuthenticationToken передаёт объект Authentication с логином и паролем AuthenticationManager-у.
  • AuthenticationManager в виде конкретного класса ProviderManager внутри своего списка объектов AuthenticationProvider, имеющих разные логики аутентификации, пытается аутентифицировать посетителя, вызывая его метод authenticate(). У каждого AuthenticationProvider-а: 1 Метод authenticate() принимает в качестве аргумента незаполненный объект Authentication, например только с логином и паролем, полученными в форме логина на сайте. Затем с помощью UserDetailsService метод идёт в БД и ищет такого пользователя. 2 Если такой пользователь есть в БД, AuthenticationProvider получает его из базы в виде объекта UserDetails. Объект Authentication заполняется данными из UserDetails - в него включаются Authorities, а в Principal записывается сам объект UserDetails, содержащий пользователя. 3 Затем этот метод возвращает заполненный объект Authentication (прошли аутентификацию). Вызывается AuthenticationSuccessHandler. 4 Если логин либо пароль неверные, то выбрасывается исключение. Вызывается AuthenticationFailureHandler.
  • Затем этот объект Authentication передается в AccessDecisionManager и получаем решение на получение доступа к запрашиваемой странице (проходим авторизацию).

Аннотации:

  • @EnableGlobalMethodSecurity - включает глобальный метод безопасности.
  • @EnableWebMvcSecurity - "включает" Spring Security. Не будет работать, если наш класс не наследует WebSecurityConfigurerAdapter
  • @Secured используется для указания списка ролей в методе
  • @PreAuthorize и @PostAuthorize обеспечивают контроль доступа на основе выражений. @PreAuthorize проверяет данное выражение перед входом в метод, тогда как аннотация @PostAuthorize проверяет его после выполнения метода и может изменить результат.
  • @PreFilter для фильтрации аргумента коллекции перед выполнением метода

к оглавлению

Spring Boot

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

Чтобы ускорить процесс управления зависимостями, Spring Boot неявно упаковывает необходимые сторонние зависимости для каждого типа приложения на основе Spring и предоставляет их разработчику посредством так называемых starter-пакетов (spring-boot-starter-web, spring-boot-starter-data-jpa и т.д.)

Starter-пакеты представляют собой набор удобных дескрипторов зависимостей, которые можно включить в свое приложение. Это позволит получить универсальное решение для всех, связанных со Spring технологий, избавляя программиста от лишнего поиска примеров кода и загрузки из них требуемых дескрипторов зависимостей. Например, если вы хотите начать использовать Spring Data JPA для доступа к базе данных, просто включите в свой проект зависимость spring-boot-starter-data-jpa и все будет готово (вам не придется искать совместимые драйверы баз данных и библиотеки Hibernate)

Например, если вы добавите Spring-boot-starter-web, Spring Boot автоматически сконфигурирует такие зарегистрированные бины, как DispatcherServlet, ResourceHandlers, MessageSource. Если вы используете spring-boot-starter-jdbc, Spring Boot автоматически регистрирует бины DataSource, EntityManagerFactory, TransactionManager и считывает информацию для подключения к базе данных из файла application.properties

В основе "магии" Spring Boot нет ничего магического, он использует совершенно базовые понятия из Spring Framework. В кратком виде процесс можно описать так:

  • Аннотация @SpringBootApplication включает сканирование компонентов и авто-конфигурацию через аннотацию @EnableAutoConfiguration
  • @EnableAutoConfiguration импортирует класс EnableAutoConfigurationImportSelector
  • EnableAutoConfigurationImportSelector загружает список конфигураций из файла META-INF/spring.factories
  • Каждая конфигурация пытается сконфигурить различные аспекты приложения (web, JPA, AMQP etc), регистрируя нужные бины и используя различные условия (наличие / отсутствие бина, настройки, класса и т.п.)
  • Созданный в итоге AnnotationConfigEmbeddedWebApplicationContext ищет в том же DI контейнере фабрику для запуска embedded servlet container
  • Servlet container запускается, приложение готово к работе!

Важное понятие Spring Boot это автоконфигурация. По сути, это просто набор конфигурационных классов, которые создают и регистрируют определенные бины в приложении. По большому счету, даже сам Embedded Servlet Container — это просто еще один бин, который можно сконфигурировать! Пара важных моментов, которые важно знать об автоконфигурации:

  • Включается аннотацией @EnableAutoConfiguration
  • Работает в последнюю очередь, после регистрации пользовательских бинов
  • Принимает решения о конфигурации на основании доступных в classpath классов, свойств в application.properties и т.п.
  • Можно включать и выключать разные аспекты автоконфигурации, и применять ее частично (например, только MySQL + JPA, но не веб)
  • Всегда отдает приоритет пользовательским бинам. Если ваш код уже зарегистрировал бин DataSource — автоконфигурация не будет его перекрывать

Логика при регистрации бинов управляется набором @ConditionalOn* аннотаций. Можно указать, чтобы бин создавался при наличии класса в classpath (@ConditionalOnClass), наличии существующего бина (@ConditionalOnBean), отсуствии бина (@ConditionalOnMissingBean) и т.п.

Отключить ненужные автоконфигурации можно при помощи свойств exclude и excludeName аннотаций @EnableAutoConfiguration, @ImportAutoConfiguration и @SpringBootApplication. Или в property задать SpringAutoconfiguration exclude и передать имена классов.

Можно отказаться от использования механизма автоконфигурации, вместо этого указывая необходимые автоконфигурации вручную. Для этого надо избавиться от аннотаций @SpringBootApplication и @EnableAutoConfiguration в коде вашего проекта, а для указания нужных конфигурационных классов использовать аннотации @SpringBootConfiguration и @ImportAutoConfiguration. Однако стоит помнить, что используемые автоконфигурации всё ещё могут содержать неиспользуемые компоненты.

Как происходит автоконфигурация в Spring Boot:

  1. Отмечаем main класс аннотацией @SpringBootApplication (аннотация инкапсулирует в себе:@SpringBootConfiguration, @ComponentScan, @EnableAutoConfiguration), таким образом наличие @SpringBootApplication включает сканирование компонентов, автоконфигурацию и показывает разным компонентам Spring (например, интеграционным тестам), что это Spring Boot приложение.
@SpringBootApplication
   public class DemoApplication {
   public static void main(String[] args) {
     SpringApplication.run(DemoApplication.class, args);
   }
   }
  1. @EnableAutoConfiguration импортирует класс EnableAutoConfigurationImportSelector. Этот класс не объявляет бины сам, а использует так называемые фабрики.
  2. Класс EnableAutoConfigurationImportSelector смотрит в файл META-INF/spring.factories и загружает оттуда список значений, которые являются именами классов (авто)конфигураций, которые Spring Boot импортирует. Т.е. аннотация @EnableAutoConfiguration просто импортирует ВСЕ (более 150) перечисленные в spring.factories конфигурации, чтобы предоставить нужные бины в контекст приложения.
  3. Каждая из этих конфигураций пытается сконфигурировать различные аспекты приложения(web, JPA, AMQP и т.д.), регистрируя нужные бины. Логика при регистрации бинов управляется набором @ConditionalOn* аннотаций. Можно указать, чтобы бин создавался при наличии класса в classpath (@ConditionalOnClass), наличии существующего бина (@ConditionalOnBean), отсуствии бина (@ConditionalOnMissingBean) и т.п. Таким образом наличие конфигурации не значит, что бин будет создан, и в большинстве случаев конфигурация ничего делать и создавать не будет.
  4. Созданный в итоге AnnotationConfigEmbeddedWebApplicationContext ищет в том же DI контейнере фабрику для запуска embedded servlet container.
  5. Servlet container запускается, приложение готово к работе!

к оглавлению

Starter packs

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

Делаем свой Starter-пакет:

  • Создаем AutoConfiguration-класс, который Spring Boot находит при запуске приложения и использует для автоматического создания и конфигурирования бинов.
@Configuration   //указываем, что наш класс является конфигурацией (@Configuration)
@ConditionalOnClass({SocialConfigurerAdapter.class, VKontakteConnectionFactory.class})   //означает, что бины будут создаваться при наличии в classpath SocialConfigurerAdapter и VKontakteConnectionFactory. Таким образом, без нужных для нашего стартера зависимостей бины создаваться не будут.

@ConditionalOnProperty(prefix= "ru.shadam.social-vkontakte", name = { "client-id", "client-secret"})     //означает, что бины будут создаваться только при наличии property ru.shadam.social-vkontakte.client-id и ru.shadam.social-vkontakte.client-secret.
@AutoConfigureBefore(SocialWebAutoConfiguration.class)
@AutoConfigureAfter(WebMvcAutoConfiguration.class)          //означает, что наш бин будет инициализироваться после WebMvc и до SocialWeb. Это нужно, чтобы к моменту инициализации SocialWeb наши бины уже были зарегистрированы.
public class VKontakteAutoConfiguration {
}
  • Расширяем SocialConfigurationAdapter, который нужен для того чтобы зарегистрировать нашу ConnectionFactory. Для этого переопределяем метод addConnectionFactories(ConnectionFactoryConfigurer, Environment)
  • Создание файла, позволяющего SpringBoot найти наш AutoConfiguration класс. Для этого существует специальный файл spring.factories, который нужно поместить в META-INF папку получающегося jar-файла. В этом файле нам надо указать наш AutoConfiguration-класс.
  • Подключить получившийся jar-файл к Spring Boot проекту и задать в конфигурации пути на класс, который создали до этого.

https://www.youtube.com/watch?v=nGfeSo52_8A&t=1735s (с 30мин)

к оглавлению

Как внедрить java.util.Properties в Spring Bean

Используя SpEL

@Value("${maxReadResults}")
private int maxReadResults;

Или определить propertyConfigure bean в XML.

к оглавлению

Что нового в Spring 5

  • Используется JDK 8+ (Optional, CompletableFuture, Time API, java.util.function, default methods)
  • Поддержка Java 9 (Automatic-Module-Name in 5.0, module-info in 6.0+, ASM 6)
  • Поддержка HTTP/2 (TLS, Push), NIO/NIO.2, Kotlin
  • Поддержка Kotlin
  • Реактивность (Web on Reactive Stack)
  • Null-safety аннотации(@Nullable), новая документация
  • Совместимость с Java EE 8 (Servlet 4.0, Bean Validation 2.0, JPA 2.2, JSON Binding API 1.0)
  • Поддержка JUnit 5 + Testing Improvements (conditional and concurrent)
  • Удалена поддержка: Portlet, Velocity, JasperReports, XMLBeans, JDO, Guava

к оглавлению

RestTemplate и JDBCTemplate

Класс RestTemplate является центральным инструментом для выполнения клиентских HTTP-операций в Spring. Он предоставляет несколько утилитных методов для создания HTTP-запросов и обработки ответов.

JdbcTemplate - базовый класс, который управляет обработкой всех событий и связями с БД посредством sql запросов. При этом все пишется программистом, не автоматизированно.

к оглавлению

Socket

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

к оглавлению

Полный курс по Spring.

  1. 1. Intro

  2. lesson 1. Введение
  3. Модули Spring (out of the box)
  4. Удобство и простота использования
  5. Микросервисная архитектура
  6. Support & Community
  7. Что нужно знать для изучения курса Spring
  8. lesson 2. Установка программного обеспечения
  9. Установка Java 17
  10. Установка IntelliJ IDEA Ultimate Edition
  11. Установка PostgreSQL
  12. Установка Docker
  13. Создание нового проекта
  14. lesson 3. Inversion of Control. Dependency Injection
  15. Object Dependencies
  16. Object Dependencies в коде
  17. Inversion of Control & Dependency Injection
  18. Inversion of Control & Dependency Injection в коде
  19. 3 способа внедрения зависимостей в объекте
  20. lesson 4. IoC Container
  21. Spring IoC Container
  22. Bean
  23. Bean Definition
  24. POJO
  25. Основные интерфейсы IoC Container
  26. 3 способа создания Bean Definitions
  27. 2. XML-based Configuration

  28. lesson 5. XML-based Configuration
  29. BeanFactory и ApplicationContext интерфейсы
  30. ClassPathXmlApplicationContext
  31. XML config
  32. Идентификаторы (id) бинов как ключи в IoC Container
  33. Алиасы бинов (alias)
  34. lesson 6. Constructor Injection
  35. Внедрение примитивных типов данных
  36. Внедрение коллекций list/set
  37. Внедрение ассоциативного массива map
  38. Поле genericArgumentValues в BeanDefinition
  39. Поле indexedArgumentValues в BeanDefinition
  40. Указание атрибута type в параметрах конструктора
  41. Указание атрибута name в параметрах конструктора
  42. lesson 7. Factory Method Injection
  43. Внедрение других бинов через ref*
  44. Создание новое бина CompanyRepository
  45. Внедрение зависимостей через factory method
  46. Атрибут factory-bean (паттерн ServiceLocator)
  47. lesson 8. Property Injection
  48. Использование set* методов в ConnectionPool
  49. Поле propertyValues в BeanDefinition
  50. Упрощенный жизненный цикл бинов - Bean Lifecycle
  51. Плюсы и минусы Constructor и Property Injections
  52. Циклические зависимости через Property Injection
  53. lesson 9. Bean Scopes
  54. Common Bean Scopes
  55. Custom Bean Scopes
  56. Web Bean Scopes
  57. Prototype Bean Scope
  58. lesson 10. Lifecycle Callbacks
  59. Измененный Bean Lifecycle
  60. Initialization callbacks
  61. Destruction callbacks
  62. lesson 11. Injection from Properties Files
  63. Зачем использовать properties files
  64. Создание файла application.properties
  65. PropertySourcesPlaceholderConfigurer bean
  66. Expression Language (EL)
  67. Spring Expression Language (SpEL)
  68. SpEL документация
  69. System properties
  70. lesson 12. BeanFactoryPostProcessor (BFPP)
  71. Интерфейс BeanFactoryPostProcessor
  72. Как работает PropertySourcesPlaceholderConfigurer
  73. Измененный Bean Lifecycle
  74. Метод isAssignableFrom
  75. lesson 13. Custom BeanFactoryPostProcessor
  76. Создание собственных BeanFactoryPostProcessor
  77. Интерфейс Ordered
  78. Интерфейс PriorityOrdered
  79. 3. Annotation-based Configuration

  80. lesson 14. Annotation-based Configuration
  81. Подключение зависимости jakarta annotation api
  82. Аннотации @PostConstruct и @PreDestroy
  83. Класс CommonAnnotationBeanPostProcessor
  84. context:annotation-config xml element
  85. lesson 15. BeanPostProcessor (BPP)
  86. Интерфейс BeanPostProcessor
  87. Bean Lifecycle (final version)
  88. Интерфейс Aware
  89. Класс ApplicationContextAwareProcessor
  90. lesson 16. Custom BeanPostProcessor. Часть 1
  91. Создание своей аннотации @InjectBean
  92. Создание InjectBeanPostProcessor
  93. Утилитный класс ReflectionUtils
  94. Тестирование InjectBeanPostProcessor
  95. lesson 17. Custom BeanPostProcessor. Часть 2
  96. Создание аннотации @Transaction
  97. Создание CrudRepository
  98. Создание TransactionBeanPostProcessor
  99. Тестирование TransactionBeanPostProcessor
  100. Корректируем TransactionBeanPostProcessor
  101. Создание AuditingBeanPostProcessor
  102. lesson 18. @Autowired & @Value
  103. Аннотация @Autowired
  104. Аннотация @Resource
  105. Решение конлифкта бинов. @Qualifier
  106. Collection injection
  107. Properties injection. @Value
  108. lesson 19. Classpath Scanning
  109. context:component-scan. Аннотации @Component
  110. Замена бинов из xml на @Component
  111. lesson 20. Bean Definition Readers
  112. Component Scan classes
  113. Bean Definition Readers
  114. Класс ComponentScanBeanDefinitionParser
  115. Класс AnnotatedBeanDefinitionReader
  116. lesson 21. Type Filters
  117. Атрибут annotation-config
  118. Атрибут name-generator
  119. Атрибут resource-pattern
  120. Атрибут scoped-proxy
  121. Атрибут scope-resolver
  122. Атрибут use-default-filters
  123. 5 type filters
  124. Custom filters
  125. lesson 22. @Scope
  126. Атрибут scope-resolver
  127. Класс AnnotationScopeMetadataResolver
  128. Аннотация @Scope
  129. lesson 23. JSR 250, JSR 330
  130. Аббревиатура JSR
  131. JSR 250
  132. JSR 330
  133. Класс Jsr330ScopeMetadataResolver
  134. 4. Java-based Configuration

  135. lesson 24. Java-based Configuration
  136. Класс ConfigurationClassBeanDefinitionReader
  137. Создание ApplicationConfiguration. @Configuration
  138. Аннотация @PropertySource
  139. Аннотация @ComponentScan
  140. Класс AnnotationConfigApplicationContext
  141. lesson 25. @Import & @ImportResource
  142. Класс AnnotationConfigApplicationContext
  143. Аннотация @ImportResource
  144. Аннотация @Import
  145. lesson 26. @Bean. Часть 1
  146. Аннотация @Bean
  147. Тестирование Java конфигурации
  148. Свойства аннотации @Bean
  149. Аннотация @Scope с @Bean
  150. Внедрение зависимостей с @Bean
  151. Конфликт имен @Bean и @Component
  152. lesson 27. @Bean. Часть 2
  153. 3-ий вариант внедрения зависимостей в @Bean
  154. Cglib proxy в @Configuration
  155. Свойство proxyBeanMethods в @Configuration
  156. @Bean создаются через паттерн Service Locator
  157. lesson 28. Profiles
  158. Environment Bean
  159. Аннотация @Profile
  160. Активация profiles через properties
  161. Активация profiles через ApplicationContext
  162. 5. Event Listeners

  163. lesson 29. Event Listeners. Часть 1
  164. Шаблон проектирования Listener
  165. Создание события (Event)
  166. Создание слушателя событий (Listener). @EventListener
  167. Реализация логики для отправки события
  168. lesson 30. Event Listeners. Часть 2
  169. Bean ApplicationEventPublisher
  170. Тестирование слушателей событий
  171. Listeners order
  172. Listeners conditions
  173. 6. Spring Boot

  174. lesson 31. Spring Boot. Введение
  175. Spring modules
  176. Spring Data Configuration
  177. Modules Auto Configuration
  178. Conditions
  179. Spring Boot Starters
  180. Dependency Management
  181. How to build Spring Boot Application
  182. lesson 32. @Conditional
  183. Аннотация @Conditional
  184. Класс Condition
  185. Custom JpaCondition
  186. Тестирование JpaCondition
  187. Аннотация @Profile
  188. Другие @Condition аннотации
  189. lesson 33. Spring Boot. Настройка проекта
  190. Spring Boot Gradle Plugin
  191. Spring Dependency Management Plugin
  192. spring-boot-starter
  193. Run Spring Boot Application
  194. Autogenerated Spring Boot Project
  195. Maven spring-boot-starter-parent pom
  196. lesson 34. @SpringBootApplication
  197. Структура Spring Boot приложения
  198. Что делает метод SpringApplication.run
  199. Аннотация @SpringBootApplication
  200. Аннотация @SpringBootConfiguration
  201. Аннотация @ComponentScan
  202. Аннотация @PropertySource
  203. Аннотация @EnableAutoConfiguration
  204. lesson 35. Lombok
  205. Подключение Lombok
  206. Gradle Lombok Plugin
  207. IntelliJ IDEA Lombok Plugin
  208. Аннотации Lombok
  209. Файл lombok.config
  210. lesson 36. Properties
  211. Файл spring.properties
  212. Externalized Configuration
  213. Profile-specific properties
  214. Spring program arguments & VM options
  215. Property Placeholders & Default values
  216. spring.config.location
  217. lesson 37. Yaml format
  218. YAML - Yet Another Markup Language
  219. Класс YamlPropertiesFactoryBean
  220. Приоритет properties vs yaml
  221. Переписывание application.properties на yaml
  222. lesson 38. @ConfigurationProperties
  223. Класс JpaProperties
  224. Класс DatabaseProperties
  225. Аннотация @ConfigurationProperties
  226. Аннотация @ConfigurationPropertiesScan
  227. Immutable DatabaseProperties
  228. DatabaseProperties as record
  229. Property key names
  230. 7. Logging Starter

  231. lesson 39. Logging Starter
  232. Application as a Black Box
  233. Logging libraries
  234. Log Levels
  235. spring-boot-starter-logging dependency
  236. Аннотация @Slf4j
  237. Delombok annotations
  238. Формат логов по умолчанию
  239. logging.* properties
  240. lesson 40. Logback Configuration
  241. Logback default xml configs
  242. File Output
  243. Custom log configuration
  244. 8. Test Starter

  245. lesson 41. Test Starter
  246. Подключение spring-boot-starter-test
  247. Транзитивные зависимости spring-boot-starter-test
  248. Зависимость spring-test
  249. Зависимость spring-boot-test
  250. Зависимость spring-boot-test-autoconfigure
  251. Пример написания Unit тестов
  252. Java Gradle Plugin tasks relationship
  253. lesson 42. Integration Testing. Part 1
  254. Основные цели Spring Integration Testing
  255. Жизненный цикл тестов
  256. JUnit 5 Extension Model
  257. TestContext Framework
  258. SpringExtension source code
  259. lesson 43. Integration Testing. Part 2
  260. Создание CompanyServiceIT
  261. SpringExtension via @ExtendWith
  262. Аннотация @ContextConfiguration
  263. Аннотация @TestPropertySource
  264. Класс ApplicationContextInitializer
  265. Аннотация @SpringBootTest
  266. Написание первого интеграционного теста
  267. Класс DependencyInjectionTestExecutionListener
  268. lesson 44. Integration Testing. Part 3
  269. Аннотация @ActiveProfiles
  270. Custom Test Annotations
  271. Аннотация @TestConstructor
  272. Замена @TestConstructor на spring.properties
  273. lesson 45. Context Caching
  274. Создание нескольких ApplicationContext в тестах
  275. Аннотации @MockBean и @SpyBean
  276. Класс MockitoTestExecutionListener
  277. Аннотация @TestConfiguration
  278. Аннотация @DirtiesContext
  279. 9. Data JPA Starter

  280. lesson 46. Data JPA Starter. Введение
  281. Чего не хватало в Hibernate
  282. Установка PostgreSQL
  283. Установка Docker
  284. Postgres Docker Image
  285. Подключение к postgres из IntelliJ IDEA
  286. lesson 47. Data JPA Starter. Подключение
  287. Подключение spring-boot-starter-data-jpa
  288. Зависимости spring-boot-starter-data-jpa
  289. Класс HibernateJpaAutoConfiguration
  290. Настройка spring.datasource и spring.jpa properties
  291. Тестирование приложения
  292. lesson 48. Hibernate Entities
  293. UML диаграмма выполненных sql скриптов
  294. Создание сущности Company
  295. Создание коллекции locales (ElementCollection)
  296. Создание сущности User
  297. Создание сущности Payment
  298. Создание сущности Chat
  299. Создание сущности UserChat
  300. Проверка маппинга сущностей через hbm2ddl.auto
  301. Аннотация @EntityScan
  302. 10. Data JPA Transactions

  303. lesson 49. @Transactional. TestContext
  304. Общая структура работы с TransactionManager
  305. Создание CompanyRepository IT
  306. Аннотации @Transactional из Jakarta EE и Spring
  307. Класс TransactionalTestExecutionListener
  308. Аннотации @Commit и @Rollback
  309. lesson 50. TransactionAutoConfiguration
  310. Класс TransactionAutoConfiguration
  311. Как происходит обработка транзакций в proxy
  312. Аннотация @Transactional и Cglib proxy
  313. Как работает Cglib proxy с TransactionManager
  314. Как подключить механизм транзакций внутри объекта (не proxy)
  315. Механизм транзакций между несколькими Cglib proxy
  316. lesson 51. @Transactional Settings
  317. Свойства @Transactional. transactionManager
  318. Transaction propagation
  319. Transaction propagation резюме
  320. Transaction isolation
  321. Transaction timeout
  322. ReadOnly transaction
  323. Transaction rollbackFor & rollbackForClassName
  324. Transaction noRollbackFor & noRollbackForClassName
  325. lesson 52. Manual Transactions
  326. Свойства объекта TransactionTemplate
  327. Функционал TransactionTemplate
  328. Обработка checked exceptions
  329. Взаимодействие TransactionTemplate с другими Proxy
  330. Вынесение @Transactional в @IT
  331. 11. Data JPA Repositories

  332. lesson 53. Repository
  333. Интерфейс Repository
  334. Написание теста на удаление Company
  335. Класс JpaRepositoryAutoConfiguration
  336. lesson 54. RepositoryQuery
  337. Создание Proxy на классы Repository
  338. Класс QueryExecutorMethodInterceptor
  339. Класс RepositoryQuery
  340. Наследники Repository
  341. lesson 55. PartTreeJpaQuery
  342. Класс PartTreeJpaQuery
  343. Примеры написания запросов
  344. Тестирование запросов
  345. Весь список ключевых слов PartTreeJpaQuery
  346. lesson 56. NamedQuery
  347. Недостатки PartTreeJpaQuery
  348. Класс NamedQuery
  349. Аннотация @NamedQuery
  350. Тестирование NamedQuery
  351. Аннотация @Param
  352. lesson 57. @Query
  353. StoredProcedureJpaQuery
  354. Аннотация @Query
  355. Демонстрация работы @Query
  356. Усовершенствованный оператор LIKE
  357. Native Query
  358. lesson 58. @Modifying
  359. Запрос на обновление через @Query
  360. Аннотация @Modifying
  361. Hibernate PersistenceContext
  362. Свойства clearAutomatically и flushAutomatically
  363. clearAutomatically и LazyInitializationException
  364. lesson 59. Special parameters
  365. Top & First
  366. TopN & FirstN
  367. Класс Sort
  368. Класс Pageable
  369. lesson 60. Page & Slice
  370. Spring классы Streamable, Slice, Page
  371. Демонстрация работы Slice объекта
  372. Почему Slice объекта недостаточно
  373. Демонстрация работы Page объекта
  374. lesson 61. @EntityGraph
  375. Аннотация @EntityGraph
  376. Именованные графы @NamedEntityGraph
  377. Свойство attributePaths в @EntityGraph
  378. Конфликт Pageable при получении EAGER связей
  379. lesson 62. @Lock & @QueryHints
  380. Аннотация @Lock
  381. Демонстрация пессимистических блокировок
  382. Аннотация @QueryHints
  383. lesson 63. Projection
  384. Class-based Projections
  385. Generic Class-based Projections
  386. Interface-based Projections
  387. SpEL in Projections
  388. lesson 64. Custom Repository Implementation
  389. Запрос фильтрации через Custom Implementation
  390. Criteria API для запроса фильтрации
  391. Аннотация @EnableJpaRepository
  392. Тестирование запроса фильтрации
  393. lesson 65. JPA Auditing
  394. Создание AuditingEntity
  395. Аннотация @EnableJpaAuditing
  396. Тестирование @CreatedDate и @LastModifiedDate
  397. Аннотации @CreatedBy и @LastModifiedBy
  398. Создание AuditorAware Bean
  399. Тестирование @CreatedBy и @LastModifiedBy
  400. lesson 66. Hibernate Envers
  401. Подключение Hibernate Envers
  402. Создание сущности Revision
  403. Аннотация @Audited
  404. Аннотация @EnableEnversRepositories
  405. #Тестирование Hibernate Envers
  406. Класс RevisionRepository
  407. lesson 67. Querydsl
  408. Подключение Querydsl
  409. Создание QPredicates
  410. Замена Criteria API на Querydsl
  411. Тестирование Querydsl
  412. Класс QuerydslPredicateExecutor
  413. 12. JDBC Starter

  414. lesson 68. JDBC Starter
  415. Зависимость spring-boot-starter-jdbc
  416. JdbcTemplateAutoConfiguration
  417. Функционал класса JdbcTemplate
  418. Практика JdbcTemplate
  419. Подключение логов для JdbcTemplate
  420. lesson 69. Batch size & Fetch size
  421. Batch запросы
  422. Batch запрос через JdbcTemplate
  423. Тестирование Batch запроса через JdbcTemplate
  424. Batch запрос через NamedParameterJdbcTemplate
  425. Установка batch_size в Hibernate
  426. Fetch size
  427. 13. Databases in tests

  428. lesson 70. In-Memory databases. H2
  429. Два варианта поднятия тестовой базы данных
  430. Подключение H2 database
  431. Аннотация @Sql
  432. lesson 71. Testcontainers
  433. testcontainers lib
  434. Подключение testcontainers
  435. Создание IntegrationTestBase
  436. Тестирование testcontainers
  437. Тестовые данные (ids)
  438. 14. Database Migrations

  439. lesson 72. Liquibase. Теория
  440. Устройство migration frameworks
  441. Стуктура Liquibase changelog
  442. Changelog master file
  443. lesson 73. Liquibase. Практика
  444. Подключение зависимости liquibase-core
  445. Класс LiquibaseAutoConfiguration
  446. Создание master changelog
  447. liquibase formatted sql
  448. Тестирование Liquibase
  449. Добавление нового changelog (envers tables)
  450. md5sum
  451. Использование Liquibase в тестах
  452. 15. Web Starter

  453. lesson 74. Web Starter. Введение
  454. MVC и классические web-приложения
  455. web-приложение на Spring Boot
  456. Embedded Tomcat
  457. Настройка spring-web приложения
  458. Класс WebMvcAutoConfiguration
  459. lesson 75. Dispatcher Servlet
  460. Жизненный цикл сервлетов
  461. Псевдокод метода service в DispatcherServlet
  462. Исходный код класса DispatcherServlet
  463. lesson 76. @Controller
  464. Подключение зависимостей и настройка view resolver
  465. Создание контроллера. @Controller
  466. lesson 77. @RequestMapping
  467. Основные составляющие HTTP запроса и HTTP ответа
  468. Основные составляющие URL
  469. Аннотации @RequestMapping
  470. lesson 78. Parameters, Headers, Cookies
  471. Parameters. @RequestParam annotation
  472. Headers. @RequestHeader annotation
  473. Cookies. @CookieValue annotation
  474. Method params naming
  475. DispatcherServlet sources
  476. @PathVariable annotation
  477. lesson 79. Model
  478. Attributes
  479. Добавление Request атрибутов в Model
  480. Добавление Session атрибутов в Model
  481. DispatcherServlet sources
  482. lesson 80. @ModelAttribute
  483. Упрощение работы с объектом ModelAndView
  484. Динамическое создание атрибутов
  485. Аннотация @ModelAttribute
  486. HTML Form. LoginController
  487. lesson 81. Forward, Include, Redirect
  488. 3 вида перенаправления запросов
  489. forward in Spring
  490. redirect in Spring
  491. lesson 82. CRUD. API Design
  492. API design best practices
  493. CRUD. Method findAll
  494. CRUD. Method findById
  495. CRUD. Method create
  496. CRUD. Method update
  497. CRUD. Method delete
  498. lesson 83. CRUD. Service Layer
  499. UserService. Method findAll
  500. UserService. Method findById
  501. UserService. Method create
  502. @Transactional annotation
  503. UserService. Method update
  504. UserService. Method delete
  505. Test UserService functionality
  506. Tips. Method delete
  507. lesson 84. Spring MVC Testing
  508. Аннотация @AutoConfigureMockMvc
  509. Test findAll method
  510. Transactions. spring.jpa.open-in-view property
  511. Test create method
  512. Problem with sending dates in params
  513. lesson 85. Type Converters
    1. spring.mvc.format properties
    1. Аннотация @DateTimeFormat
    1. Интерфейс WebMvcConfigurer
  514. 16. Thymeleaf

  515. lesson 86. Thymeleaf Starter. Введение
  516. View Resolvers
  517. Thymeleaf Template Engine Intro
  518. Настройка Thymeleaf в проекте
  519. Использование Thymeleaf
  520. lesson 87. CRUD. View Layer. Часть 1
  521. Создание users.html для метода findAll
  522. Создание user.html для метода findById
  523. Добавление кнопки для метода delete
  524. lesson 88. CRUD. View Layer. Часть 2
  525. Создание registration endpoint
  526. Создание registration.html registration
  527. redirect с сохранением введенных параметров
  528. lesson 89. Filter Query
  529. Add UserFilter - Controller & Service layers
  530. Add UserFilter - users.html
  531. lesson 90. Pagination. Best practices
  532. HTTP endpoints best practices
  533. 2 options of pagination implementation
  534. offset-based pagination
  535. PageableArgumentResolver
  536. Building PageResponse
  537. 17. Validation Starter

  538. lesson 91. Validation Starter. Введение
  539. Подключение validation starter
  540. Validation annotations
  541. How to use annotations in practice
  542. @Valid & @Validated
  543. BindingResult object
  544. Show validation errors on the page
  545. lesson 92. Custom validator
  546. Main parts in JSR 303 annotations
  547. Custom annotation @UserInfo
  548. Configuration properties validation
  549. Validation groups
  550. lesson 93. @ControllerAdvice & @ExceptionHandler
  551. @ExceptionHandler annotation
  552. @ControllerAdvice annotation
  553. Класс ResponseEntityExceptionHandler
  554. 18. REST

  555. lesson 94. REST. Введение
  556. Проблемы Controller API
  557. REST API
  558. REST API Usages
  559. lesson 95. REST. Практика
  560. @ResponseBody & findAll method
  561. findById method
  562. @RequestBody & create method
  563. update method
  564. delete method
  565. @RestController
  566. @RestControllerAdvice
  567. lesson 96. Swagger. API docs
  568. Rest clients
  569. Подключение springdoc
  570. Сгенерированная документация для Rest Controllers
  571. Swagger ui
  572. Swagger annotations
  573. lesson 97. Upload image
  574. Добавление новой колонки image в таблице users
  575. Создание ImageService
  576. upload images from html pages. MultipartFile
  577. lesson 98. Get image
  578. Реализация функционала на уровне service
  579. Отображение картинки на html странице
  580. Реализация функционала на уровне rest controller
  581. Отображение отсутствующей картинки
  582. Класс ResponseEntity
  583. 19. Security Starter

  584. lesson 99. Security Starter. Введение
  585. Понятия Аутентификация и Авторизация
  586. Servlet Filters mechanism
  587. Spring Servlet Filters mechanism
  588. Подключение Spring Security Starter
  589. lesson 100. Authentication Architecture
  590. Spring Security Model
  591. Spring Security Authentication Logic
  592. Debug Security filters (default behaviour)
  593. lesson 101. DaoAuthenticationProvider
  594. DaoAuthenticationProvider source code
  595. Add column password into users table
  596. Update entity & enum
  597. Implement UserDetailsService
  598. Тестирование функциональности
  599. lesson 102. Form Login
  600. Default login page source code
  601. Custom login page
  602. Customise SecurityFilterChain
  603. Тестирование функицонала
  604. Class UsernamePasswordAuthenticationFilter
  605. lesson 103. HTTP Basic Authentication
  606. HTTP Basic Authentication principle
  607. HTTP Basic encoder & decoder
  608. Customise SecurityFilterChain to support HTTP Basic
  609. BasicAuthenticationFilter source code
  610. lesson 104. PasswordEncoder
  611. Зачем шифровать пароли
  612. List of password encoders
  613. Implement password encode/decode in the app
  614. lesson 105. Logout
  615. LogoutFilter source code
  616. Customise logout in SecurityFilterChain
  617. Add button Logout on pages
  618. lesson 106. Authorization Architecture
  619. AuthorizationFilter source code and logic
  620. AuthorizationFilter implementations
  621. Customise authorizeHttpRequests in SecurityFilterChain
  622. lesson 107. Method Security
  623. @PreAuthorize annotation
  624. @PostAuthorize annotation
  625. @EnableMethodSecurity annotation
  626. @Secured annotation
  627. Service layer authentication
  628. @PreFilter & @PostFilter annotations
  629. lesson 108. Access to authenticated user
  630. Get current user via SecurityContextHolder
  631. @CurrentSecutiryContext annotation
  632. @AuthenticationPrincipal annotation
  633. Thymeleaf and Spring Security integration
  634. lesson 109. CSRF Filter
  635. Cross-Site Request Forgery
  636. How to solve CSRF problem
  637. Synchronizer Token Pattern
  638. When to use CSRF protection
  639. CsrfFilter source code
  640. How to work with CSRF token
  641. Class CsrfRequestDataValueProcessor
  642. lesson 110. Security Testing
  643. Исправление существующих тестов
  644. spring-security-test dependency
    1. Manually define a user in tests
    1. @WithMockUser annotation
    1. SecurityMockMvcRequestPostProcessor
  645. lesson 111. OAuth 2.0. Теория
  646. Текущий Authentication функционал в приложении
  647. Что такое OAuth 2
  648. Как внедрить OAuth 2 в приложении
  649. OAuth 2 flow types
  650. OAuth 2 Authorization Code Flow
  651. OAuth 2 Implicit Flow
  652. OpenID Connect (OIDC)
  653. lesson 112. OAuth 2.0. Практика
  654. Create a new project in GCP
  655. Configure OAuth 2 in the project
  656. Configure Login Page
  657. lesson 113. OAuth 2.0. Authentication Principle
  658. Add UserInfoEndpoint config in SecurityFilterChain
  659. Create oidcUserService
  660. lesson 114. JWT. JSON Web Token
  661. How to extract info from JWT
  662. JWT header
  663. JWT payload
  664. JWT signature
  665. Code Book
  666. lesson 115. Swagger Authorization
  667. 3 options to pass authorization in Swagger
  668. springdoc properties to support OAuth 2
  669. @SecurityScheme configuration
  670. 20. i18n & l10n

  671. lesson 116. i18n. MessageSource
  672. spring.messages properties
  673. IntelliJ IDEA UTF-8 settings
  674. Creating MessageRestController
  675. lesson 117. i18n. Thymeleaf
  676. Login page i18n
  677. How to change the language
  678. LocalChangeInterceptor bean
  679. LocaleResolver bean
  680. 21. AOP Starter

  681. lesson 118. AOP Starter. Введение
  682. Усложнение кода второстепенной логикой
  683. Crosscutting concerns
  684. AOP terminology
  685. AOP approaches
  686. lesson 119. AOP. Pointcut
  687. spring-boot-starter-aop dependency
  688. AspectJ annotations
  689. @Pointcut
  690. @within
  691. within
  692. this & target
  693. @annotation
  694. args
  695. @args
  696. bean
  697. execution
  698. lesson 120. AOP. @Before Advice
  699. @Before annotation
  700. CglibAopProxy
  701. Proxy interceptors
  702. Spring AOP diagram
  703. AopAutoConfiguration
  704. lesson 121. AOP. JoinPoint. Params
  705. JoinPoint object
  706. Get access to proxy data from advice method params
  707. argNames
  708. lesson 122. AOP. @After Advices
  709. All types of advice
  710. @AfterReturning annotation
  711. @AfterThrowing annotation
  712. @After annotation
  713. lesson 123. AOP. @Around Advice
  714. TransactionInterceptor
  715. @Around annotation
  716. lesson 124. AOP. Best Practices
    1. Combine different Pointcut types
    1. Move common Pointcuts to separate Aspect
    1. Don t use @Around advice everywhere
    1. Separate Pointcuts by business logic
  717. Aspects order
  718. 22. Заключение

  719. lesson 125. Custom Spring Boot Starter
  720. Create a new Gradle module
  721. Define starter properties
  722. Create Autoconfiguration
  723. File META-INF/spring.factories
  724. Move Aspects from the old to the new module
  725. How to use newly created starter
  726. spring-boot-configuration-processor
  727. lesson 126. Заключение. Путь развития
  728. Spring Framework Documentation
  729. List of all main Spring Boot Starters
  730. Java Road Map