Spring AOP не работает для вызова метода внутри другого метода

В ABC.java

определены два метода:
public void method1(){
   .........
   method2();
  ...........
}


public void method2(){
  ...............
  ...............  
}

Я хочу иметь AOP по вызову method2. Так, Я создал один класс AOPLogger.java, имеющий функциональные возможности, предоставляемые в методе checkAccess
В файле конфигурации я сделал что-то вроде ниже

<bean id="advice" class="p.AOPLogger" />
<aop:config>
  <aop:pointcut id="abc" expression="execution(*p.ABC.method2(..))" />
  <aop:aspect id="service" ref="advice">
    <aop:before pointcut-ref="abc" method="checkAccess" />          
  </aop:aspect>
</aop:config>

Но когда вызывается мой метод2, функция AOP не вызывается, т.е. метод checkAccess не будет вызван классом AOPLogger.

Любая вещь, которую я не вижу?

Ответ 1

Аспект применяется к прокси-серверу, окружающему bean. Обратите внимание, что каждый раз, когда вы получаете ссылку на bean, это не фактически класс, на который ссылается ваша конфигурация, а синтетический класс, реализующий соответствующие интерфейсы, делегируя фактический класс и добавляя функциональность, например, ваш AOP.

В приведенном выше примере вы вызываете непосредственно в классе, тогда как если этот экземпляр класса вводится в другой как Spring bean, он вводится как его прокси, и, следовательно, вызовы методов будут вызываться в прокси-сервере (и аспекты будут активированы)

Если вы хотите достичь вышеизложенного, вы можете разделить method1/method2 на отдельный beans или использовать неориентированную структуру AOP без spring.

Spring doc подробно описывает это и несколько обходных решений (включая мое первое предложение выше)

Ответ 2

Это можно сделать путем использования собственной инъекции. Вы можете вызвать внутренний метод через инъецируемый экземпляр:

@Component
public class Foo {
    @Resource
    private Foo foo;

    public void method1(){
        ..
        foo.method2();
        ..
    }
    public void method2(){
        ..
    }
}

Так как Spring 4.3 вы также можете сделать это с помощью @Autowired.

Начиная с 4.3, @Autowired также рассматривает собственные ссылки для инъекций, т.е. ссылается на bean, который в настоящее время вводится.

Ответ 3

У меня была такая же проблема, и я преодолел реализацию Spring ApplicationContextAware, BeanNameAware и внедряя соответствующие методы, как показано ниже.

class ABC implements ApplicationContextAware,BeanNameAware{

      @Override
      public void setApplicationContext(ApplicationContext ac) throws BeansException {
          applicationContext=ac;
      }

      @Override
      public void setBeanName(String beanName) {
          this.beanName=beanName;
      }
      private ApplicationContext applicationContext;
      private String beanName;
}

то я заменил this. на ((ABC) applicationContext.getBean(beanName))., вызывая методы того же класса. Это гарантирует, что вызовы методов одного и того же класса происходят только через прокси.

Итак method1() изменяется на

 public void method1(){
    .........
    ((ABC) applicationContext.getBean(beanName)).method2();
    ...........
  }

Надеюсь, что это поможет.

Ответ 4

Spring Основа AOP основана на "прокси", и здесь очень хорошо объясняется:   http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/aop.html#aop-understanding-aop-proxies

Когда Spring строит bean, который настроен на аспект (например, "ABC" в вашем примере), он фактически создает объект "прокси", который действует как реальный bean. Прокси просто делегирует вызовы "реальному" объекту, но, создав это косвенное действие, прокси получает возможность реализовать "совет". Например, ваш совет может регистрировать сообщение для каждого вызова метода. В этой схеме, если метод в реальном объекте ( "метод1" ) вызывает другие методы в одном и том же объекте (скажем, метод2), эти вызовы происходят без прокси на картинке, поэтому нет никаких шансов для реализации каких-либо советов.

В вашем примере, когда вызывается метод1(), прокси получит возможность делать то, что он должен делать, но если метод1() вызывает метод2(), на картинке нет никакого аспекта. Однако, если метод2 вызывается из какого-либо другого bean, прокси сможет выполнить совет.

Надеюсь, что это поможет.

Спасибо, Рагу

Ответ 5

Используя @Autowired, он работает. Вместо вызова внутреннего метода this.method() вы можете сделать:

@Autowired
Foo foo;

а затем вызов:

foo.method2();

Ответ 6

Невозможно, чего вы хотите достичь. Объяснение приведено в Spring Reference Documentation.