Как создать pointcut для абстрактного метода - PullRequest
0 голосов
/ 05 февраля 2019

Я пытаюсь создать pointcut для абстрактного метода, реализованного в дочернем классе, но AOP никогда не вызывается.

Вот мой минимальный код Java:

package com.example;

public class Service {
    private ParentAbstractClass clazz;

    public Service(ParentAbstractClass clazz) {
        this.clazz = clazz;
    }

    public void process() {
        clazz.method();
    }
}

Этоэто класс обслуживания, в котором есть абстракция класса для внедрения, и он вызывает метод.

Мой абстрактный класс, который имеет некоторую общую логику и специфический для реализации код, который является абстрактным методом.

package com.example;

import java.util.List;

public abstract class ParentAbstractClass {
    public void method() {
        abstractMethod(List.of("test"));
    }

    public abstract void abstractMethod(List<String> names);
}

Это класс, обеспечивающий реализацию абстрактного метода.

package com.example;

import java.util.List;

public class ConcreteClass extends ParentAbstractClass {
    @Override
    public void abstractMethod(List<String> names) {
        System.out.println("Look up! AOP should have executed");
    }
}

В этой настройке я использую Spring XML для настройки моих bean-компонентов.

<bean id = "clazz" class="com.example.ConcreteClass"/>

<bean id="myservice" class="com.example.Service">
    <constructor-arg ref="clazz"/>
</bean>

<bean id = "aspect" class="com.exmple.TxAspect"/>

<aop:config>
    <aop:aspect id="mergeEnableAspect" ref="aspect">
        <aop:pointcut id="mergeServicePointCut"
                      expression="execution(* com.example.ConcreteClass.abstractMethod(..))"/>
        <aop:around pointcut-ref="mergeServicePointCut" method="test" arg-names="pjp"/>
    </aop:aspect>
</aop:config>

Инаконец, класс AOP:

import org.aspectj.lang.ProceedingJoinPoint;

public class TxAspect {
    public void test(ProceedingJoinPoint pjp) {

        System.out.println("I am not going to do anything");

    }
}

В моем abstractMethod я делаю что-то, что носит транзакционный характер, и у меня есть бизнес-потребность контролировать транзакции вручную, но Мой аспектный класс никогда не вызывается.Может кто-нибудь, пожалуйста, помогите мне выяснить, какую ошибку я совершил.

Спасибо.

1 Ответ

0 голосов
/ 12 февраля 2019

Проблема вызвана ограничениями реализации Spring AOP.Вот цитата из документации Spring :

Из-за природы платформы AOP, основанной на прокси, вызовы внутри целевого объекта по определению не перехватываются.Для прокси-серверов JDK могут быть перехвачены только вызовы методов открытого интерфейса на прокси-сервере.При использовании CGLIB публичные и защищенные вызовы методов на прокси-сервере перехватываются (и даже видимые в пакете методы, если необходимо).Тем не менее, обычные взаимодействия через прокси-серверы всегда должны разрабатываться с помощью общедоступных подписей.

Обратите внимание, что определения точек разреза обычно сопоставляются с любым перехваченным методом.Если pointcut строго предназначен только для публичного доступа, даже в сценарии с прокси-сервером CGLIB с потенциальными непубличными взаимодействиями через прокси-серверы, его необходимо определить соответствующим образом.

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

Итак, у вас есть два возможных подхода к решению проблемы:

  1. Используйте ваш аспект только для методов, которые вызываются другими компонентами (без самостоятельного вызова)
  2. Используйте реализацию AspectJ AOP.Я подготовил простой пример, используя ткачество времени компиляции AspectJ, код размещен на github

Подход AspectJ в двух словах:

Изменить аспект транзакцииследующим образом:

package com.example;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class TxAspect {

    @Around("methodsToBeProfiled()")
    public void test(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("I am not going to do anything");
        pjp.proceed();
    }

    @Pointcut("execution(* com.example.ConcreteClass.abstractMethod(..))")
    public void methodsToBeProfiled(){}
} 

Сократите конфигурацию XML следующим образом:

  <bean id="clazz" class="com.example.ConcreteClass"/>

  <bean id="myservice" class="com.example.Service">
    <constructor-arg ref="clazz"/>
  </bean>

Скомпилируйте приложение, используя следующий плагин maven:

  <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.11</version>
    <dependencies>
      <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjtools</artifactId>
        <version>${ascpectj.version}</version>
      </dependency>
    </dependencies>
    <configuration>
      <source>1.8</source>
      <target>1.8</target>
      <complianceLevel>1.8</complianceLevel>
    </configuration>
    <executions>
      <execution>
        <goals>
          <goal>compile</goal>
          <goal>test-compile</goal>
        </goals>
      </execution>
    </executions>
  </plugin>
...