какие блины будут использоваться для настройки приложения spring
Внешние данные конфигурации в Spring
Введение
Ситуация
Большинство наших приложений зависят от внешних сервисов, например серверов баз данных, SMS-шлюзов и систем наподобие PayPal. Эти сервисы могут существовать более чем в одной среде, то есть в средах разработки и эксплуатации. Если мы хотим подключиться к эксплуатационной среде, мы должны сначала пройти через среду разработки. Таким образом, во время создания приложений нам приходится переключаться между средами. Это связано с тем, что у каждой среды своя уникальная конфигурация со своими параметрами подключения и прочими значениями.
Проблема
Предположим, что мы разрабатываем платежный сервис, который подключается к внешнему платежному провайдеру. Значения коммерческого счета в эксплуатационной среде и среде разработки не совпадают. То есть при каждом переключении среды нам приходится изменять значения и компилировать код заново, а это неэффективно.
Решение
Лучшее решение этой проблемы — вывод данных конфигурации приложения во внешний источник. Нам не нужно будет каждый раз заново компилировать код при переключении среды. Мы сможем переопределить значения параметров конфигурации, затратив меньше усилий. Наше приложение Spring сможет считывать значения коммерческого счета из внешних источников, например переменных среды, аргументов командной строки и файлов свойств.
Вывод данных конфигурации во внешний источник
Источники свойств
Существуют различные способы вывода данных конфигурации приложения Spring во внешний источник. Для задания свойств приложения мы можем использовать переменные среды, файлы свойств (например, в формате YAML или с расширением *.properties) и аргументы командной строки. Мы также можем хранить файлы свойств в произвольных местах и сообщать приложению Spring, где их искать.
Файлы свойств
По умолчанию приложение Spring загружает свойства из файлов application.properties или application.yml из перечисленных ниже источников в порядке приоритета (то есть вышестоящий файл свойств переопределяет файлы из источников нижнего уровня) и добавляет их в среду:
подкаталог конфигурации текущего каталога;
пакет конфигураций в параметре classpath;
корневой каталог classpath.
Пользовательское место хранения
Примечание. При указании расположения каталога необходимо убедиться, что после значения spring.config.location стоит символ / (например, spring.config.location=classpath:/config/ ) и что задано имя файла конфигурации по умолчанию. Также с помощью ключа свойств spring.config.additional-location можно указать дополнительные каталоги, поиск в которых будет проводиться перед поиском в местоположениях по умолчанию.
Форматы файлов
application.properties
application.yml
Множество профилей
1. Файл YAML
application.yml
application-development.properties
application-production.properties
При наличии свойств, общих для профилей разработки и эксплуатации, нам может потребоваться файл application.properties, заданный по умолчанию.
application.properties
Образец данных конфигурации в приведенных выше фрагментах кода устанавливает профиль разработки в качестве активного профиля. Поэтому при запуске приложения значения свойств, определенные в этом профиле, будут иметь приоритет над эксплуатационным профилем. Но не стоит забывать, что настройки профиля можно также переопределить с помощью аргументов командной строки.
Вы можете узнать больше о профилях Spring в этой статье.
Читаемость
application.yml
application.properties
Тестовые примеры для проверки сопоставлений можно найти в тестовых пакетах с примером кода из данной статьи.
Аргументы командной строки
Когда мы вводим аргумент командной строки, приложение Spring преобразует его в свойство и добавляет в Spring Environment. С помощью этих аргументов можно сконфигурировать параметры приложения. К примеру, следующие аргументы командной строки переопределят порт сервера приложения, заданный любым другим источником свойств. При запуске приложения командой Maven или Java мы все равно получим тот же результат.
Команда Maven:
Команда JVM:
Также можно вводить несколько аргументов одновременно. Дополним приведенный выше пример еще одним свойством — портом сервера, как показано ниже.
Команда Maven (через пробел):
Команда JVM:
Переменные среды
Откроем терминал и выполним следующую команду. Она устанавливает переменные среды приложения, переопределяя настройки подключения.
После этого запустим наше приложение:
Результат
Проверив журнал, мы заметим, что адреса подключения в профиле разработки были переопределены, а значения в файле JSON, который мы передали через переменную среды, были в приоритете.
Передача свойств
Этот метод актуален при наличии небольшого количества свойств, но он не рекомендуется, если свойств много. Представьте, если в коммерческом счете более двадцати свойств, нам придется указывать аннотацию @Value двадцать раз. Приведенный ниже фрагмент кода показывает, как можно использовать эту аннотацию для внедрения значения свойства в приложение.
Важно убедиться, что имя свойства @Value совпадает с именем, указанным в источниках свойств.
При наличии нескольких свойств мы можем сгруппировать их и сопоставить с классом POJO. Таким образом, мы получим структурированный и типобезопасный объект, который сможем внедрить в любое место в нашем приложении. Поэтому вместо использования аннотации @Value значения свойств можно получить с помощью метода чтения значения класса POJO.
application.yml
Важно отметить, что аннотация @ConfigurationProperties также позволяет нам сопоставлять списки и карты, как показано ниже:
Порядок приоритета данных конфигурации
В приложении Spring Boot может быть несколько источников свойств. Поэтому важно знать, какой источник свойства имеет наивысший приоритет. Например, если конфигурация нашего приложения находится в файле application.yml и во время выполнения приложения мы решаем передать аргументы командной строки, тогда значения свойств в файле application.yml будут переопределены значениями аргументов командной строки.
В Spring Boot 2.2.x используется приведенный ниже порядок источников свойств. Источник свойств, расположенный выше в списке, имеет приоритет над источниками под ним.
Аннотации @TestPropertySource в ваших тестах.
Атрибут свойств в ваших тестах. Он доступен в @SpringBootTest и тестовых аннотациях для проверки работы определенного фрагмента вашего приложения.
Аргументы командной строки.
Свойства из SPRING_APPLICATION_JSON (строковый JSON в переменной среды или системном свойстве).
Переменные среды ОС.
Свойства по умолчанию (заданные настройкой SpringApplication.setDefaultProperties ).
Заключение
Подготовка к Spring Professional Certification. Контейнер, IoC, бины
Доброго времени суток, Хабр.
Сегодня я решил представить вам перевод цикла статей для подготовки к Spring Professional Certification.
Это перевод только первой статьи, если он зайдет аудитории, я продолжу выпуск переводов.
Внедрение зависимостей — это специальный паттерн, который уменьшает связь между Spring компонентами. Таким образом, при применении DI, ваш код становится чище, проще, его становится легче понять и тестировать.
Согласно паттерну DI, создание объектов для зависимостей переходит на фабрику или отдается третьей стороне. Это означает, что мы можем сосредоточиться на использовании этих объектов вместо их создания.
В Spring Framework интерфейс org.springframework.factory.BeanFactory предоставляет фабрику для бинов, которая в то же время является IoC контейнером приложения. Управление бинами основано на конфигурации(java или xml).
Интерфейс org.springframework.context.ApplicationContext — это обертка над bean factory, предоставляющая некоторые дополнительные возможности, например AOP, транзакции, безопасность, i18n, и т.п.
Основа Spring Framework — контейнер, и наши объекты «живут» в этом контейнере.
Контейнер обычно создает множество объектов на основе их конфигураций и управляет их жизненным циклом от создания объекта до уничтожения.
Контейнер — это объект, реализующий интерфейс ApplicationContext.
Spring обеспечивает несколько разновидностей контекста.
Есть несколько основных реализаций интерфейса ApplicationContext:
Примеры создания контекста:
Если вы используете JUnit 5, то вам нужно указать 2 аннотации:
Если это не веб-приложение, то есть 2 способа:
В Spring Boot приложении:
Этот класс поместит в контейнер экземпляр класса DataSource. Позднее его можно будет использовать при доступе к базе данных.
Component scanning(сканирование компонентов) — Spring автоматически обнаруживает бины, которые будут находиться в контейнере. Это бины с аннотациями-стереотипами.
Component | Корневая аннотация, которая помечает класс как кандидат для автовнедрения |
Controller | Указывает, что класс является контроллером для отправления данных на фронт. |
@RestController | Указывает, что класс является контроллером для REST. Содержит аннотации Controller и @ResponseBody |
Service | Указывает, что класс является сервисом для выполнения бизнес-логики |
Repository | Указывает, что класс является репозиторием для работы с бд |
@Configuration | Указывает, что класс содержит Java-конфигурацию(@Bean-методы) |
Область видимости — scope, скоуп. Существует 2 области видимости по умолчанию.
Singleton | Область видимости по умолчанию. В контейнере находится всего 1 экземпляр бина |
Prototype | В контейнере может находится любое количество экземпляров бина |
И 4 области видимости в веб-приложении.
Request | Область видимости — 1 HTTP запрос. На каждый запрос создается новый бин |
Session | Область видимости — 1 сессия. На каждую сессию создается новый бин |
Application | Область видимости — жизненный цикл ServletContext |
WebSocket | Область видимости — жизненный цикл WebSocket |
Область видимости указывается с помощью аннотации @Scope на @Bean методах.
Prototype Scope не потокбезопасный, т.к. он не гарантирует что один и тот же экземпляр будет вызываться только в 1 потоке.
Singleton Scope же наоборот потокобезопасный.
Singleton-бины обычно создаются сразу при сканировании.
Prototype-бины обычно создаются только после запроса.
Singleton bean можно внедрять в любой другой бин.
Prototype может быть зависимостью для любого бина.
Внедрять можно только singleton или prototype.
Для того чтобы использовать кастомный BFPP. Вы можете переопределить механизм получения данных из метафайлов.
Есть 3 варианта для создания таких методов:
Ниже перечислены типы DI, которые могут быть использованы в вашем приложении:
DI через конструктор считается самым лучшим способом, т.к. для него не надо использовать рефлексию, а также он не имеет недостатков DI через сеттер.
DI через поле не рекомендуется использовать, т.к. для этого применяется рефлексия, снижающая производительность.
DI через конструктор может приводить к циклическим зависимостям. Чтобы этого избежать, можно использовать ленивую инициализацию бинов или DI через сеттер.
Контейнер обрабатывает DI с помощью AutowiredAnnotationBeanPostProcessor. В связи с этим, аннотация не может быть использована ни в одном BeanFactoryPP или BeanPP.
Если внедряемый объект массив, коллекция, или map с дженериком, то Spring внедрит все бины подходящие по типу в этот массив(или другую структуру данных). В случае с map ключом будет имя бина.
Вы можете использовать разные типы внедрения:
Spring предоставляет аннотацию Qualifier, чтобы преодолеть проблему неоднозначности при DI.
Если в контейнере есть несколько бинов одного типа(SomeClass), то контейнер внедрит именно тот бин, над @Bean-методом которого стоит соответствующий квалификатор. Также можно не ставить квалификатор на метод, а использовать имя бина в качестве параметра квалификатора.
Имя бина можно можно указать через параметр аннотации Bean, а по умолчанию это имя фабричного метода.
Прокси это специальный объект, который имеет такие же публичные методы как и бин, но у которого есть дополнительная функциональность.
Два вида прокси:
Если в контейнере нет экземпляра бина, то вызывается @Bean-метод. Если экземпляр бина есть, то возвращается уже созданный бин.
В эту переменную будет внедрена строка, например из property или из view.
Как обычно, просьба присылать правки или найденные ошибки в личку.
Настройка приложения — Spring Configuration Metadata
Для создания настройки приложения с использованием ConfigurationProperties, можно начать с класса свойств. В нем собственно указаны свойства, компоненты системы которые хотим настраивать.
В классе prefix=«demo» будет использоваться в application.properties, как префикс к свойству.
Тут я объявил два spring бина
В классе AppProperties я указал ссылку на некоторый доступный сервис приложения, его я буду менять в application.properties, у меня будет две его реализации и я буду подключать одну из них в application.properties.
При попытке ввести неверный тип IDE сообщит
Даже если оставить как есть и попытаться стартовать приложение, то будет вполне внятная ошибка
Добавление дополнительных метаданных
В ней можно будет перечислить например допустимые значения для demo.vehicle
В поле «name» указано св-во «demo.vehicle», а в «values» список допустимых значений. Теперь если сделать rebuild проекта и перейти в файл application.properties, то при вводе demo.vehicle получу список допустимых значений
При вводе отличного от предложенного, но того же типа, редактор подсветит, но приложение в этом случае будет стартовать, так как это не строгое ограничение, а всего лишь подсказка.
Пример Providers для DemoService:
После чего в application.properties для параметра demo.service будет доступен выбор двух сервисов, можно посмотреть их описание (description из определения).
Теперь удобно выбирать нужный сервис, менять функционал приложения. Есть один момент для объектных настроек, Spring надо немного помочь конвертировать строку которая указана в настройке, в объект. Для этого делается небольшой класс наследник от Converter.
На диаграмме классов проекта видно как эти сервисы отделены от основного приложения и доступны через AppProperties.
К полям класса AppProperties можно добавить проверки доступные в рамках JSR 303. Про это я писал здесь. Получится проверяемый, удобный файл конфигурации приложения.
Spring: вопросы к собеседованию
Этот небольшой список вопросов даст вам понимание самых важных концепций Spring, а так же поможет подготовится к собеседованию
Если вы понимаете как работает Component Scan, то вы понимаете Spring
Однако, Spring ничего не знает об этих бинах, если он не знает где искать их. То, что скажет Spring где искать эти бины и называется Component Scan. В @ComponentScan вы указываете пакеты, которые должны сканироваться.
Spring будет искать бины не только в пакетах для сканирования, но и в их подпакетах.
@SpringBootApplication определяет автоматическое сканирование пакета, где находится класс Application
Всё будет в порядке, ваш код целиком находится в указанном пакете или его подпакетах.
Однако, если необходимый вам компонент находится в другом пакете, вы должны использовать дополнительно аннотацию @ComponentScan, где перечислите все дополнительные пакеты для сканирования
@Component и @ComponentScan предназначены для разных целей
@Component помечает класс в качестве кандидата для создания Spring бина.
@ComponentScan указывает где Spring искать классы, помеченные аннотацией @Component или его производной
В классах конфигурации Spring, @Bean используется для определения компонентов с кастомной логикой.
@Bean используется в конфигурационных классах Spring. Он используется для непосредственного создания бина.
Все они определяют бины Spring. Однако между ними всё же есть разница.
@Component — универсальный компонент
@Repository — компонент, который предназначен для хранения, извлечения и поиска. Как правило, используется для работы с базами данных.
@Service — фасад для некоторой бизнес логики
Если @Component является универсальным стереотипом для любого Spring компонента, то @Service в настоящее время является его псевдонимом. Однако, в официальной документации Spring рекомендуется использовать именно @Service для бизнес логики. Вполне возможно, что в будущих версиях фреймворка, для данного стереотипа добавится дополнительная семантика, и его бины станут обладать дополнительной логикой.
web.xml — Метаданные и конфигурация любого веб-приложения, совместимого с Java EE. Java EE стандарт для веб-приложений.
servlet.xml — файл конфигурации, специфичный для Spring Framework.
Предпочитаю аннотации, если кодовая база хорошо описывается такими элементами, как @Service, @Component, @Autowired
Однако когда дело доходит до конфигурации, у меня нет каких-либо предпочтений. Я бы оставил этот вопрос команде.
@Autowired может использоваться вместе с конструкторами, сеттерами или любым другими методами. Когда Spring находит @Autowired на методе, Spring автоматически вызовет этот метод, после создания экземпляра бина. В качестве аргументов, будут подобраны подходящие объекты из контекста Spring.
Сквозная Функциональность — функциональность, которая может потребоваться вам на нескольких различных уровнях — логирование, управление производительностью, безопасность и т.д.
АОП — один из подходов к реализации данной проблемы
IOC — инверсия управления. Вместо ручного внедрения зависимостей, фреймворк забирает ответственность за это.
ApplicationContext — реализация IOC спрингом.
Bean Factory — это базовая версия IOC контейнера
Application Context также включает дополнительные функции, которые обычно нужны для разработки корпоративных приложений
classPathXmlApplicationContext — если вы хотите инициализировать контекст Spring при помощи xml
annotationConfigApplicationContext — если вы хотите инициализировать контекст Spring при помощи конфигурационного класса java
Если все бины имеют одинаковый приоритет, мы всегда будем использовать @Qualifier
На мой взгляд это Functional Web Framework, Kotlin и и поддержка реактивного программирования.
Web Container и EJB Containers являются частью приложения/веб-сервера, таких как Tomcat, Websphere, Weblogic. Они добавляют свою дополнительную функциональность к ним. Java EE определяет контракт для веб-приложений, эти контейнеры являются реализацией этих контрактов.
Spring контейнер может являться частью любого приложения, которое вы делаете на java. Spring может работать внутри веб-контейнера, ejb контейнера или даже без них.
Тогда в application.properties добавим свойство
application.greeting: real
Воспользуемся данным решением:
Spring 5.0 и Spring Boot 2.0 поддерживают Java 8 и более поздней версии.
@RestController = @Controller + @ResponseBody
@RestController превращает помеченный класс в Spring-бин. Этот бин для конвертации входящих/исходящих данных использует Jackson message converter. Как правило целевые данные представлены в json или xml.
ResponseEntity необходим, только если мы хотим кастомизировать ответ, добавив к нему статус ответа. Во всех остальных случаях будем использовать @ResponseBody.
Стандартные HTTP коды статусов ответов, которые можно использовать.
200 — SUCCESS
201 — CREATED
404 — RESOURCE NOT FOUND
400 — BAD REQUEST
401 — UNAUTHORIZED
500 — SERVER ERROR
Для @ResponseBody единственные состояния статуса это SUCCESS(200), если всё ок и SERVER ERROR(500), если произошла какая-либо ошибка.
Допустим мы что-то создали и хотим отправить статус CREATED(201). В этом случае мы используем ResponseEntity.
Концептуально всё просто, фильтры сервлетов могут перехватывать только HTTPServlets. Listeners могут перехватывать специфические события. Как перехватить события которые относятся ни к тем не другим?
Фильтры и перехватчики делают по сути одно и тоже: они перехватывают какое-то событие, и делают что-то до или после.
Java EE использует термин Filter, Spring называет их Interceptors.
Именно здесь AOP используется в полную силу, благодаря чему возможно перехватывание вызовов любых объектов
Model — интерфейс, ModelMap его реализация..
ModelAndView является контейнером для пары, как ModelMap и View.
Обычно я люблю использовать ModelAndView. Однако есть так же способ когда мы задаем необходимые атрибуты в ModelMap, и возвращаем название View обычной строкой из метода контроллера.
Метод addAttribute отделяет нас от работы с базовой структурой hashmap. По сути addAttribute это обертка над put, где делается дополнительная проверка на null. Метод addAttribute в отличии от put возвращает modelmap.
model.addAttribute(“attribute1”,”value1”).addAttribute(“attribute2”,”value2”);
Нам это может понадобиться, если мы, например, захотим взять некоторое значение с HTML страницы и сохранить его в БД. Для этого нам надо это значение переместить в контроллер Спринга.
Если мы будем использовать Spring MVC form tags, Spring автоматически свяжет переменные на HTML странице с Бином Спринга.
Если мне придется с этим работать, я обязательно буду смотреть официальную документацию Spring MVC Form Tags.
Hibernate Validator никак не связан с БД. Это просто библиотека для валидации.
Hibernate Validator версии 5.x является эталонной реализацией Bean Validation 1.1
Так же если взглянуть по адресу http://beanvalidation.org/2.0, то Hibernate Validator является единственным, который сертифицирован.
Расположение статических ресурсов можно настроить. В документации Spring Boot рекомендуется использовать /static, или /public, или /resources, или /META-INF/resources
В случае GET запроса передаваемые параметры являются частью url, и все маршрутизаторы, через которые пройдет наш GET запрос, смогут их прочитать.
В случае POST запроса передаваемые параметры являются частью тела запроса. При использовании HTTPs, тело запроса шифруется. Следовательно, использование POST запросов является более безопасным
Пример:
http://localhost:8080/login?name=Ranga&name=Ravi&name=Sathish
Да, можно принять все значения, используя массив в методе контроллера
Хочу поблагодарить пользователя хабра jd2050, за помощь с переводом.
Обратная сторона Spring
Неделя Spring на Хабре, судя по всему, открыта. Хочется сказать спасибо переводчику и комментаторам статьи «Почему я ненавижу Spring», которая не смотря на сильный негативный посыл в названии вызвала ряд интересных дискуссий, а так же тем, кто отреагировал на мою прошлую статью Как писать на Spring в 2017. Во многом благодаря комментариям к прошлой статье и появилась эта.
В этот раз мы погрузимся в пучины Spring фреймворка, разоблачим его магию, посмотрим как базовое веб приложение выглядит изнутри, и разберемся, какую-же задачу и как решает Spring Boot.
В комментариях к предыдущей статье несколько человек очень справедливо указали, что пример Hello World-а на Spring все же не очень показателен. Spring, особенно с использованием Spring Boot, дает ощущение простоты и всемогущества, но непонимание основ и внутренностей фреймворка ведет к большой опасности получить стектрейсом по логу. Что ж, чтобы немного развеять ощущение полной магии происходящего, сегодня мы возьмем приложение из предыдущей статьи и разберем, как и что происходит внутри фреймворка и от каких проблем нас отгораживает Boot. Целевая аудитория все же начинающие разработчики, но с некоторым опытом и базовыми знаниями Java и Spring. Хотя, возможно, и опытным пользователям Spring будет интересно освежить знания того, что происходит под капотом.
Ключевые понятия
Начнем срывать покровы с самых базовых понятий Spring. Бин (bean) — это не что иное, как самый обычный объект. Разница лишь в том, что бинами принято называть те объекты, которые управляются Spring-ом и живут внутри его DI-контейнера. Бином является почти все в Spring — сервисы, контроллеры, репозитории, по сути все приложение состоит из набора бинов. Их можно регистрировать, получать в качестве зависимостей, проксировать, мокать и т.п.
DI контейнер
Ключевой и фундаментальный механизм Spring. Внешне очень простой, но внутри он предоставляет очень много механизмов для тонкой настройки зависимостей. По сути, любое приложение Спринг — это набор бинов, связанных вместе через DI контейнер.
Еще один очень важный момент, который многие упускают при обсуждении DI контейнера, это то, что использование инъекции зависимостей не подразумевает создания интерфейсов для каждого компонента. Это очень простая мысль, но я много раз видел, что из-за своей простоты она не всегда очевидна. Более того, создание интерфейса, если у него лишь одна реализация — считается плохой практикой. Т.е. классы вполне могут сами по себе быть объектами DI. Более того, отсутствие интерфейса даже не мешает их мокать в тестах, т.к. Mockito, например, вполне умеет мокать классы.
Контекст
Конфигурация
Итак, если приложение — это набор бинов, чтобы оно заработало нам нужно этот набор описать.
Конфигурация — это просто описание доступных бинов. Spring дает несколько вариантов, как можно описать набор бинов, которые сформируют приложение. Исторический вариант — это через набор xml файлов. В наши дни ему на смену пришли Java аннотации. Spring Boot построен на аннтациях чуть более, чем полностью и большинство современных библиотек в принципе тоже можно сконфигурить через аннотации. В третьем своем поколении, конфигурация бинов пришла к подходу функциональной регистрации (functional bean registration), которая станет одной из важных новых фич готовящегося к выходу Spring 5.
Типичный класс конфигурации может, выглядеть, например так:
Эта конфигурация определяет два бина, причем второй зависит от первого. И здесь в игру вступит Spring – когда мы просим предоставить инстанс PaymentProvider — Spring найдет его в контексте и предоставит нам.
Конфигурацию не обязательно описывать в одном огромном файле, можно разбить на несколько и объединять их с помощью @Import аннотаций.
Сканирование компонентов
Резюме
Spring Boot
Теперь переходим к следующей части. Допустим, нам надо сконфигурить подключение к MySQL базе данных. Если мы хотим использовать Spring Data JPA с Hibernate в качестве провайдера, нам потребуется сконфигурировать несколько бинов — EntityManagerFactory (основной класс JPA), DataSource для подключения непосредственно к базе через JDBC драйвер и т.п. Но с другой стороны, если мы это делаем каждый раз и, по сути, делаем одно и то же — почему бы это не автоматизировать? Скажем, если мы указали строку подключения к базе и добавили зависимость на MySQL драйвер — почему бы чему-то автоматически не создать все нужные бины для работы с MySQL? Именно это и делает Spring Boot. По сути, Spring Boot это просто набор классов конфигурации, которые создают нужные бины в контексте. Точно так же их можно создать руками, просто Boot это автоматизирует.
Автоконфигурация
Важное понятие Spring Boot это автоконфигурация. По сути, это просто набор конфигурационных классов, которые создают и регистрируют определенные бины в приложении. По большому счету, даже сам Embedded Servlet Container — это просто еще один бин, который можно сконфигурировать! Пара важных моментов, которые важно знать об автоконфигурации:
Условия и порядок регистрации бинов
Логика при регистрации бинов управляется набором @ConditionalOn* аннотаций. Можно указать, чтобы бин создавался при наличии класса в classpath ( @ConditionalOnClass ), наличии существующего бина ( @ConditionalOnBean ), отсуствии бина ( @ConditionalOnMissingBean ) и т.п.
Spring Boot активно использует эти аннотации чтобы оставаться как можно более незаметным и не перекрывать пользовательские конфигурации.
Погружение в Hello World
Теперь, имея в запасе базовые теоретические знания, разберем что же происходит при запуске приложения.
Итак, наше приложение включает такой код:
Давайте разберем что здесь происходит по шагам.
Класс DemoApplication
Т.е. наличие @SpringBootApplication включает сканирование компонентов, автоконфигурацию и показывает разным компонентам Spring (например, интеграционным тестам), что это Spring Boot приложение
SpringApplication.run()
То получим точно такое же работающее приложение, т.к. класс AnnotationConfigEmbeddedWebApplicationContext найдет в контексте бин типа EmbeddedServletContainerFactory и через него создаст и запустит встроенный контейнер. Обратите внимание, что все это работает в рамках общего DI контейнера, то есть этот класс можно реализовать самим.
@EnableAutoConfiguration
Эта аннотация включает автоконфигурацию. И здесь, пожалуй, ключевой момент в развенчании магии Spring. Вот как объявлена эта аннотация:
Т.е. это самый обычный импорт конфигурации, про который мы говорили выше. Класс же EnableAutoConfigurationImportSelector (и его преемник в Spring Boot 1.5+ — AutoConfigurationImportSelector ) это просто конфигурация, которая добавит несколько бинов в контекст. Однако, у этого класса есть одна тонкость — он не объявляет бины сам, а использует так называемые фабрики.
Класс EnableAutoConfigurationImportSelector смотрит в файл spring.factories и загружает оттуда список значений, которые являются именами классов (авто)конфигураций, которые Spring Boot импортирует.
Т.е. аннотация @EnableAutoConfiguration просто импортирует все перечисленные конфигурации, чтобы предоставить нужные бины в контекст приложения.
По сути, ее можно заменить на ручной импорт нужных конфигураций:
Однако, особенность в том, что Spring Boot пытается применить все конфигурации (а их около сотни). Я думаю, у внимательного читателя уже появилась пара вопросов, которые стоит прояснить.
«Но это же медленно!». И да, и нет — под рукой нет точных цифр, но сам по себе процесс автоконфигурации очень быстрый (порядка сотни миллисекунд на абстрактной машине в вакууме)
Краткое резюме
В основе «магии» Spring Boot нет ничего магического, он использует совершенно базовые понятия из Spring Framework. В кратком виде процесс можно описать так:
Это может выглядеть сложно, но по большей части разработчикам приложений влезать во внутренности автоконфигурации не нужно, если речь не идет о поддержке авто-конфигурации для своей библиотеке.
Диагностика
Auto-configuration report
В ответ Spring выдаст детальный Auto-configuration report:
Строчка в Positive / Negative matches будет для каждой примененной автоконфигурации, более того, Boot сообщит, почему тот или иной бин был создан (т.е. укажет, какие из условий регистрации были выполнены).
Actuator
Spring Boot Actuator это мощный инструмент диагностики работающего приложения, который умеет давать много полезной аналитики (более того, набор этих метрик можно легко расширять из приложения).
Резюме
Spring все же остается большим и не самым простым фреймворком, но это цена высокоуровневых абстракций, которые он предоставляет. И хотя знать все тонкости работы фреймворка в ежедневной разработке не нужно, знать, как он работает изнутри, все же, полезно. Надеюсь, что эта статья помогла понять важность и ценность Spring именно как экосистемы и убрала немного «магичности» в происходящем, особенно при использовании Spring Boot. Мой совет — не бойтесь углубляться в недра фреймворка, читайте исходники и документацию, благо они у Spring-a почти эталонные, на мой взгляд.