Search code examples
javaspringdynamic-proxy

Handling AOP reflective proxies: "object is not an instance of declaring class"


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?


Solution

  • 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