Рассмотрим интерфейс InventoryService:
1
2
3
4
5
6
7
8
|
public interface InventoryService{ public Inventory create(Inventory inventory); public List<Inventory> list(); public Inventory findByVin(String vin); public Inventory update(Inventory inventory); public boolean delete(Long id); public Inventory compositeUpdateService(String vin, String newMake); } |
Также учтите, что для этой службы существует реализация по умолчанию, и предположим, что последний метод ComodUpdateService внутренне вызывает два метода на самом компоненте, например:
1
2
3
4
5
6
7
|
public Inventory compositeUpdateService(String vin, String newMake) { logger.info( "composite Update Service called" ); Inventory inventory = this .findByVin(vin); inventory.setMake(newMake); this .update(inventory); return inventory; } |
Если сейчас я создам аспект, который будет рекомендовать любые вызовы InventoryService с целью отслеживания продолжительности каждого вызова метода, Spring AOP создаст динамический прокси-компонент для компонента InventoryService:
Однако вызовы CompositeUpdateService будет записывать время только на уровне этого метода, вызовы, которые CompeatUpdateService внутренне делает, чтобы найтиByVin, обновление обходит прокси и, следовательно, не будет отслеживаться:
Хорошее решение этой проблемы — использовать всю мощь AspectJ — AspectJ изменит байт-код всех методов DefaultInventoryService, чтобы включить вызов совета.
Обходной путь, который мы разработали, заключался в том, чтобы вставить ссылку на сам прокси в компонент и вместо вызова say this.findByVin и this.update, вызвать proxy.findByVin и proxy.update!
Итак, теперь, как мы можем аккуратно внедрить ссылку на прокси в bean-компонент — решение, которое я предложил, заключалось в создании интерфейса для пометки bean-компонентов, заинтересованных в их собственных прокси:
1
2
3
|
public interface ProxyAware<T> { void setProxy(T proxy); } |
Интересующий интерфейс и его реализация будут выглядеть так:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
public interface InventoryService extends ProxyAware<InventoryService>{ ... } public class DefaultInventoryService implements InventoryService{ ... private InventoryService proxy; @Override public void setProxy(InventoryService proxy) { this .proxy = proxy; } } |
а затем определите BeanPostProcessor для внедрения в этот прокси!
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public class ProxyInjectingBeanPostProcessor implements BeanPostProcessor, Ordered { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (AopUtils.isAopProxy((bean))){ try { Object target = ((Advised)bean).getTargetSource().getTarget(); if (target instanceof ProxyAware){ ((ProxyAware) target).setProxy(bean); } } catch (Exception e) { return bean; } } return bean; } @Override public int getOrder() { return Integer.MAX_VALUE; } } |
Не самая чистая из реализаций, но работает!
Ссылка: http://blog.springsource.org/2012/05/23/understanding-proxy-usage-in-spring/
Ссылка: Ссылка на динамический прокси в прокси-классе от нашего партнера по JCG Биджу Кунджуммен в блоге all and sundry