Статьи

Ссылка на динамический прокси в прокси-классе

В Stackoverflow возник интересный вопрос о том, как Spring Bean может получить ссылку на прокси-сервер, созданный Spring для обработки транзакций, Spring AOP, кэширования, асинхронных потоков и т. Д. Ссылка на прокси требовалась, потому что при вызове самого себя прокси-компонентом этот вызов полностью обойдёт прокси.

Рассмотрим интерфейс 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