0

I currently have Spring AOP defining an aspect for logging(Logging Aspect) on a Spring Bean (Target Implementation). When I access the method normally (Regular Access) My Implementation runs and I receive logs. However, when I access my implementation via reflection (Reflection Access) My implementation runs, but I do not receive any logging. I would like to understand why this is and how I can get my reflection based access pattern to get logs. See Reference picture

Basic code flow

Full example: (Note the reflection call actually comes from the parent class)

Parent Class (with reflection call)

public class EventValidationHandler {
    private List<Method> aggregateMethods;

    public EventValidationHandler() {
        aggregateMethods = getMethodsAnnotatedWithAggregateEventHandler(this.getClass());
    }

    public boolean validateEvent(DomainEvent event) throws Exception {
        Class clazz = event.getClass();
        for(Method method : aggregateMethods) {
            for(Class aClass : method.getParameterTypes()) {
                if(clazz.equals(aClass)) {
                    Object response = method.invoke(this, event);
                    return (Boolean) response;
                }
            }
        }
        throw new NoSuchMethodError("Unable to locate event validator method for event type: " + event.getClass().getSimpleName() + ".  Please confirm method is annotated with @" + EventValidator.class.getSimpleName());
    }

    private static List<Method> getMethodsAnnotatedWithAggregateEventHandler(final Class<?> type) {
        final List<Method> methods = new ArrayList<>();
        final List<Method> allMethods = new ArrayList<>(Arrays.asList(type.getDeclaredMethods()));
        for (final Method method : allMethods) {
            if (method.isAnnotationPresent(EventValidator.class)) {
                methods.add(method);
            }
        }
        return methods;
    }
}

Implementation

@Component
@AllArgsConstructor
public class MyEventValidator extends EventValidationHandler {
    private AggregateRepository aggregateRepository;

    @EventValidator
    public boolean isValid(CreateProfileEvent event) {
        if(aggregateRepository.findOne(event.getEmployeeId()) == null) {
            return true;
        }
        return false;
    }

    @EventValidator
    public boolean isValid(ChangePhoneNumberEvent event) {
        if(aggregateRepository.findOne(event.getEmployeeId()) != null) {
            return true;
        }
        return false;
    }
}

Logging Aspect

@Aspect
@Component
public class MyEventValidatorLoggingAspect {
    public static Logger log = LoggerFactory.getLogger(MyEventValidator.class);

    @Pointcut("execution(* com.mcf7.eventsourcing.test.data.events.MyEventValidator.isValid(com.mcf7.eventsourcing.test.data.events.CreateProfileEvent))")
    private void createProfile() {}

    @Before("createProfile()")
    public void logHere(JoinPoint joinPoint) {
        log.info("super cool log of create profile");
    }
}
Matthew Fontana
  • 3,790
  • 2
  • 30
  • 50
  • How do you invoke `validateEvent()`? You're probably invoking the `validateEvent` on the proxy target class and not the proxy itself. See http://stackoverflow.com/questions/1606559/spring-aop-vs-aspectj – Nándor Előd Fekete Jan 17 '17 at 19:10
  • Hmm I think you might be right. How can I adjust this to access the proxy itself? – Matthew Fontana Jan 17 '17 at 19:28
  • That's why asked how you invoke `validateEvent()`. – Nándor Előd Fekete Jan 17 '17 at 22:08
  • 2
    You could do it the other way around: avoid proxies by using native AspectJ (not proxy-based) instead of Spring AOP (needs dynamic proxies). If you do not use proxies, an `execution()` pointcut does what you want, no matter if the method is called directly or via reflection. – kriegaex Jan 17 '17 at 22:08
  • Hmm that definitely is an option. I will probably go with that approach, however, How can I get a reference to the proxy? I invoke validateEvent by calling it directly from my implementation – Matthew Fontana Jan 17 '17 at 22:47
  • Well, that's precisely one of the use-cases where proxy based AOP fails. Using native AspectJ is the answer. I recommend compile-time weaving. – Nándor Előd Fekete Jan 18 '17 at 14:36

0 Answers0