比如我拦截的方法是foobar()
项目中有很多方法中调用了foobar这个方法,
比如
A method -> foobar
B method -> foobar
C method -> foobar
...
XXX method -> foobar
我在拦截到foobar的时候,怎么获取是哪个方法调用了它?是A还是B还是C还是XXX?
网上目前找到的唯一方式是取堆栈信息里取,但是感觉非常不优雅而且拿堆栈信息的成本似乎比较高,有更好的方式吗?
###经过和题主评论询问,才总算把这个问题真正展示出来了,不然的话,大家都会围绕这foobar
的切面去想
而你的问题或者说你现在实现的一个新业务,最重要的地方其实应该是:
如何处理调用foobar
的方法
也就是说,你这个新业务其实就是处理某些调用foobar
的方法的定制化,而直接分析一下就可以发现你这个新业务中需要处理或者像你说的加注解的方法它们唯一相同点是内部调用了foobar
。
所以它们既是分散的,但是内部却有相同想要统一实现的业务。分散但有需要统一处理的情况我们一般咋做啊,没错,就是切面啊~哈哈哈(想想怎么所有Controller
方法打印日志,做事务管理等)。因此这里理应来说其实就有2个切面。
那真正重要的切面地方其实是去切调用了foobar
的方法,也就是你说A method
,B method
等。切入点也很简单,就是注解咯。
那切面做啥,很简单,就是你想要做的业务,即通知接下来调用foobar
时需要打印消耗时间,这个通知咋通知,不额外添加其他修改的情况下就是ThreadLocal
咯。那可能写出来就是如下效果
@Around(value = "pointcut(xxxAnotation)")
public Object around(ProceedingJoinPoint joinPoint, XXXAnotation xxxAnotation) {
// TODO 在ThreadLocal中塞入Flag,
// 当然你可以从注解xxxAnotation中获取你的配置信息,决定是否要塞入Flag
// 执行方法(这里面执行的时候就会去调用foobar)
Object result = joinPoint.proceed();
// TODO 在ThreadLocal中清除Flag
}
好了,这里Flag
处理完了,再回到foobar
的切面里,这里就很简单啦,直接获取Flag
,然后决定是否要打印消耗时间咯。
综上,其实也许处理问题的观察点切换一下,没准问题反而好解决了(但是描述问题不完整,大家不太容易能想到还要做个切面),最终解决的效果看起来也不是很别扭,也算是很优雅了叭,对以前代码的修改也只是增加了注解,其他地方都是新增代码,但是这也是这个新业务必须的地方,毕竟这个新业务就要去明确调用foobar
的方法,明确的话就一定需要标注了(无论通过什么形式)。
加个参数标示调用方、放个ThreadLocal存调用方
无侵入的貌似只有拿栈往上翻了