Унаследованный метод от обобщенного абстрактного класса в конкретном неуниверсальном классе - PullRequest
0 голосов
/ 22 января 2019

Например, у меня есть следующий интерфейс

public interface Converter<I, O> {
    public O convert(I input);
}

Абстрактный класс, который реализует этот интерфейс

public abstract class AbstractIntegerConverter<T> implements Converter<T, Integer> {

    public Integer convert(T input) {
        // convert anything to int
    }

    public abstract Integer defaultValue();
}

И конкретная реализация

@Component
public class StringToIntegerConverter extends AbstractIntegerConverter<String> {

    @Override
    public Integer defaultValue() {
        // implementation
    }
}

Я хочу посоветовать все методы, которые преобразуют String во что угодно. Я создал следующий аспект

@Aspect
@Component
public class ConverterAspect {

    @Around("execution(* *..*.Converter+.convert(String))")
    public Object adviceStringConverter(ProceedingJoinPoint joinPoint) throws Throwable {
        // logic
    }
}

Это не работает. Spring не создает прокси для класса StringToIntegerConverter. Однако если я переопределил метод convert(String input) из абстрактного класса, он начинает работать, и Spring успешно создает прокси для StringToIntegerConverter и выполняет всю необходимую логику.

@Component
public class StringToIntegerConverter extends AbstractIntegerConverter<String> {

    @Override
    public Integer convert(String input) {
        return super.convert(input);
    }

    @Override
    public Integer defaultValue() {
        // implementation
    }
}

Почему это происходит? Есть ли способ определить pointcut, чтобы мне не нужно было переопределять метод convert(String input)?

1 Ответ

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

В AbstractIntegerConverter сигнатура метода не public Integer convert(String input), а public Integer convert(T input), поэтому ваш pointcut * *..*.Converter+.convert(String) не совпадает. Вместо этого используйте Object или * и проверьте тип среды выполнения с помощью args() вместо использования сигнатуры времени компиляции:

@Around("execution(* *..Converter+.convert(*)) && args(input)")
public Object adviceStringConverter(ProceedingJoinPoint joinPoint, String input) throws Throwable {
    System.out.println(joinPoint);
    return joinPoint.proceed();
}

С моей добавленной строкой журнала вы увидите на консоли что-то вроде:

execution(Integer de.scrum_master.app.AbstractIntegerConverter.convert(Object))

См? convert(Object), а не convert(String), следовательно, несоответствие.

P.S .: Это был интересный вопрос, не такой скучный, как у большинства AspectJ или Spring AOP. Так что спасибо за это. :-)


Обновление:

Что касается вашего дополнительного вопроса, вот что печатает javap -s (аспект деактивирован):

javap -s AbstractIntegerConverter.class

Compiled from "AbstractIntegerConverter.java"
public abstract class de.scrum_master.app.AbstractIntegerConverter<T> implements de.scrum_master.app.Converter<T, java.lang.Integer> {
  public de.scrum_master.app.AbstractIntegerConverter();
    descriptor: ()V

  public java.lang.Integer convert(T);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Integer;

  public abstract java.lang.Integer defaultValue();
    descriptor: ()Ljava/lang/Integer;

  public java.lang.Object convert(java.lang.Object);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
}

См? Подпись convert(Object), как показывает вывод журнала моего аспекта.

javap -s StringToIntegerConverter.class

Compiled from "StringToIntegerConverter.java"
public class de.scrum_master.app.StringToIntegerConverter extends de.scrum_master.app.AbstractIntegerConverter<java.lang.String> {
  public de.scrum_master.app.StringToIntegerConverter();
    descriptor: ()V

  public java.lang.Integer defaultValue();
    descriptor: ()Ljava/lang/Integer;
}

И нет метода convert(String) или convert(whatever) в классе бетонного преобразователя.

Вы мне сейчас верите?

...