深入aop

前言

使用切面一段时间,写个基本切面没有问题。
然而这次使用切面时遇到了问题,发现切面没有执行,找错误又无从找起,只能硬看代码。
大概代码是这样的

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()调用的,这就是对象内调用。而切面是基于代理模式,对象内调用方法是不走代理的,当然是不起作用的。
深入aop_第1张图片
原来写的被切方法都是在一个对象中的方法调用另一个对象中的a方法的情况下。
此时spring会为被调用方法所在对象生成一个代理,此代理拥有与服务相同的方法,如果方法没有被执行切面,则在代理中直接将执行的方法转发给实际的服务,如果有切面,则会在代理中完成切面,这就是切面的原理。
我们在类中打入断点
深入aop_第2张图片
其中userServiceImpl.frozen使我们的被切方法。注入的类名总是类似UserServiceImpl$$EnhancerBySpringCGLIB$$1c76af9d。为了让调用方获得UserServiceImpl的引用,它必须继承自UserServiceImpl。然后,该代理类会覆写所有publicprotected方法,并在内部将调用委托给原始的UserServiceImpl实例。~~~~

解决

解决的办法就是自己注入自己

class A {

  @Autowired
  A a;
  
  public void test() {
      // 这样使用切不到,是对象的内部调用
    this->setXxx();
    
    // 这样用就可以,因为注入的a实际上是a的代理
    a->setXxx();
  }
  
  @Xxxxx
  public xxx setXxx() {
  
  }
}

而这种依赖注入只能使用@Autowired的形式,不能使用构造函数的形式,构造函数形式会造成依赖注入的死循环。

总结

原来只会用AOP而不懂AOP的原理,以为他就如同@before的作用一样简单,直到遇到问题,才能理解aop的原理。

你可能感兴趣的:(spring)