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

Настройка бинов с помощью BeanPostProcessor

Интерфейс BeanPostProcessor определяет методы обратного вызова, которые можно реализовать для обеспечения собственной (или переопределения стандартной) логики создания экземпляров, логики разрешения зависимостей и так далее. Если вам нужно реализовать некоторую специальную логику после того, как контейнер Spring завершит создание экземпляра, конфигурирование и инициализацию бина, то можно подключить одну или несколько специальных реализаций BeanPostProcessor.

Вы можете настроить несколько экземпляров BeanPostProcessor, и вы можете контролировать порядок запуска этих экземпляров BeanPostProcessor, установив свойство order. Вы можете установить это свойство, только если BeanPostProcessor реализует интерфейс Ordered. Если вы пишете свой собственный BeanPostProcessor, вам следует подумать о реализации интерфейса Ordered. Для получения более подробной информации см. javadoc интерфейсов BeanPostProcessor и Ordered.

Экземпляры BeanPostProcessor работают с экземплярами бинов (или объектов). То есть IoC-контейнер Spring создает экземпляр бина, а затем экземпляры BeanPostProcessor выполняют свою работу.

Экземпляры BeanPostProcessor привязаны к одному контейнеру. Это актуально, только если вы используете иерархии контейнеров. Если вы определили BeanPostProcessor в одном контейнере, он будет выполнять постобработку только тех бинов, которые содержатся в этом контейнере. Иными словами, бины, определенные для одного контейнера, не проходят постобработку с помощью BeanPostProcessor, которые определены для другого контейнера, даже если оба контейнера являются частью одной иерархии.

Чтобы изменить фактическое определение бина (то есть макет, определяющий бин), вам необходимо использовать BeanFactoryPostProcessor.

Интерфейс org.springframework.beans.factory.config.BeanPostProcessor состоит ровно из двух методов обратного вызова. Когда такой класс зарегистрирован в качестве постпроцессора в контейнере, для каждого экземпляра бина, создаваемого контейнером, постпроцессор получает обратный вызов от контейнера как до вызова методов инициализации контейнера (таких как InitializingBean.afterPropertiesSet() или любого объявленного метода init), так и после любых обратных вызовов инициализации бина. Постпроцессор может выполнять любое действие с экземпляром бина, включая полное игнорирование обратного вызова. Постпроцессор бина обычно проверяет наличие интерфейсов обратного вызова, или он может обернуть бин прокси. Некоторые классы инфраструктуры АОП Spring реализованы как постпроцессоры бинов, чтобы обеспечить логику обертывания с помощью прокси.

ApplicationContext автоматически обнаруживает любые бины, определенные в конфигурационных метаданных, которые реализуют интерфейс BeanPostProcessor. ApplicationContext регистрирует эти бины как постпроцессоры, чтобы их можно было вызвать позже, при создании бинов. Постпроцессоры бинов могут быть развернуты в контейнере таким же способом, как и любые другие бины.

Обратите внимание, что при объявлении BeanPostProcessor с помощью фабричного метода @Bean для конфигурационного класса, возвращаемым типом фабричного метода должен быть сам класс реализации или, по крайней мере, интерфейс org.springframework.beans.factory.config.BeanPostProcessor, четко указывающий на постпроцессорный характер этого бина. В противном случае ApplicationContext не сможет автоматически определить его по типу до полного создания. Поскольку экземпляр BeanPostProcessor должен быть создан на ранней стадии, чтобы быть применимым к инициализации других бинов в контексте, такое заблаговременное определение типа является очень важным.

Программная регистрация экземпляров BeanPostProcessor
Хотя рекомендуемым подходом для регистрации BeanPostProcessor является автоматическое определение через ApplicationContext (как было описано ранее), можно регистрировать их программно за счет ConfigurableBeanFactory, используя метод addBeanPostProcessor. Это может быть полезно, если нужно оценить логику условного перехода перед регистрацией или даже для копирования постпроцессоров бинов между контекстами в иерархии. Обратите внимание, однако, что экземпляры BeanPostProcessor, добавленные программно, не соблюдают интерфейс Ordered. Здесь порядок регистрации диктует порядок исполнения. Обратите внимание, что экземпляры BeanPostProcessor, зарегистрированные программно, всегда обрабатываются раньше тех, которые зарегистрированы посредством автоматического определения, независимо от явного упорядочивания.
Экземпляры BeanPostProcessor и АОП-автопроксирование

