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:
- Scan all beans
- Look for methods annotated with
@EventConsumer
- Add them to registry
- 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:
- Reflection + AOP - advice not intercepted when calling method via invoke
- Spring AOP Target Object Aspect not being called when target object is accessed via reflection
- Spring AOP vs AspectJ
- Spring AOP - Access to Repositories autowired field by reflection
- How to invoke method on proxy(Spring AOP) by reflection in Java?