Spring предлагает концепцию TargetSource, выраженную в интерфейсе org.springframework.aop.TargetSource. Этот интерфейс отвечает за возврат "целевого объекта", который реализует точку соединения. Реализация TargetSource запрашивается для целевого экземпляра каждый раз, когда прокси АОП обрабатывает вызов метода.

Разработчикам, использующим Spring AOP, обычно не приходится работать напрямую с реализацией TargetSource, но она представляет собой эффективное средство поддержки объединяемых в пул, заменяемых в "горячем" режиме и других сложных целей. Например, TargetSource для объединенных в пул целей может возвращать разные экземпляры цели для каждого вызова, используя пул для управления экземплярами.

Если вы не задали TargetSource, то для обертывания локального объекта используется реализация по умолчанию. Для каждого вызова возвращается одна и та же цель (как и можно ожидать).

В остальной части этого раздела рассматриваются стандартные источники целей, предоставляемые Spring, и способы их использования.

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

Источники целей, заменяемых в "горячем" режиме

org.springframework.aop.target.HotSwappableTargetSource предназначен для того, чтобы заменять цель прокси АОП, позволив при этом вызывающим подпрограммам сохранять свои ссылки на нее.

Замена цели из источника целей срабатывает немедленно. HotSwappableTargetSource является потокобезопасным.

Вы можете изменить цель с помощью метода swap() для HotSwappableTargetSource, как показано в следующем примере:

Java
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget);
Kotlin
val swapper = beanFactory.getBean("swapper") as HotSwappableTargetSource
val oldTarget = swapper.swap(newTarget)

The following example shows the required XML definitions:

<bean id="initialTarget" class="mycompany.OldTarget"/>
<bean id="swapper" class="org.springframework.aop.target.HotSwappableTargetSource">
    <constructor-arg ref="initialTarget"/>
</bean>
<bean id="swappable" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetSource" ref="swapper"/>
</bean>

Предшествующий вызов swap() изменяет цель заменяемого бина. Клиентские части кода, имеющие ссылку на этот бин, не знают об изменении, но сразу же начинают "бить" по новой цели.

Хотя в данном примере не было добавлено никаких советов (для использования TargetSource не обязательно добавлять советы), любой TargetSource может быть использован в сочетании с произвольными советами.

Источники целей, объединяемых в пул

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

Критическое различие между пулингом в Spring и пулингом в SLSB заключается в том, что пулинг в Spring может быть применен к любому POJO. Как и в случае со Spring в целом, эта служба может быть применена неагрессивным способом.

Spring обеспечивает поддержку Commons Pool 2.2, который предоставляет достаточно эффективную реализацию пулинга. Для использования этой возможности необходимо, чтобы в пути классов вашего приложения находился Jar-файл commons-pool. Вы также можете разбить org.springframework.aop.target.AbstractPoolingTargetSource на подклассы, чтобы обеспечить поддержку любого другого API-интерфейса для пулинга.

Commons Pool 1.5+ также поддерживается, но он устарел по сравнению с Spring Framework 4.2.

The following listing shows an example configuration:

<bean id="businessObjectTarget" class="com.mycompany.MyBusinessObject"
        scope="prototype">
    ... Свойства опущены
</bean>
<bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPool2TargetSource">
    <property name="targetBeanName" value="businessObjectTarget"/>
    <property name="maxSize" value="25"/>
</bean>
<bean id="businessObject" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="targetSource" ref="poolTargetSource"/>
    <property name="interceptorNames" value="myInterceptor"/>
</bean>

Обратите внимание, что целевой объект(businessObjectTarget в предыдущем примере) должен быть прототипом. Это позволит реализации PoolingTargetSource создавать новые экземпляры цели для увеличения пула по мере необходимости. См. javadoc по AbstractPoolingTargetSource и конкретному подклассу, который вы хотите использовать, для получения информации о его свойствах. maxSize является самым основным свойством и всегда гарантированно присутствует.

В данном случае myInterceptor – это имя перехватчика, который должен быть определен в том же IoC-контексте. Однако, чтобы использовать пулинг, не обязательно указывать перехватчики. Если требуется только пулинг и не нужны никакие советы, не устанавливайте свойство interceptorNames вообще.

