+-
深入aop
首页 专栏 spring 文章详情
5

深入aop

小强Zzz 发布于 2 月 27 日

前言

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

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。然后,该代理类会覆写所有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
阅读 325 更新于 2 月 27 日
赞5 收藏
分享
本作品系原创, 采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议
avatar
小强Zzz
748 声望
15 粉丝
关注作者
0 条评论
得票 时间
提交评论
avatar
小强Zzz
748 声望
15 粉丝
关注作者
宣传栏
目录

前言

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

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。然后,该代理类会覆写所有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的原理。