Сообщения

Сообщения за июнь, 2020

Spring IoC контейнер: настройка бинов, обратные вызовы при запуске и завершении работы

Изображение
Интерфейс Lifecycle определяет основные методы для любого объекта, который имеет свои собственные требования жизненного цикла (например, запуск и остановка некоторого фонового процесса): Java public interface Lifecycle { void start(); void stop(); boolean isRunning(); } Kotlin interface Lifecycle { fun start() fun stop() val isRunning: Boolean } Любой объект, управляемый Spring, может реализовать интерфейс Lifecycle. Затем, когда сам ApplicationContext получает сигналы запуска и остановки (например, для сценария остановки/перезапуска во время выполнения), он связывает эти вызовы со всеми реализациями жизненного цикла, определенными в этом контексте. Это делается путем делегирования LifecycleProcessor, как показано в следующем листинге: Java public interface LifecycleProcessor extends Lifecycle { void onRefresh(); void onClose(); } Kotlin interface LifecycleProcessor : Lifecycle { fun onRefresh() fun onClose()

Spring IoC контейнер: настройка бинов, объединение механизмов жизненного цикла

Изображение
Начиная с Spring 2.5 у вас есть три варианта управления поведением жизненного цикла бина: Интерфейсы обратного вызова InitializingBean и DisposableBean Пользовательские методы init() и destroy() Аннотации @PostConstruct и @PreDestroy. Вы можете комбинировать эти механизмы для управления данным бином. Если для bean-компонента настроено несколько механизмов жизненного цикла, и каждый механизм настроен с различным именем метода, то каждый настроенный метод выполняется в порядке, указанном ниже. Однако если одно и то же имя метода настроено - например, init() для метода инициализации - для более чем одного из этих механизмов жизненного цикла, этот метод выполняется один раз. Несколько механизмов жизненного цикла, настроенных для одного и того же bean-компонента с различными методами инициализации, называются следующим образом: Методы, аннотированные @PostConstruct afterPropertiesSet() как определено интерфейсом обратного вызова InitializingBean Настраиваемый метод init() Метод

Spring IoC контейнер: настройка бинов, методы инициализации и уничтожения по умолчанию

Изображение
Когда вы пишете методы обратных вызовов инициализации и уничтожения, которые не используют специфичные для Spring интерфейсы обратного вызова InitializingBean и DisposableBean, вы обычно пишете методы с именами, такими как init(), initialize(), dispose() и так далее. В идеале имена таких методов обратного вызова жизненного цикла стандартизируются по всему проекту, чтобы все разработчики использовали одинаковые имена методов и обеспечивали согласованность. Вы можете настроить контейнер Spring на "поиск" именованных методов обратного вызова инициализации и уничтожения на каждом бине. Это означает, что вы, как разработчик приложения, можете написать свои классы приложения и использовать обратный вызов инициализации, называемый init(), без необходимости настраивать атрибут init-method="init" для каждого определения компонента. Контейнер Spring IoC вызывает этот метод при создании компонента (и в соответствии со стандартным контрактом обратного вызова жизненного цикла,

Spring IoC контейнер: настройка бинов, обратные вызовы разрушения

Изображение
Реализация интерфейса org.springframework.beans.factory.DisposableBean позволяет бину получить обратный вызов, когда контейнер, содержащий его, уничтожен. Интерфейс DisposableBean указывает один метод: Java void destroy() throws Exception; Kotlin fun destroy() Рекомендуется не использовать интерфейс обратного вызова DisposableBean, поскольку он без необходимости связывает код со Spring. В качестве альтернативы предлагается использовать аннотацию @PreDestroy или указать общий метод, который поддерживается определениями компонентов. С метаданными конфигурации на основе XML вы можете использовать атрибут destroy-method в <bean/>. При Java конфигурации вы можете использовать атрибут destroyMethod @Bean. Рассмотрим следующее определение: <bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/> Java public class ExampleBean { public void cleanup() { // выполнить некоторую работу по унич

Spring IoC контейнер: настройка бинов, обратные вызовы инициализации

Изображение
Интерфейс org.springframework.beans.factory.InitializingBean позволяет компоненту выполнять работу по инициализации после того, как контейнер установил все необходимые свойства для компонента. Интерфейс InitializingBean определяет единственный метод: Java void afterPropertiesSet() throws Exception; Kotlin fun afterPropertiesSet() Рекомендуется не использовать интерфейс InitializingBean, потому что он без необходимости связывает код с Spring. В качестве альтернативы предлагается использовать аннотацию @PostConstruct или указать метод инициализации POJO. В случае метаданных конфигурации на основе XML вы можете использовать атрибут init-method, чтобы указать имя метода, имеющего пустую подпись без аргументов. С Java конфигурацией вы можете использовать атрибут initMethod @Bean. Рассмотрим следующий пример: <bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/> Java public class ExampleBean { public vo

Spring IoC контейнер: настройка бинов, обратные вызовы жизненного цикла

Изображение
Для взаимодействия с управлением контейнером жизненного цикла компонента вы можете реализовать интерфейсы Spring InitializingBean и DisposableBean. Контейнер вызывает afterPropertiesSet() для первого и destroy() для второго, чтобы bean-компонент мог выполнять определенные действия при инициализации и уничтожении ваших bean-компонентов. Аннотации JSR-250 @PostConstruct и @PreDestroy обычно считаются лучшей практикой для получения обратных вызовов жизненного цикла в современном приложении Spring. Использование этих аннотаций означает, что ваши компоненты не связаны с интерфейсами, специфичными для Spring. Если вы не хотите использовать аннотации JSR-250, но по-прежнему хотите удалить соединение, рассмотрите метаданные определения bean-метода init и destroy-method. Внутренне Spring Framework использует реализации BeanPostProcessor для обработки любых интерфейсов обратного вызова, которые он может найти, и для вызова соответствующих методов. Если вам нужны пользовательские функции или д

Spring IoC контейнер: использование пользовательской области применения бинов

Изображение
После того, как вы напишете и протестируете одну или несколько пользовательских реализаций Scope , вам необходимо сообщить контейнеру Spring о ваших новых областях. Следующий метод является основным методом регистрации нового Scope в контейнере Spring: Java void registerScope(String scopeName, Scope scope); Kotlin fun registerScope(scopeName: String, scope: Scope) Этот метод объявлен в интерфейсе ConfigurableBeanFactory, который доступен через свойство BeanFactory в большинстве конкретных реализаций ApplicationContext, которые поставляются с Spring. Первый аргумент метода registerScope(..) - это уникальное имя, связанное с областью действия. Примерами таких имен в самом контейнере Spring являются singleton и prototype. Второй аргумент метода registerScope(..) - это фактический экземпляр пользовательской реализации Scope, которую вы хотите зарегистрировать и использовать. Предположим, что вы пишете свою собственную реализацию Scope, а затем регистрируете ее, как показан

Spring IoC контейнер: пользовательские области применения бинов

Изображение
Механизм определения бинов расширяемый. Вы можете определить свои собственные области или даже переопределить существующие области, хотя последнее считается плохой практикой, и вы не можете переопределить встроенные области singleton и prototype. Создание пользовательской области применения бинов Чтобы интегрировать ваши пользовательские области в контейнер Spring, вам необходимо реализовать интерфейс org.springframework.beans.factory.config.Scope. Интерфейс Scope имеет четыре метода для получения объектов из области, удаления их из области и предоставления возможности их уничтожения. Например, реализация области действия session возвращает bean-объект в области session (если он не существует, метод возвращает новый экземпляр bean-компонента после привязки его к session для использования в будущем). Следующий метод возвращает объект из базовой области: Java Object get(String name, ObjectFactory<?> objectFactory) Kotlin fun get(name: String, objectFactory: ObjectFa

Spring IoC контейнер: области применения бинов, выбор типа прокси для создания

Изображение
По умолчанию, когда контейнер Spring создает прокси для компонента, размеченного с помощью элемента <aop:scoped-proxy/> , создается прокси класса на основе CGLIB. CGLIB-прокси перехватывает только публичные вызовы методов! Не вызывайте непубличные методы на таком прокси. Они не делегируются фактическому целевому объекту с областью действия. В качестве альтернативы вы можете настроить контейнер Spring для создания стандартных прокси на основе интерфейса JDK для таких bean-компонентов, указав значение false для значения атрибута proxy-target-class элемента <aop:scoped-proxy/>. Использование прокси на основе интерфейса JDK означает, что вам не нужны дополнительные библиотеки в пути к классам вашего приложения, чтобы повлиять на такое прокси. Тем не менее, это также означает, что класс объекта EJB должен реализовывать как минимум один интерфейс, и что все соавторы, в которые вводится объект EJB, должны ссылаться на компонент через один из его интерфейсов. В следующем примере

Spring IoC контейнер: области применения бинов, бины с областями применения как зависимости

Изображение
Контейнер Spring IoC управляет не только созданием ваших объектов (компонентов), но и подключением сотрудников (collaborators) (или зависимостей). Если вы хотите внедрить (например) компонент с областью действия HTTP-запроса в другой компонент с более длительным сроком действия, вы можете внедрить прокси AOP (AOP proxy) вместо этого компонента. То есть вам нужно внедрить прокси-объект, который предоставляет тот же открытый интерфейс, что и объект области действия, но который также может извлечь реальный целевой объект из соответствующей области (например, HTTP-запрос) и делегировать вызовы методов в реальный объект. Вы также можете использовать <aop:scoped-proxy/> между bean-компонентами, которые определены как одноэлементные (singleton), причем ссылка затем проходит через промежуточный прокси, который сериализуем и поэтому может повторно получить целевой одноэлементный компонент при десериализации. При объявлении <aop:scoped-proxy/> против bean-объекта области видимости

Spring IoC контейнер: области применения бинов, application

Изображение
Рассмотрим следующую конфигурацию XML для определения компонента: <bean id="appPreferences" class="com.something.AppPreferences" scope="application"/> Контейнер Spring создает новый экземпляр компонента AppPreferences, используя определение компонента appPreferences один раз для всего веб-приложения. Таким образом, bean-компонент appPreferences находится на уровне ServletContext и сохраняется как обычный атрибут ServletContext. Это в некоторой степени похоже на одноэлементный компонент Spring (singleton), но отличается двумя важными фактами: это один компонент для ServletContext, а не для Spring 'ApplicationContext' (для которого может быть несколько в любом данном веб-приложении), и он фактически отображается и поэтому видимый как атрибут ServletContext. При использовании управляемых аннотациями компонентов или конфигурации Java вы можете использовать аннотацию @ApplicationScope, чтобы назначить компонент области приложения. В следую

Spring IoC контейнер: области применения бинов, session

Изображение
Рассмотрим следующую конфигурацию XML для определения компонента: <bean id="userPreferences" class="com.something.UserPreferences" scope="session"/> Контейнер Spring создает новый экземпляр компонента UserPreferences с помощью определения компонента userPreferences для времени жизни одного сеанса HTTP. Другими словами, bean-компонент userPreferences эффективно ограничен на уровне сеанса HTTP. Как и в случае bean-объектов в области запросов, вы можете изменять внутреннее состояние экземпляра, который создается столько раз, сколько вы хотите, зная, что другие экземпляры сеанса HTTP, которые также используют экземпляры, созданные из того же определения bean-компонента userPreferences, не видят эти изменения в состоянии, потому что они специфичны для отдельного сеанса HTTP. Когда сеанс HTTP в конечном итоге отбрасывается, компонент, который ограничен этим конкретным сеансом HTTP, также отбрасывается. При использовании управляемых аннотациями комп

Spring IoC контейнер: области применения бинов, request

Изображение
Рассмотрим следующую конфигурацию XML для определения компонента: <bean id="loginAction" class="com.something.LoginAction" scope="request"/> Контейнер Spring создает новый экземпляр bean-компонента LoginAction, используя определение bean-компонента loginAction для каждого HTTP-запроса. То есть bean-компонент loginAction находится на уровне HTTP-запроса. Вы можете изменять внутреннее состояние экземпляра, который создается столько раз, сколько вы хотите, потому что другие экземпляры, созданные из того же определения бина loginAction, не видят эти изменения в состоянии. Они относятся к индивидуальному запросу. Когда запрос завершает обработку, компонент, находящийся в области действия запроса, отбрасывается. При использовании компонентов, управляемых аннотациями, или конфигурации Java, аннотация @RequestScope может использоваться для назначения компонента области запроса. В следующем примере показано, как это сделать: Java @RequestScope @C

Spring IoC контейнер: области применения бинов - Request, Session, Application и WebSocket

Изображение
Области Request, Session, Application и WebSocket доступны только при использовании веб-реализации Spring ApplicationContext (такой как XmlWebApplicationContext). Если вы используете эти области с обычными контейнерами Spring IoC, такими как ClassPathXmlApplicationContext, генерируется исключение IllegalStateException, которое жалуется на неизвестную область действия компонента. Начальная веб-конфигурация Чтобы поддерживать область действия bean-компонентов на уровнеrequest, session, application, и websocket (web-scoped beans), перед определением ваших bean-компонентов требуется небольшая начальная конфигурация. (Эта начальная настройка не требуется для стандартных областей применения: singleton и prototype.) То, как вы выполните эту начальную настройку, зависит от вашей конкретной среды сервлетов. Если вы обращаетесь к bean-объектам области действия в Spring Web MVC, то в действительности в рамках запроса, который обрабатывается Spring DispatcherServlet, никакой специальной настро

Spring IoC контейнер: области применения бинов, прототип (prototype)

Изображение
Область применения не-синглтон прототипа (prototype) развертывания компонента приводит к созданию нового экземпляра компонента каждый раз, когда делается запрос на этот конкретный компонент. Таким образом, компонент вводится в другой компонент, или вы запрашиваете его с помощью вызова метода getBean() для контейнера. Как правило, вы должны использовать область действия прототипа для всех bean-компонентов с состоянием и единственную (singleton) область для bean-компонентов без состояния. В следующем примере бин определяется как прототип в XML: <bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/> В отличие от других областей, Spring не управляет полным жизненным циклом прототипа bean-компонента. Контейнер создает, настраивает и иным образом собирает объект-прототип и передает его клиенту без дальнейшей записи этого экземпляра-прототипа. Таким образом, хотя методы обратного вызова жизненного цикла инициализац

Spring IoC контейнер: области применения бинов, синглтон

Изображение
Управляется только один общий экземпляр одноэлементного (синглтон) компонента, и все запросы на компоненты с идентификатором или идентификаторами, которые соответствуют определению этого компонента, приводят к тому, что один конкретный экземпляр компонента возвращается контейнером Spring. Иными словами, когда вы задаете определение bean-компонента, и оно определено как одноэлементное (синглтон), контейнер IoC Spring создает ровно один экземпляр объекта, определенного этим определением bean-компонента. Этот единственный экземпляр хранится в кэше таких одноэлементных компонентов, и все последующие запросы и ссылки для этого именованного компонента возвращают кешированный объект. Концепция Spring в отношении одноэлементного (синглтон) компонента отличается от паттерна синглтон, определенного в книге паттернов Gang of Four (GoF). Синглтон GoF жестко кодирует область объекта так, что для ClassLoader создается один и только один экземпляр определенного класса. Область применения синглтона

Spring IoC контейнер: области применения бинов

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

Spring IoC контейнер: зависимости, инъекция метода, произвольная замена метода

Изображение
Менее полезной формой внедрения метода, чем внедрением метода поиска , является возможность замены произвольных методов в управляемом компоненте другой реализацией метода. С помощью метаданных конфигурации на основе XML вы можете использовать элемент replace-method, чтобы заменить существующую реализацию метода другой, для развернутого компонента. Рассмотрим следующий класс, в котором есть метод computeValue, который мы хотим переопределить: Java public class MyValueCalculator { public String computeValue(String input) { // некоторый реальный код... } // некоторые другие методы... } Kotlin class MyValueCalculator { fun computeValue(input: String): String { // некоторый реальный код... } // некоторые другие методы... } Класс, который реализует интерфейс org.springframework.beans.factory.support.MethodReplacer, предоставляет определение нового метода, как показано в следующем примере: Java /** * предназначен для переопреде

Spring IoC контейнер: зависимости, инъекция метода поиска

Изображение
Инъекция метода поиска (Lookup method injection) - это способность контейнера переопределять методы в управляемых контейнером bean-компонентах и ​​возвращать результат поиска для другого именованного bean-компонента в контейнере. Поиск обычно включает в себя прототип bean-компонента, как в сценарии, описанном в предыдущем посте . Spring Framework реализует внедрение этого метода, используя генерацию байт-кода из библиотеки CGLIB для динамической генерации подкласса, который переопределяет метод. Чтобы этот динамический подкласс работал, класс, который является подклассом контейнера bean-компонента Spring, не может быть окончательным (final), и переопределяемый метод также не может быть конечным (final). Модульное тестирование (юнит-тестирование) класса, который имеет абстрактный метод, требует, чтобы вы сами создали подкласс класса и предоставили реализацию-заглушку абстрактного метода. Конкретные методы также необходимы для сканирования компонентов, для которого требуются конкретн

Spring IoC контейнер: зависимости, инъекция метода

Изображение
В большинстве сценариев применения большинство бинов в контейнере являются синглтонами. Когда синглтону необходимо сотрудничать с другим синглтоном или не-синглтон компоненту необходимо сотрудничать с другим не-синглтон компонентом, вы обычно обрабатываете зависимость, определяя один компонент как свойство другого. Проблема возникает, когда жизненные циклы бинов различны. Предположим, что синглтон компоненту A нужно использовать не-синглтон (прототип) компонент B, возможно, при каждом вызове метода в A. Контейнер создает синглтон компонент A только один раз и, таким образом, получает только одну возможность установить свойства. Контейнер не может предоставить компоненту A новый экземпляр компонента B каждый раз, когда он нужен. Решение состоит в том, чтобы отказаться от некоторой инверсии контроля. Вы можете сделать так, чтобы компонент A узнал о контейнере, реализовав интерфейс ApplicationContextAware и сделав вызов getBean("B") для контейнера, запрашивая (как правило, новы

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

Изображение
Для каждого компонента вы можете исключить компонент из автопривязки. В XML-формате Spring установите для атрибута autowire-candidate элемента <bean/> значение false. Контейнер делает это конкретное определение компонента недоступным для инфраструктуры автопривязки (включая конфигурации стиля аннотации, такие как @Autowired). Атрибут autowire-candidate предназначен для воздействия только на автопривязку на основе типов. Это не влияет на явные ссылки по имени, которые разрешаются, даже если указанный компонент не помечен как кандидат для автопривязки. Как следствие, автопривязка по имени тем не менее внедряет компонент, если имя совпадает. Вы также можете ограничить число кандидатов на автопривязку, основываясь на сопоставлении с шаблоном по отношению к именам компонентов. Элемент верхнего уровня <beans/> принимает один или несколько шаблонов в своем атрибуте default-autowire-candidates. Например, чтобы ограничить статус кандидата автопривязки любым компонентом, имя которо

Spring IoC контейнер: зависимости, ограничения и недостатки автопривязки

Изображение
Автопривязка работает лучше всего, когда она используется последовательно в проекте. Если автовязка вообще не используется, разработчикам может быть неудобно использовать ее для привязки только одного или двух определений компонента. Рассмотрим ограничения и недостатки автовязки: Явные зависимости в property и constructor-arg всегда переопределяют автопривязку. Вы не можете автоматически связывать простые свойства, такие как примитивы, строки и классы (и массивы таких простых свойств). Это ограничение является побочным. Автопривязка менее точная, чем явная привязка. Хотя Spring старается избегать догадок в случае двусмысленности, которая может привести к неожиданным результатам. Отношения между объектами, управляемыми Spring, больше не документированы в явном виде. Информация о привязке может быть недоступна для инструментов, которые могут генерировать документацию из контейнера Spring. Несколько определений bean-компонентов в контейнере могут соответствовать типу, указанному в м

Spring IoC контейнер: зависимости, автопривязка взаимодействующих компонентов

Изображение
Контейнер Spring может автоматически связывать отношения между взаимодействующими компонентами. Вы можете позволить Spring автоматически разрешать коллабораторов (другие компоненты) для вашего компонента, проверяя содержимое ApplicationContext. Автопривязка (autowiring) имеет следующие преимущества: Автопривязка может значительно уменьшить необходимость указывать свойства или аргументы конструктора. (Другие механизмы, такие как шаблон bean-компонента, также ценны в этом отношении.) Автопривязка может обновлять конфигурацию по мере развития ваших объектов. Например, если вам нужно добавить зависимость к классу, эта зависимость может быть удовлетворена автоматически без необходимости изменять конфигурацию. Таким образом, автопривязка может быть особенно полезным во время разработки, не отменяя возможность переключения на явное подключение, когда кодовая база становится более стабильной. При использовании метаданных конфигурации на основе XML вы можете указать режим автопривязки для