前言
使用切面一段时间,写个基本切面没有问题。
然而这次使用切面时遇到了问题,发现切面没有执行,找错误又无从找起,只能硬看代码。
大概代码是这样的
public class ImportExcelServiceImpl {
public void readFromFileAndSetAnswerSheets() {
...
setAnswerSheet(currentRow);
...
}
@UpdateAnswerSheetTotalNumber
public AnswerSheet setAnswerSheet(Row currentRow) {
...
return answerSheet;
}
}
这是被切方法
@Aspect
@Component
public class UpdateAnswerSheetTotalNumberAspect {
@Pointcut("@annotation(....UpdateAnswerSheetTotalNumber)")
public void annotationPointCut() {
}
@AfterReturning(value = "annotationPointCut()", returning = "answerSheet")
public void after(AnswerSheet answerSheet) {
...
}
这是切面
原因
找了半天找不出来哪里错了,就去问了老师,老师看出了问题所在。
原来我的被切方法是setAnswerSheet()
,调用被切方法是通过this.setAnswerSheet()
调用的,这就是对象内调用。而切面是基于代理模式,对象内调用方法是不走代理的,当然是不起作用的。
原来写的被切方法都是在一个对象中的方法调用另一个对象中的a方法的情况下。
此时spring会为被调用方法所在对象生成一个代理,此代理拥有与服务相同的方法,如果方法没有被执行切面,则在代理中直接将执行的方法转发给实际的服务,如果有切面,则会在代理中完成切面,这就是切面的原理。
我们在类中打入断点
其中userServiceImpl.frozen使我们的被切方法。注入的类名总是类似UserServiceImpl$$EnhancerBySpringCGLIB$$1c76af9d
。为了让调用方获得UserServiceImpl
的引用,它必须继承自UserServiceImpl
。然后,该代理类会覆写所有public
和protected
方法,并在内部将调用委托给原始的UserServiceImpl
实例。~~~~
解决
解决的办法就是自己注入自己
class A {
@Autowired
A a;
public void test() {
// 这样使用切不到,是对象的内部调用
this->setXxx();
// 这样用就可以,因为注入的a实际上是a的代理
a->setXxx();
}
@Xxxxx
public xxx setXxx() {
}
}
而这种依赖注入只能使用@Autowired的形式,不能使用构造函数的形式,构造函数形式会造成依赖注入的死循环。
总结
原来只会用AOP而不懂AOP的原理,以为他就如同@before的作用一样简单,直到遇到问题,才能理解aop的原理。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。