前言
使用切面一段时间,写个基本切面没有问题。
然而这次使用切面时遇到了问题,发现切面没有执行,找错误又无从找起,只能硬看代码。
大概代码是这样的
publicclassImportExcelServiceImpl{publicvoidreadFromFileAndSetAnswerSheets(){...setAnswerSheet(currentRow);...}@UpdateAnswerSheetTotalNumberpublic AnswerSheetsetAnswerSheet(Row currentRow){...return answerSheet;}}
这是被切方法
@Aspect@ComponentpublicclassUpdateAnswerSheetTotalNumberAspect{@Pointcut("@annotation(....UpdateAnswerSheetTotalNumber)")publicvoidannotationPointCut(){}@AfterReturning(value="annotationPointCut()", returning="answerSheet")publicvoidafter(AnswerSheet answerSheet){...}
这是切面
原因
找了半天找不出来哪里错了,就去问了老师,老师看出了问题所在。
原来我的被切方法是setAnswerSheet(),调用被切方法是通过this.setAnswerSheet()调用的,这就是对象内调用。而切面是基于代理模式,对象内调用方法是不走代理的,当然是不起作用的。
原来写的被切方法都是在一个对象中的方法调用另一个对象中的a方法的情况下。
此时spring会为被调用方法所在对象生成一个代理,此代理拥有与服务相同的方法,如果方法没有被执行切面,则在代理中直接将执行的方法转发给实际的服务,如果有切面,则会在代理中完成切面,这就是切面的原理。
我们在类中打入断点
其中userServiceImpl.frozen使我们的被切方法。注入的类名总是类似UserServiceImpl
E
n
h
a
n
c
e
r
B
y
S
p
r
i
n
g
C
G
L
I
B
EnhancerBySpringCGLIBEnhancerBySpringCGLIB1c76af9d。为了让调用方获得UserServiceImpl的引用,它必须继承自UserServiceImpl。然后,该代理类会覆写所有public和protected方法,并在内部将调用委托给原始的UserServiceImpl实例。~~~~
解决
解决的办法就是自己注入自己
classA{@Autowired
A a;publicvoidtest(){// 这样使用切不到,是对象的内部调用this->setXxx();// 这样用就可以,因为注入的a实际上是a的代理
a->setXxx();}@Xxxxxpublic xxxsetXxx(){}}
而这种依赖注入只能使用@Autowired的形式,不能使用构造函数的形式,构造函数形式会造成依赖注入的死循环。
总结
原来只会用AOP而不懂AOP的原理,以为他就如同@before的作用一样简单,直到遇到问题,才能理解aop的原理。
参考:《2020最新Java基础精讲视频教程和学习路线!》
原文链接:https://segmentfault.com/a/1190000039297484