Spring предлагает концепцию TargetSource
, выраженную в интерфейсе org.springframework.aop.TargetSource
. Этот интерфейс отвечает за возврат "целевого объекта", который реализует точку соединения. Реализация TargetSource
запрашивается для целевого экземпляра каждый раз, когда прокси АОП обрабатывает вызов метода.
Разработчикам, использующим Spring AOP, обычно не приходится работать напрямую с реализацией TargetSource
, но она представляет собой эффективное средство поддержки объединяемых в пул, заменяемых в "горячем" режиме и других сложных целей. Например, TargetSource
для объединенных в пул целей может возвращать разные экземпляры цели для каждого вызова, используя пул для управления экземплярами.
Если вы не задали TargetSource
, то для обертывания локального объекта используется реализация по умолчанию. Для каждого вызова возвращается одна и та же цель (как и можно ожидать).
В остальной части этого раздела рассматриваются стандартные источники целей, предоставляемые Spring, и способы их использования.
Источники целей, заменяемых в "горячем" режиме
org.springframework.aop.target.HotSwappableTargetSource
предназначен для того, чтобы заменять цель прокси АОП, позволив при этом вызывающим подпрограммам сохранять свои ссылки на нее.
Замена цели из источника целей срабатывает немедленно. HotSwappableTargetSource
является потокобезопасным.
Вы можете изменить цель с помощью метода swap()
для HotSwappableTargetSource, как показано в следующем примере:
HotSwappableTargetSource swapper = (HotSwappableTargetSource) beanFactory.getBean("swapper");
Object oldTarget = swapper.swap(newTarget);
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-интерфейса для пулинга.
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
, который открывает объект пула.
Приведение определяется следующим образом:
PoolingConfig conf = (PoolingConfig) beanFactory.getBean("businessObject");
System.out.println("Max pool size is " + conf.getMaxSize());
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
.