My application needs to perform dynamic invocation on Spring bean. I scan a Spring bean for methods annotated with a custom annotation and store the reference to the Method
object for future invocation.
public Method getMethod(Class<?> clazz, final String name) throws ReflectiveOperationException, NoSuchMethodException
{
Method meth = null;
for (Method m : clazz.getMethods())
{
String alias;
WorkflowMethod annotation = m.getAnnotation(WorkflowMethod.class);
if (annotation == null)
{
log.warn(...);
continue;
}
alias = annotation.alias();
if (StringUtils.isEmpty(alias))
alias = m.getName();
if (!name.equals(alias))
continue;
if (meth != null)
throw new Exception(...);
meth = m;
}
if (meth == null)
throw new NoSuchMethodException(...);
return meth;
}
The above code will extract a method by name according to the fact it has no overloads (by requirement).
However, when I later in the code try to invoke meth.invoke(springBean,params)
I get the InvocationTargetException
descripted in the title.
All my beans are AOP proxies because I use Spring Transactions.
I obtain clazz
variable with following code, because AOP proxy don't show annotations from source class
Class<?> managerClass;
if (workflowManager instanceof TargetClassAware)
managerClass = ((TargetClassAware) workflowManager).getTargetClass();
else
managerClass = workflowManager.getClass();
Summarizing, I am required to use TargetClassAware
because otherwise I wouldn't be able to scan for annotations, but if I get a method from the original class it will not be compatible with the proxy class.
How to invoke the method correctly?
While I was writing the question I found the answer myself.
My thought was right: I cannot scan the AOP proxy otherwise I can't get annotations, but then I have to pick the method not from the original class but from the proxy itself.
The approach is the following: scan the target class for annotated methods, but then pick the identical method (same name and parameters) from the proxy class. The code can be modified in two ways, here is one:
public Method getMethod(Class<?> targetClass, Class<?> proxyClass, final String name) throws ReflectiveOperationException, NoSuchMethodException
{
Method meth = null;
for (Method m : targetClass.getMethods())
{
String alias;
WorkflowMethod annotation = m.getAnnotation(WorkflowMethod.class);
if (annotation == null)
{
log.warn("...);
continue;
}
alias = annotation.alias();
if (StringUtils.isEmpty(alias))
alias = m.getName();
if (!name.equals(alias))
continue;
if (meth != null)
throw new Exception(...);
if (proxyClass == null)
meth = m;
else
meth = proxyClass.getMethod(m.getName(), m.getParameterTypes());
}
if (meth == null)
throw new NoSuchMethodException(...);
return meth;
}
The code can also be modified to check for TargetClassAware
from within getMethod
, without the need of using 2 parameters