2015-07-20 421 views
4

接口:如何通过Java中的反射调用代理(Spring AOP)方法?

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

实现该接口的类:

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

为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(); 
    } 
} 

控制器:

@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); 
    } 
} 

因此,当spring注入管理器变量创建控制器代理时。
当方法直接援引:

manager.read(1L) 

方面被调用。

然而,当我尝试做这样的(见readViaReflection

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

得到java.lang.reflect.InvocationTargetException对象不是声明类的一个实例。
这是合理的。

的问题是:如何可以通过由弹簧创建代理对象上反射调用方法(I有方法从目标对象中提取和我有代理通过创建弹簧的实例)。

无法对目标执行调用,因为此时aspect不会调用。

+0

为什么你需要这个丑陋的装置。我需要调用方法,而不是在接口中做错事情或者接口错误。如果你真的想这样做(我强烈建议不要),你需要切换到基于类的代理,而不是基于接口的代理。 –

回答

0

您必须从代理的类中调用该方法。试试这个:

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

+0

在创建帖子之前尝试了此操作。 结果:它抛出java.lang.NoSuchMethodException:java.lang.Class.read(java.lang.Long) – Radon

+0

这工作在我做了一个测试。代理类应该有接口方法。奇怪的是,它就像试图在错误的地方寻找方法('java.lang.Class')。你真的使用'manager.getClass()'? –

+0

是的,这真的是经理。结果: 的className = com.sun.proxy $ Proxy46 方法:71项 接口: 接口local.aspect.manager.Manager 接口org.springframework.aop.SpringProxy 接口org.springframework.aop.framework (公共抽象java.lang.Long manager.Manager.read(java.lang.Long)“ 所以,Proxy没有读取方法(java.lang.Long),但已经一个实现read(java.lang.Long)的接口管理器结束此方法可以通过从接口反射来提取。 而且此方法不能在代理上调用。 Reale不是tipical。 – Radon

0

我不认为Java反射会做行了,你需要使用if() pointcut expression

要做到实现它,你可以定义另一个boolean变量(名为invokeAOP),当你用invokeAOP = true调用manager,那么你会得到你的Aspect。否则你的方面将被省略。

0

你可以不用使用反射 - 它只是需要一些铸造:

((Manager) ((Advised)manager).getTargetSource().getTarget()).read(1L);

很酷的事情是,它与JDK和CGLIB代理。

如果必须使用反射只需使用该解决方案的一部分:

Manager managerBean = ((Manager) ((Advised)manager).getTargetSource().getTarget()); managerBean.getClass().getMethod("read", Long.class).invoke(managerBean, id)

+0

问题是我不能使用这种方法,因为方法名称(如“读取”)在应用程序运行时可用。此方法读取显示为示例。 – Radon

+0

@Radon检查编辑答案。 –

+0

这存在绕过代理添加的任何附加功能的问题。 –

1

正如你已经注意到了,你不能调用ManagerImpl的方法对bean,东阳这个Bean实际上是由一个实现代理。

对我来说,解决方案是获取代理的调用处理程序并调用该方法。

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); 

else部分是必要时Bean是不是代理,但无论是裸ManagerImpl类或cglib代理类(这将在你的情况下继承ManagerImpl)。