Вы можете настроить Spring на возможность приведения любого объекта, помещенного в пул, к интерфейсу org.springframework.aop.target.PoolingConfig, который открывает информацию о конфигурации и текущем размере пула через введение. Необходимо определить советник, подобный следующему:

<bean id="poolConfigAdvisor" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject" ref="poolTargetSource"/>
    <property name="targetMethod" value="getPoolingConfigMixin"/>
</bean>

Этот советник можно получить путем вызова вспомогательного метода для класса AbstractPoolingTargetSource, поэтому используется MethodInvokingFactoryBean. Имя этого советника (здесьpoolConfigAdvisor) должно находиться в списке имен перехватчиков в ProxyFactoryBean, который открывает объект пула.

Приведение определяется следующим образом:

Java
PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
System.out.println("Max pool size is " + conf.getMaxSize());
Kotlin
val conf = beanFactory.getBean("businessObject") as PoolingConfig
println("Max pool size is " + conf.maxSize)
Объединение в пул объектов-служб, не сохраняющих состояние, обычно не требуется. Мы не считаем, что это следует делать выбором по умолчанию, так как большинство объектов, не сохраняющих состояние, естественно потокобезопасны, а пулинг экземпляров проблематичен, если ресурсы кэшируются.

Более простой пулинг можно осуществить при использовании автопроксирования. Можно установить реализацию TargetSource, используемую любым создателем авто-прокси.

Источники целей-прототипов

Настройка источника целей-прототипов аналогична настройке пулинга TargetSource. В этом случае при каждом вызове метода создается новый экземпляр цели. Хотя затраты ресурсов на создание нового объекта в современной JVM невелики, затраты на связывание нового объекта (удовлетворение его IoC-зависимостей) могут быть достаточно высоки. Таким образом, не следует использовать данный подход без крайне веских причин.

Для этого можно изменить определение poolTargetSource, показанное ранее, следующим образом (для ясности мы также изменили имя):

<bean id="prototypeTargetSource" class="org.springframework.aop.target.PrototypeTargetSource">
    <property name="targetBeanName" ref="businessObjectTarget"/>
</bean>

Единственным свойством является имя целевого бина. Наследование используется в реализации TargetSource для обеспечения согласованного именования. Как и в случае источника целей, объединяемых в пул, целевой бин должен быть определением бина-прототипа.

Источники целей ThreadLocal

Источники целей ThreadLocal практичны, если нужно, чтобы объект создавался для каждого входящего запроса (то есть для каждого потока). Концепция ThreadLocal предлагает JDK-средство для прозрачного хранения ресурса вместе с потоком. Настройка ThreadLocalTargetSource практически не отличается от настройки других типов источников целей, как это показано в следующем примере:

<bean id="threadlocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource">
    <property name="targetBeanName" value="businessObjectTarget"/>
</bean>
При неправильном использовании экземпляров ThreadLocal в многопоточных средах и средах с несколькими загрузчиками возникают серьезные проблемы (потенциально приводящие к утечкам памяти). Всегда необходимо рассматривать возможность обертывания threadlocal в какой-либо другой класс и никогда не использовать непосредственно сам ThreadLocal (кроме как в классе-обёртке). Кроме того, всегда следует помнить, что нужно правильно устанавливать и возвращать в исходное состояние (где под последним подразумевается вызов ThreadLocal.set(null)) локальный для потока ресурс. Возвращение в исходное состояние следует осуществлять в любом случае, иначе это может привести к сбоям в логике работы. Средства поддержки ThreadLocal в Spring делают это за вас и им всегда следует отдавать предпочтения вместо использования экземпляров ThreadLocal без иного надлежащего кода обработки.

Определение новых типов советов

Spring AOP спроектирован так, чтобы быть расширяемым. Хотя стратегия реализации перехвата в настоящее время используется внутренним образом, можно обеспечить поддержку произвольных типов советов в дополнение к советам "перехват", "перед", "генерация исключения" и "после возврата".

Пакет org.springframework.aop.framework.adapter – это пакет SPI-интерфейса, который позволяет добавлять поддержку новых пользовательских типов советов без изменения основной структуры. Единственным ограничением для специализированного типа Advice является то, что он должен реализовывать интерфейс маркера org.aopalliance.aop.Advice.

Более подробную информацию см. в javadoc по org.springframework.aop.framework.adapter.