Spring (Core) lessons part 4 - Java-based Configuration.
В папке DOC sql-скрипты и др. полезные файлы.
Док. для изучения:
- Spring Framework 3.2.x Reference Documentation ;
- Spring Framework 6.1.5 Documentation ;
- Пакет org.springframework.context.annotation - Поддержка аннотаций для контекста приложения, включая «общие» аннотации JSR-250, сканирование компонентов и метаданные на основе Java для создания объектов, управляемых Spring;
- Пакет org.springframework.stereotype - Аннотации, обозначающие роли типов или методов в общей архитектуре (на концептуальном, а не на уровне реализации); Предназначен для использования инструментами и аспектами (идеальная target для pointcut);
- Пакет org.springframework.beans.factory - Базовый пакет, реализующий облегченный контейнер Spring Inversion of Control (IoC); Предоставляет альтернативу шаблонам проектирования Singleton и Prototype, включая согласованный подход к управлению конфигурацией. Создан на основе пакета org.springframework.beans;
- Пакет org.springframework.context - Этот пакет основан на пакете bean-компонентов и добавляет поддержку источников сообщений и шаблона проектирования Observer, а также возможность объектам приложения получать ресурсы с использованием согласованного API; Приложениям Spring нет необходимости явно зависеть от ApplicationContext или даже от функциональности BeanFactory. Одной из сильных сторон архитектуры Spring является то, что объекты приложений часто можно настраивать без какой-либо зависимости от API-интерфейсов Spring;
- Пакет org.springframework.core.env - Абстракция среды (окружения) Spring, состоящая из профиля определения компонента и поддержки иерархического источника свойств;
!!! При чтении учебного кода рекомендуется внимательно изучать комментарии к оному и запускать приложение в режиме DEBUG для более эффективного наблюдения за процессами происходящими в приложении !!!
Для начала проведем предварительную подготовку:
Шаг 1. - в файле build.gradle добавим необходимые нам зависимости:
/* Подключим Spring-core и Spring-context. */
implementation 'org.springframework:spring-core:5.3.22'
implementation 'org.springframework:spring-context:5.3.22'
Шаг 2. - подключаем Jakarta Annotation API:
implementation 'jakarta.annotation:jakarta.annotation-api:1.3.5'
Шаг 3. - для того, чтобы обрабатывать аннотации, мы добавляем в application.xml нужные строки и удаляем все лишнее (указываем Sprig-у какую папку сканировать на наличие аннотаций @Component, @Controller, @Repository, @Service:
<context:component-scan base-package="spring.oldboy"/>
В Spring lessons (part 1 - part 2 - part 3) мы настраивали bean-компоненты Spring с помощью файла конфигурации *.XML (см. resources/application.xml) и немного при помощи аннотаций. Теперь приступим к настройке наших bean-ов на основе Java конфигурирования.
Конфигурация на основе Java позволяет настроить работу Spring приложения без использования *.XML, а с помощью нескольких аннотаций на основе Java.
*** @Configuration и @Bean-аннотации ***
Аннотация класса с помощью @Configuration указывает, что класс может использоваться контейнером Spring IoC в качестве источника определений bean-компонентов. Аннотация @Bean сообщает Spring-у, что метод, помеченный @Bean, вернет объект, который должен быть зарегистрирован как bean-компонент в контексте приложения Spring-а. Самый простой возможный класс @Configuration будет следующим:
package spring.oldboy;
import org.springframework.context.annotation.*;
@Configuration
public class HelloWorldConfig {
@Bean
public HelloWorld helloWorld(){
return new HelloWorld();
}
}
Приведенный выше код будет эквивалентен следующей конфигурации *.XML:
<beans>
<bean id = "helloWorld" class = "spring.oldboy.HelloWorld" />
</beans>
В данном примере - имя метода помечено @Bean, и работает как идентификатор компонента, создает и возвращает фактический компонент. Наш класс конфигурации может иметь объявление более чем для одного @Bean-а. После того как наши классы конфигурации определены, мы можем загрузить и предоставить их в контейнер Spring с помощью AnnotationConfigApplicationContext:
public static void main(String[] args) {
ApplicationContext appContext =
new AnnotationConfigApplicationContext(HelloWorldConfig.class);
HelloWorld helloWorld = appContext.getBean(HelloWorld.class);
helloWorld.setMessage("Hello World!");
helloWorld.getMessage();
}
Мы можем загрузить различные классы конфигураций следующим образом:
public static void main(String[] args) {
AnnotationConfigApplicationContext appContext =
new AnnotationConfigApplicationContext();
appContext.register(AppConfig.class, OtherConfig.class);
appContext.register(AdditionalConfig.class);
appContext.refresh();
}
Доп. док. для изучения:
- Class ConfigurationClassBeanDefinitionReader ;
- Annotation Interface Configuration ;
- Spring JavaConfig Reference Guide ;
Когда @Bean-ы имеют зависимости друг от друга, выразить эту зависимость просто:
package spring.oldboy;
import org.springframework.context.annotation.*;
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
return new Foo(bar());
}
@Bean
public Bar bar() {
return new Bar();
}
}
Здесь компонент foo получает ссылку на bar через внедрение в конструктор.
Доп. док. для изучения:
- Annotation Interface Configuration ;
- Annotation Interface Bean ;
- Class AnnotationConfigApplicationContext ;
- Class AnnotationConfigWebApplicationContext ;
- Class ConfigurationClassPostProcessor ;
- Interface Environment ;
Lesson 19 - @Configuration
Создадим папку для хранения всех наших конфигураций - config.
Фактически для работы Spring приложения, обычно используют несколько файлов конфигураций, для логического разделения функциональности:
- управление БД;
- управление web-частью приложения (Web Services, Web Flow, Session и т.д.);
- управление работой message broker (integration broker, interface engine) (ActiveMQ, Kafka, Qpid, RabbitMQ);
- общий конфигурационный файл, для управления всем набором конфигураторов;
В папке config создаем ApplicationConfiguration.java и аннотируем его @Configuration. Далее перетащим в него из application.xml максимально возможный набор управляющих конфигурацией настроек.
Погнали!
- Аннотация @PropertySource является повторяемой, т.е. сама аннотирована как @Repeatable, что означает для нас возможность размещать ее над аннотируемыми классами по нескольку штук и указывать необходимые пути к файлам настроек.
Из файла application.xml стока с указанием пути к файлу свойств:
<context:property-placeholder location="classpath:application.properties"/>
Превращается в аннотацию в файле ApplicationConfiguration.java:
@PropertySource("classpath:application.properties")
На данном этапе наш JavaConfigAndXmlDemo.java все еще работает и выдает результат.
-
Аннотация @ComponentScan над ApplicationConfiguration и ее параметры, практически полностью повторяют настройки из application.xml:
<context:component-scan base-package="spring.oldboy" annotation-config="true" resource-pattern="**/*.class" scoped-proxy="no" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/> <context:include-filter type="assignable" expression="spring.oldboy.repository.CrudRepository"/> <context:include-filter type="regex" expression="spring.oldboy..+Repository"/> </context:component-scan>
И выглядят как:
@ComponentScan(basePackages = "spring.oldboy",
useDefaultFilters = false,
includeFilters = {
@Filter(type = FilterType.ANNOTATION, value = Component.class),
@Filter(type = FilterType.ASSIGNABLE_TYPE, value = CrudRepository.class),
@Filter(type = FilterType.REGEX, pattern = "com\\..+Repository")
})
На данном этапе в IoC контейнере находятся bean-ы созданные согласно нашей Java-конфигурации и OnlyJavaConfigDemo.java прекрасно это демонстрирует. Мы не видим часть bean-ов которые создавали ранее используя application.xml.
Lesson 20 - @ImportResource и @Import
Spring предоставляет аннотацию @ImportResource, которая используется для загрузки bean-компонентов из файла applicationContext.xml в контекст приложения. Применительно к нашему ApplicationConfiguration.java мы получаем комбинированный способ создания 'контекста' используя и XML конфигурирование (Groovy скрипты) и аннотации и Java настройку будущего Spring приложения.
Аннотация @Import позволяет группировать классы конфигураций (например):
@Configuration
@Import({ DogConfig.class, CatConfig.class })
class MammalConfiguration {
}
См. наш ApplicationConfiguration.java
Lesson 21 - @Bean
@Bean - это аннотация уровня метода и прямой аналог элемента XML . Аннотация поддерживает большинство атрибутов, предлагаемых , например: init-method, destroy-method, autowiring, lazy-init, dependency-check, depends-on and scope.
Объявление bean - компонента происходит через добавление аннотации @Bean к методу. Когда JavaConfig обнаруживает такой метод, он выполнит этот метод и зарегистрирует возвращаемое значение как компонент внутри BeanFactory реализации. По умолчанию имя компонента будет таким же, как имя метода. Ниже приведен простой пример объявления @Bean метода:
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
Для сравнения, приведенная выше конфигурация в точности эквивалентна следующему Spring XML:
<beans>
<bean name="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
Оба варианта приведут к тому, что bean-компонент с именем transferService будет доступен в BeanFactory / ApplicationContext и привязан к экземпляру объекта типа TransferServiceImpl.
Внедрение зависимостей тоже упрощается. Когда @Bean-ы имеют зависимости друг от друга, выразить эту зависимость так же просто, как вызвать один метод компонента другим:
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
return new Foo(bar());
}
@Bean
public Bar bar() {
return new Bar();
}
}
- ApplicationConfiguration.java - пример файла конфигурации, в котором созданы bean используя аннотацию @Bean
- BeanJavaConfigDemo.java - простая демонстрация работоспособности контекста на основе Java конфигурации.
Lesson 22 - @Profile
Профили Spring предоставляют возможность разделить части конфигурации нашего приложения и сделать их доступными только в определенных средах. Любой @Component или @Configuration может быть помечен значком @Profile для ограничения при загрузке:
@Configuration
@Profile("production")
public class ProductionConfiguration {
// some code ...
}
В обычном случае в Spring мы можем использовать spring.profiles.active Environment свойство, чтобы указать, какие профили активны. Мы можем указать свойство любым из обычных способов, например, мы можем включить его в свой application.properties:
spring.profiles.active=dev,hsqldb
или указать в командной строке с помощью переключателя: --spring.profiles.active=dev,hsqldb.
- application.properties - в последней строке приведен пример профилирования, и следовательно, активации того или иного профиля через файл свойств.
Мы также можем программно установить активные профили, вызвав их SpringApplication.setAdditionalProfiles(...) перед запуском приложения. Также можно активировать профили с помощью ConfigurableEnvironment интерфейса Spring.
- BeanDefinitionNamesDemo.java - демонстрация работы профилирования запуска bean-ов.
Поскольку в BeanDefinitionNamesDemo мы прописали только один профиль:
myContext.getEnvironment().setActiveProfiles("prod");
А в файле application.properties задан только: spring.profiles.active=production, то в результирующем списке bean-в мы не найдем: spring.web_demo_config.WebConfiguration