- PVSM.RU - https://www.pvsm.ru -
В этой статье я хочу базово пройтись по Spring. Рассказать о возможностях конфигурации её бинов и немного залезть во внутрь.
IoC container - это контейнер, реализующий принцип Inversion of Control (IoC). Он управляет созданием, связыванием и жизненным циклом бинов, которые конфигурируются на различных этапах сборки приложения и затем добавляются в контекст.
org.springframework.beans
и org.springframework.context
пакеты являются основой для Spring Framework’s IoC container. BeanFactory [1] - это интерфейс контейнера Spring, предоставляющий базовый функционал для создания и управления бинами. BeanFactory
используется в основном для простых приложений и в случаях, когда ресурсы ограничены. Это наиболее низкоуровневый интерфейс, предоставляющий базовые возможности по конфигурации и управлению бинами. ApplicationContext [2] является под-интерфейсом BeanFactory [1]. Он добавляет:
Упрощенную интеграцию со Spring AOP, что позволяет добавлять проксирование
Механизмы работы с интернационализацией [3], что позволяет разработчикам проще создавать приложения на различных языках
Механизмы публикации и обработки событий, что позволяет компонентам приложения общаться между собой (благодаря этому компоненты приложения станут более гибкими и менее связанными)
Свои специальные реализации:
WebApplicationContext
: Расширяет стандартный ApplicationContext
и добавляет функции, необходимые для работы с веб-приложениями, такие как поддержка сервлетов, фильтров, и интеграция с веб-сессиями
AnnotationConfigApplicationContext
: Используется для конфигурации на основе аннотаций и Java-кода, что позволяет работать без XML-конфигурации
GenericWebApplicationContext
: Позволяет использовать как традиционную конфигурацию на основе XML, так и конфигурацию на основе аннотаций и Java-кода
ClassPathXmlApplicationContext
: Позволяет конфигурировать бины на основе вашего XML файла
Вкратце, BeanFactory
обеспечивает механизм конфигурации и базовое управление бинами в IoC контейнере, тогда как ApplicationContext
расширяет эти возможности, предлагая дополнительные функции, специфичные для корпоративных приложений.
На диаграмме ниже показано высокоуровневое представление о работе контейнера:
Как видно из диаграммы, Spring контейнер требует декларацию каждого бина. Эта декларация описывается при помощи аннотаций или XML. В ней описано метаданные для конфигурации бина (из какого класса нужно создавать, есть ли у него init() метод и как он называется, какие у него property, scope и далее). Метаданные бина Spring должен знать для того, чтобы его собрать.
Контейнер IoC Spring полностью независим от формата, в котором записаны метаданные конфигурации. Существует несколько способов настроить метаданные бинов:
Конфигурация на основе аннотаций: прямо в классе аннотациями размечать для Spring данные о будущем бине (@Component, @Service, @Repository, @Controller, @Autowired, @Qualifier)
@Service
public class ExampleService {
private int someValue;
public void someMethod(){
System.out.println("I'm a service");
}
public int getSomeValue() {
return someValue;
}
public void setSomeValue(int someValue) {
this.someValue = someValue;
}
}
@Component
public class ExampleClass {
private int value;
@Autowired
private ExampleService service;
public void exampleMethod(){
System.out.println("Hello world!");
System.out.println("Service value: "+service.getSomeValue());
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
Конфигурация на основе Java: для настройки метаданных о бине нужно создать отдельный конфигурационный класс (пометить его аннотацией @Configuration), а бины регистрировать при помощи метода (помеченного аннотацией @Bean), который возвращает объект нужного класса
@Configuration
public class ConfigurationClass {
@Bean
public ExampleService service(){
return new ExampleService();
}
@Bean
public ExampleClass exampleClass(){
return new ExampleClass(service());
}
}
XML конфигурация: все бины и их зависимости прописываются в XML файле
<!-- Определение бина ExampleService -->
<bean id="service" class="com.example.ExampleService"/>
<!-- Определение бина ExampleClass и внедрение зависимости service -->
<bean id="exampleClass" class="com.example.ExampleClass">
<constructor-arg ref="service"/>
</bean>
У каждого бина есть свой жизненный цикл (ЖЦ). Это время от момента создания и до уничтожения его из контекста.
BeanDefenition
– объект, который хранит информацию про бин
BeanPostProcessor (BPP)
– позволяет настраивать бины до того как они попали в IoC контейнер (он управляет аннотациями @Autowired, @Transactional, @Async и т.д.)
BeanFactoryPostProcessor (BFPP)
– класс, который позволяет настраивать BeanDefenition (т.е. еще до того как BeanFactory начала создавать бины). Может что-то изменять как в BeanDefenition, так и в самом BeanFactory перед тем, как он начнет работать и создавать бины из BeanDefenition.
Init() метод
– метод, который выполняется до попадания бина в IoC контейнер (после первого и до второго прохода по всем BPP). Можно прописать:
Если через xml, то через атрибут “init-method” в теге <bean>
Если работа с аннотациями, то поставить @PostConstract
Зачем использовать init-метод если есть конструктор? Потому что конструктор вызывается и используется BeanFactory для создания объекта и базовой инициализации данных. Однако, spring начнет дополнительно настраивать поля (например, какую-либо аннотацию для поля начнет обрабатывать BPP). Возникнет проблема если в конструкторе попытаться обратиться к данным, которые настраивает Spring уже после использования самого конструктора.
В Spring инициализация бина может проходить в три фазы:
Обычный конструктор класса
init-метод
Контекстные слушатели (ContextListener)
ApplicationListener
– слушает events контекста, и принимает соответствующие действия
Виды событий:
ContextStartedEvent (контекст начал свое построение, а когда заканчивает, то делает refresh)
ContextStoppedEvent (контекст приложения останавливается)
ContextRefreshedEvent (контекст был инициализирован или обновлен)
ContexClosedEvent (контекст приложения закрывается)
Зачем он нужен? Пример:
Во время старта приложения нужно “разогреть” кэш (для этого нужно сходить в БД, взять данные и обновить их у себя).
В конструкторе логику не написать, потому что на этапе использования конструктора бин еще вообще не готов, в init-метод не написать, потому что на этом этапе еще не
существует транзакции (@Transaction не будет настроена, т.к. init-метод сработает раньше, чем BPP настроит аннотацию). Остается дождаться полного создания контекста и прослушать событие refreshed.
Этапы создания бина:
Считываются все декларации бинов и кладет в специальную Map “BeanDefenitions
” (id бина = декларация)
BeanFactoryPostProcessor
изменяет BeanDefenitions
или BeanFactory
(если настроено)
После создания BeanDefenitions
, BeanFactory
начинает по ним работать
Берет бин
Настраивает его согласно конфигурации
Отсылает по очереди ко всем BeanPostProcessor (BPP)
, потому что могут быть как кастомные, так и от spring (с аннотациями @Autowired, @Async, @Transactional и т.д.)
У объекта вызывается init() метод
Еще один проход по всем BPP
(на случай проксирования)
Кладет в IoC контейнер
(если scope бина singleton)
Так же, у каждого бина можно создать destroy-метод, который сработает перед его уничтожением. Можно прописать:
Если работа с аннотациями, то над методом поставить @PreDestroy
Если через xml, то через атрибут “destroy-method ” в теге <bean>
Хочешь, хорошо работать – пользуйся Спрингом
Хочешь, чтобы работало хорошо – знай его кишкиЕвгений Борисов на вебинаре "Спринг Потрошитель"
Автор: VE_N_IK
Источник [4]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/393232
Ссылки в тексте:
[1] BeanFactory: https://docs.spring.io/spring-framework/docs/6.1.11/javadoc-api/org/springframework/beans/factory/BeanFactory.html
[2] ApplicationContext: https://docs.spring.io/spring-framework/docs/6.1.11/javadoc-api/org/springframework/context/ApplicationContext.html
[3] интернационализацией: https://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D0%BD%D0%B0%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F
[4] Источник: https://habr.com/ru/articles/834966/?utm_source=habrahabr&utm_medium=rss&utm_campaign=834966
Нажмите здесь для печати.