How to invoke spring bean method with all proxies/aspects?

Viewed 512

Java: 11, Spring-boot: 2.5.3

I am trying to implement some annotation-based abstraction on event processing. I have created annotation @EventConsumer and I get all methods annotated with it after context is up. The problem is when I invoke such method, it does not respect any spring proxies/aspects, i.e. @Transactional, @Cacheable, @NewSpan, etc...

Here is code how I get those methods:

    private void processBean(final String beanName, final Object bean, final Class<?> targetType) {
        if (!this.nonAnnotatedClasses.contains(targetType)) {

            final MethodIntrospector.MetadataLookup<EventConsumer> metadataLookup =
                    method -> AnnotatedElementUtils.findMergedAnnotation(method, EventConsumer.class);

            Map<Method, EventConsumer> annotatedMethods = MethodIntrospector.selectMethods(targetType, metadataLookup);

            if (MapUtils.isEmpty(annotatedMethods)) {
                this.nonAnnotatedClasses.add(targetType);
                logger.debug("No @EventConsumer annotations found on bean class: {}", targetType.getName());
            } else {
                for (Method method : annotatedMethods.keySet()) {
                    Method methodToUse = AopUtils.selectInvocableMethod(method, targetType);
                    final EventConsumer eventConsumerAnnotation = annotatedMethods.get(methodToUse);

                    getProxiedMethod(bean, methodToUse).ifPresent(proxiedMethod -> {
                        MethodInvokingEventConsumer consumer = methodInvokingEventConsumerFactory.create(bean, proxiedMethod, eventConsumerAnnotation);
                        eventConsumerRegistry.register(consumer);
                    });
                }
                logger.info("{} @EventConsumer methods processed on bean '{}': {}", annotatedMethods.size(), beanName, annotatedMethods);
            }
        }
    }

    private Optional<Method> getProxiedMethod(Object bean, Method methodToUse) {
        try {
            return Optional.of(bean.getClass().getDeclaredMethod(methodToUse.getName(), methodToUse.getParameterTypes()));
        } catch (NoSuchMethodException e) {
            logger.error("Class {} do not contains method {}, which was marked as @EventConsumer. This consumer will not be active!", methodToUse.getDeclaringClass(), methodToUse.getName(), e);
            return Optional.empty();
        }
    }

And invocation part:

    public EventConsumptionFailures consume(Object obj) {
        ReflectionUtils.makeAccessible(methodToUse);
        Object[] args = new Object[]{obj};
        Span span = getSpan(obj);
        try (Tracer.SpanInScope ignore = tracing.tracer().withSpanInScope(span.name(name).start())) {
            return (EventConsumptionFailures) methodToUse.invoke(bean, args);
        } catch (IllegalArgumentException | IllegalAccessException ex) {
            span.error(ex);
            throw new IllegalStateException(ex.getMessage(), ex);
        } catch (InvocationTargetException ex) {
            Throwable targetException = ex.getTargetException();
            span.error(targetException);
            if (targetException instanceof RuntimeException) {
                throw (RuntimeException) targetException;
            } else {
                throw new UndeclaredThrowableException(targetException, targetException.getMessage());
            }
        } finally {
            span.finish();
        }
    }

the question is how to get method reference with all proxies and aspects? so i can benefit from annotation configuration.


edit I want to achieve smth similar to org.springframework.kafka.config.MethodKafkaListenerEndpoint, so:

  1. Scan all beans
  2. Look for methods annotated with @EventConsumer
  3. Add them to registry
  4. Invoke the Method but with all aspects, e.g. @Transactional, @Cacheable

so far I have points 1-3, but when I invoke method, it does not create transaction.


edit

other words:

@Service 
class SomeService { @Transactional void methodA(){}; }

...
@Autowired SomeService someService


someService.methodA(); -> this will create transaction
SomeService.class.getDeclaredMethod("methodA").invoke(someService); -> this will not create transaction

question: how to make 2nd approach to respect aspects and all proxies?

--- edit 4 I dont know, when exactly it stopped working, but the method getProxiedMethod was already fixing this issue in past. In the mean time we had many library (including spring-boot) upgrades as well as java upgrade (8 -> 11)

here is list of related questions, but all of them do not work for me:

0 Answers
Related