Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to invoke method on proxy(Spring AOP) by reflection in Java?

An interface:

public interface Manager {
  Object read(Long id);
}

A class which implements this interface:

@Transactional
Public class ManagerImpl implements Manager {
  @Override  
  public Object read(Long id) {
    //  Implementation here  
  }
}

An aspect for ManagerImpl:

@Aspect
public class Interceptor {
  @Pointcut("execution(public * manager.impl.*.*(..))")
  public void executionAsManager() {
  }

  @Around("executionAsManager()")
  public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
    //  Do some actions
    return joinPoint.proceed();
  }
}

A controller:

@RestController()
public class Controller {

  @Autowired
  private Manager manager;

  @RequestMapping(value = "/{id}", method = RequestMethod.GET)
  public Object read(@PathVariable Long id) {
    return manager.read(id);
  }

  @RequestMapping(value = "reflection/{id}", method = RequestMethod.GET)
  public Object readViaReflection(@PathVariable Long id) {
    return ManagerImpl.class.getMethod("read", Long.class).invoke(manager, id);
  }
}

So, when spring injects manager variable within controller proxy created.
When method invoked directly:

manager.read(1L)  

aspect is invoked.

However, when I try to do like this (see readViaReflection)

ManagerImpl.class.getMethod("read", Long.class).invoke(manager, 1L);

got java.lang.reflect.InvocationTargetException object is not an instance of declaring class.
Which is reasonable.

The question is: how can I invoke method via reflection on proxy-object created by spring (I have method extracted from target-object and I have instance of proxy created by spring).

Can not do invocation on target because then aspect will not invoke.

like image 495
Radon Avatar asked Jul 20 '15 14:07

Radon


2 Answers

As you have noticed, you cannot invoke the method of ManagerImpl on the bean, beacause the Bean is actually implemented by a Proxy.

For me, the solution was to get the invocation handler of the Proxy and call the method.

if (Proxy.isProxyClass(manager.getClass())) {
    Method readMethod = ManagerImpl.class.getMethod("read", Long.class);
    Proxy.getInvocationHandler(manager).invoke(manager, readMethod, parameter);
} else
    info.getMethod().invoke(serviceClass, parameter);

The else part is necessary when the Bean is not a Proxy, but either the bare ManagerImpl class or a CGLib proxy class (which would subclass ManagerImpl in your case).

like image 196
Martin Nyolt Avatar answered Nov 10 '22 01:11

Martin Nyolt


You must invoke the method from the proxy's class. Try this:

manager.getClass().getMethod("read", Long.class).invoke(manager, 1L);

like image 43
Fred Porciúncula Avatar answered Nov 10 '22 01:11

Fred Porciúncula