Классы, реализующие интерфейс BeanPostProcessor, являются специальными и обрабатываются контейнером иначе. Все экземпляры BeanPostProcessor и экземпляры бинов, на которые они непосредственно ссылаются, создаются при запуске в рамках специальной фазы запуска ApplicationContext. Далее все экземпляры BeanPostProcessor регистрируются в отсортированном виде и применяются ко всем последующим бинам в контейнере. Поскольку АОП-автопроксирование реализовано как сам BeanPostProcessor, ни экземпляры BeanPostProcessor, ни бины, на которые они непосредственно ссылаются, не подходят для автопроксирования и, таким образом, не имеют "вплетенных" в них аспектов.

Для любого такого бина необходимо просматривать диагностическое сообщение журнала: Бин someBean не подходит для обработки любыми интерфейсами BeanPostProcessor (например: не подходит для автопроксирования).

Если у вас есть бины, подключенные к вашему BeanPostProcessor с посредством автоматического обнаружения и связывания или @Resource (который может вернуться к автоматическому обнаружение и связыванию), Spring может получить доступ к непредвиденным бинам при поиске соответствующих типу компонентов-кандидатов на зависимость и, следовательно, сделать их непригодными для автопроксирования или других видов постобработки бинов. Например, если у вас есть зависимость, помеченная аннотацией @Resource, где имя поля или сеттера не соответствует напрямую объявленному имени бина и не используется атрибут имени, Spring обращается к другим бинам для сопоставления их по типу.

Следующие примеры показывают, как записывать, регистрировать и использовать экземпляры BeanPostProcessor в ApplicationContext.

Пример: Hello World, BeanPostProcessor-style

Этот первый пример иллюстрирует базовое использование. В примере показана реализация специального BeanPostProcessor, который вызывает метод toString() каждого бина по мере его создания контейнером и выводит полученную строку на системную консоль.

В следующем листинге показано определение пользовательского класса реализации BeanPostProcessor:

Java
package scripting;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
    // просто возвращаем созданный экземпляр бина как есть
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean; // потенциально мы могли бы вернуть здесь ссылку на любой объект...
    }
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("Bean '" + beanName + "' created : " + bean.toString());
        return bean;
    }
}
Kotlin
import org.springframework.beans.factory.config.BeanPostProcessor
class InstantiationTracingBeanPostProcessor : BeanPostProcessor {
    // просто возвращаем созданный экземпляр бина как есть
    override fun postProcessBeforeInitialization(bean: Any, beanName: String): Any? {
        return bean // потенциально мы могли бы вернуть здесь ссылку на любой объект...
    }
    override fun postProcessAfterInitialization(bean: Any, beanName: String): Any? {
        println("Bean '$beanName' created : $bean")
        return bean
    }
}

Следующий элемент beans использует InstantiationTracingBeanPostProcessor:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:lang="http://www.springframework.org/schema/lang"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/lang
        https://www.springframework.org/schema/lang/spring-lang.xsd">
    <lang:groovy id="messenger"
            script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
        <lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
    </lang:groovy>
    <!--
    если создается экземпляр вышеупомянутого бина (messenger), эта специальная
    реализация BeanPostProcessor выводит факт в системную консоль
    -->
    <bean class="scripting.InstantiationTracingBeanPostProcessor"/>
</beans>

Обратите внимание, что InstantiationTracingBeanPostProcessor просто определен. У него даже нет имени, и, поскольку это бин, в него можно внедрить зависимости, как и в любой другой бин. (Предыдущая конфигурация также определяет бин, который поддерживается Groovy-скриптом.

Следующее Java-приложение запускает предыдущий код и конфигурацию:

Java
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;
public final class Boot {
    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
        Messenger messenger = ctx.getBean("messenger", Messenger.class);
        System.out.println(messenger);
    }
}
Kotlin
import org.springframework.beans.factory.getBean
fun main() {
    val ctx = ClassPathXmlApplicationContext("scripting/beans.xml")
    val messenger = ctx.getBean<Messenger>("messenger")
    println(messenger)
}

Вывод в консоль предыдущего приложения выглядят следующим образом:

Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961
Пример: AutowiredAnnotationBeanPostProcessor

Использование интерфейсов обратного вызова или аннотаций в сочетании со специальной реализацией BeanPostProcessor является распространенным средством расширения IoC-контейнера Spring. Примером может служить AutowiredAnnotationBeanPostProcessor в Spring - реализация BeanPostProcessor, поставляемая с дистрибутивом Spring и автоматически подключающая аннотированные поля, сеттеры и произвольные методы конфигурации.