В папке DOC sql-скрипты и др. полезные файлы.
Док. (ссылки) для изучения:
- Spring Boot Reference Documentation ;
- Spring Framework 6.1.5 Documentation ;
- Spring Framework 3.2.x Reference Documentation ;
- Getting Started Guides ;
- Developing with Spring Boot ;
- Документация по Spring Boot (архив) ;
Для начала проведем предварительную подготовку (подгрузим зависимости в build.gradle):
/*
Плагин Spring Boot добавляет необходимые задачи в Gradle
и имеет обширную взаимосвязь с другими plugin-ами.
*/
id 'org.springframework.boot' version '3.1.3'
/*
Менеджер зависимостей позволяет решать проблемы несовместимости
различных версий и модулей Spring-а
*/
id "io.spring.dependency-management" version '1.0.11.RELEASE'
/* Подключим Lombok */
id "io.freefair.lombok" version "8.3"
/*
Автоматически Gradle создал тестовую зависимость на Junit5, мы можем
использовать как Junit4, так и TestNG
*/
test {
useJUnitPlatform()
}
/*
Подключим Spring Boot Starter он включает поддержку
авто-конфигурации, логирование и YAML
*/
implementation 'org.springframework.boot:spring-boot-starter'
/* Подключим блок для работы с БД (Spring Boot Starter Data Jpa) */
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
/* Для работы с PostgreSQL подключим и его зависимости */
implementation 'org.postgresql:postgresql'
!!! НЕ ЗАБЫВАЕМ !!! У нас есть классы (см. ConnectionPool.java и комментарии), где мы пытаемся внедрить параметры в
поля через аннотации, с использованием аннатационного же конструктора @RequiredArgsConstructor. Фокус не пройдет без
создания и настройки файла конфигурации: lombok.config - 'контекст' просто завалится.
Либо все делаем руками от начала и до конца, либо помним какие вспомогательные средства используем и какие их особенности
могут повлиять на работу приложения.
implementation 'org.springframework.data:spring-data-envers'
/* Подключим миграционный фреймворк Liquibase */
implementation 'org.liquibase:liquibase-core'
/* Подключаем Wed - Starter */
implementation 'org.springframework.boot:spring-boot-starter-web'
/* Подключим Thymeleaf */
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
/* Подключим валидацию */
implementation 'org.springframework.boot:spring-boot-starter-validation'
/* Подключим стартер безопаности */
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.apache.tomcat.embed:tomcat-embed-jasper'
/* Зависимости необходимые при тестировании Spring Boot приложения */
testImplementation 'org.springframework.boot:spring-boot-starter-test'
/* Зависимости позволяет тестировать приложение с подключенным Spring Security */
testImplementation 'org.springframework.security:spring-security-test'
testImplementation "org.testcontainers:postgresql:${versions.testcontainers}"
В качестве стартера мы возьмем и вынесем наш блок АОП с функционалом логирования:
-
Шаг 1. - Создадим отдельный модуль Gradle - myfirst-logging-spring-boot-starter. У этого отдельного модуля свой build.gradle (со своими зависимостями):
plugins { id 'java' } group 'spring.oldboy' version '1.0-SNAPSHOT' repositories { mavenCentral() } dependencies { /* Добавляем AOP зависимость поскольку мы ее используем */ implementation 'org.springframework.boot:spring-boot-starter-aop:2.6.2' /* Добавляем JPA стартер т.к. мы используем транзакции и работу с БД */ implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.6.2' /* Добавляем зависимости от Lombok т.к. мы используем его аннотации */ compileOnly 'org.projectlombok:lombok:1.18.22' annotationProcessor 'org.projectlombok:lombok:1.18.22' /* Позволяет генерировать файл подсказок JSON для конфигурационых файлов, чтобы получлось сгенерировать качественные подсказки, нужно делать качественные комментарии в формате Java Doc см. LoggingProperties.java */ annotationProcessor "org.springframework.boot:spring-boot-configuration-processor:2.6.2" /* Добавляем тестовую зависимость, т.к. любой код (в т.ч. и starter) нужно тестировать */ testImplementation 'org.springframework.boot:spring-boot-starter-test:2.6.2' } test { useJUnitPlatform() }
Либо мы можем использовать Spring Dependency Management, как и в основном проекте, так же см. описание варианта конфигурации в DOC/HandMade_SB_Starter/BuildingSpringBootStarter.txt
У нашего отдельного модуля свой src пакет в который мы перенесем нашу АОП логику из прошлого проекта, чтобы выделить в отдельный стартер.
-
Шаг 2. - Создаем файл свойств (настроек стартера) см. подробнее статьи из папки DOC. Выделяем отдельно конфигурационную папку для нашего модуля, куда будут помещаться все файлы связанные с настройкой нашего starter-a: java/spring/oldboy/logging/config. В этой папке создаем файл - LoggingProperties.java см. код и комментарии.
-
Шаг 3. - Создадим файл авто-конфигурации для нашего Custom Starter-a, в той же папке config - LoggingAutoConfiguration.java, именно он будет запускаться первым для конфигурации всего стартера. Он сразу помечается аннотацией:
-
@Configuration - Указывает, что класс объявляет один или несколько методов @Bean и может обрабатываться контейнером Spring для создания определений компонентов и запросов на обслуживание для этих компонентов во время выполнения;
-
EnableConfigurationProperties - Включает поддержку аннотированных bean-компонентов @ConfigurationProperties. Bean @ConfigurationProperties можно зарегистрировать стандартным способом (например, с помощью методов @Bean) или, для удобства, указать непосредственно в этой аннотации см. LoggingAutoConfiguration.java
-
@ConditionalOnClass - @ConditionOn... условие, которое соответствует только тогда, когда указанные классы находятся в пути к классам. Значение класса можно безопасно указать в классах @Configuration, поскольку метаданные аннотации анализируются с помощью ASM перед загрузкой класса. Если ссылку на класс нельзя использовать, можно использовать атрибут строки имени.
-
@ConditionalOnProperty (см. на GitHub) - @Conditional, который проверяет, имеют ли указанные свойства определенное значение. По умолчанию свойства должны присутствовать в среде и не быть равными false. Атрибуты havingValue() и matchIfMissing() допускают дальнейшую настройку. Атрибут havingValue() можно использовать для указания значения, которое должно иметь свойство. Если свойство вообще не содержится в среде, используется атрибут matchIfMissing(). По умолчанию отсутствующие атрибуты не совпадают. Это условие нельзя надежно использовать для сопоставления свойств коллекции. Для таких случаев лучше использовать собственное условие.
В нашем случае, если в качестве параметра аннотации @ConditionalOnClass() используется LoggingProperties.class, это значит, что некто пытается использовать наш starter для своих нужд.
Так же мы через параметры преданные в эту аннотацию @ConditionalOnProperty() устанавливаем поле 'enabled' для LoggingProperties.java в - 'true', иначе наш стартер не будет запущен.
Т.е. чтобы подключился LoggingAutoConfiguration.java, необходимо соблюдение двух условий переданных в @ConditionalOnClass и в @ConditionalOnProperty.
-
-
Шаг 4. - Нам необходимо как-то просканировать LoggingAutoConfiguration.java, чтобы увидеть аннотацию @Configuration (чтобы Spring приложение увидело эту аннотацию и поместило класс и его bean-ы в контекст) см. DOC/HandMade_SB_Starter/BuildingSpringBootStarter.txt Для этого мы в папке 'resources' создаем папку 'META-INF' и добавляем в нее spring.factories (с содержимым):
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ spring.oldboy.logging.config.LoggingAutoConfiguration
Мы указываем путь к нашей авто-конфигурации через специальный ключ см. выше. Именно так оформляются все стартеры, с указанием пути к авто-конфигурационному файлу. Всегда (конечно можно и через @Bean, но настройка будет громоздкой) необходимо указывать точку входа в авто-настройку starter-a.
-
Шаг 5. - Создаем папку spring/oldboy/logging/aop в разделе myfirst-logging-spring-boot-starter, куда переносим всю нашу АОП (AOP) логику из старого варианта приложения, т.е. из папки spring/oldboy/aop основного приложения.
-
Шаг 6. - Все bean-ы нашего стартера должны создаваться через авто-конфигурацию, поэтому из всех наших классов перенесенных из прошлого проекта в папку 'aop', удаляем аннотацию @Component, т.е. все классы из папки 'aop' лишаются аннотаций @Component и @Order если таковые были.
-
Шаг 7. - Чтобы все перенесенные классы потерявшие аннотации @Component и @Order смогли нормально функционировать в контексте приложения (т.е. стать bean-ами), которое подключит наш starter как зависимость, мы добавляем соответствующие методы и аннотации в файл LoggingAutoConfiguration.java.
-
Шаг 8. - Необходимо подключить наш custom starter к нашему же проекту. Это делается через основной build.gradle:
implementation project(':myfirst-logging-spring-boot-starter')
И конечно добавляем его свойства в application.yml основного проекта:
app.myfirst.logging:
enabled: true
level: INFO
При настройке application.yml, обычно вводимые параметры имеют подсказки, но в данном случае они отсутствуют (либо у нас IDE community edition, либо нет нужных настроек).
- Шаг 9. - Используя средства Gradle мы build-им классы нашего starter-a и смотрим содержимое build/classes/java/main/META-INF/spring-configuration-metadata.json созданного в разделе build нашего стартера, этот файл можно перенести в папку resources/META-INF и тогда при работе с application.yml среда разработки будет генерировать подсказки (хотя эксперименты показали, что и без него таковые есть, возможно я ошибаюсь).
Запускаем приложение и видим в консоли:
...
2024-04-05T20:30:09.330+05:00 INFO 10296 --- [main] s.o.l.config.LoggingAutoConfiguration : LoggingAutoConfiguration initialized
2024-04-05T20:30:10.418+05:00 INFO 10296 --- [main] s.o.logging.config.LoggingProperties : Logging properties initialized: LoggingProperties(enabled=true, level=INFO)
2024-04-05T20:30:11.004+05:00 INFO 10296 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path ''
2024-04-05T20:30:11.018+05:00 INFO 10296 --- [main] spring.oldboy.SpringAppRunner : Started SpringAppRunner in 9.903 seconds (process running for 10.445)
Наш logger-starter запустился, инициализировались файлы конфигурации, и даже загрузились свойства из application.yml.
Если мы обратимся к нашему приложению через браузер, аутентифицируемся и сделаем пару любых стандартных действий, например, запрос user-a с конкретным ID, то в консоли увидим как сработали наши advice-ы. Но на этот раз не из приложения, а из подключенного stater-a.
Стартер позволяет выделить определенный функционал в отдельный самостоятельный программный блок для использования его в любом приложении Spring Boot.
Для этого нам необходимо:
- создать файл свойств (Properties) с уникальным префиксом (пример, LoggingProperties.java и prefix = "app.myfirst.logging")
- создать автоматическую конфигурацию custom starter-a или специальный класс (пример, LoggingAutoConfiguration.java) с которого будет начинаться конфигурация custom starter-a;
- необходимо избегать @Component аннотаций и создавать компоненты через аннотацию @Bean в файле авто-конфигурации, по возможности;
- сделайте конфигурацию starter-a настраиваемой;
- дополнить ее автоматически сгенерированными метаданными для повышения производительности и удобства использования;
- Spring Core Features;
- Annotation Interface @Configuration;
- Annotation Interface @Bean;
- Spring Boot 3.0 Migration Guide;
- Annotation Interface EnableConfigurationProperties;
- Annotation Interface @ConfigurationProperties;
- Annotation Interface @ConditionalOnProperty;
- Annotation Interface @ConditionalOnBean;
- Annotation Interface @ConditionalOnMissingBean;
- Annotation Interface @ConditionalOnClass;
- Annotation Interface @AutoConfigureAfter;
- Annotation Interface @AutoConfigureBefore;
- Class ProxyConfig;
- Interface ObjectProvider;
См. официальные Guides:
- Getting Started Guides - Эти руководства, рассчитанные на 15–30 минут, содержат быстрые практические инструкции по созданию «Hello World» для любой задачи разработки с помощью Spring. В большинстве случаев единственными необходимыми требованиями являются JDK и текстовый редактор.
- Topical Guides - Тематические руководства предназначенные для прочтения и понимания за час или меньше, содержит более широкий или субъективный контент, чем руководство по началу работы.
- Tutorials - Эти учебники, рассчитанные на 2–3 часа, обеспечивают более глубокое контекстное изучение тем разработки корпоративных приложений, что позволяет вам подготовиться к внедрению реальных решений.