记一次AOP踩的坑(注解方式AOP不生效) 置顶!
最近接了一个需求,使用配置的优惠券/项目卡生成一批券码.用户在H5端,自己填手机号绑定券码.然后后台将券码对应的优惠券/项目卡发放到用户的账户中.发放成功短信通知用户.
发送短信的方式,我采用的是基于注解的AOP 后置通知 .具体代码如下
import java.lang.annotation.*;
/**
* @author gaotianyi
* @date 2019/9/10 3:40 下午
*/
@Target({ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SendSms {
}
具体的AOP
/**
* @author gaotianyi
* @date 2019/9/10 3:35 下午
*/
@Slf4j
@Aspect
@Component
public class SendSmsConfig {
@Autowired
private RemoteSMSService remoteSMSService;
@Pointcut("@annotation(com.myour.marketing.core.anno.SendSms)")
public void pointCutReturn() {
}
/**
* 切面
*
* @param
* @return
* @throws Throwable
*/
@AfterReturning(value = "pointCutReturn()",returning = "param")
public void methodHandler(Object param) {
SendSMSParam param1 = (SendSMSParam) param;
String phoneNumbers = param1.getPhoneNumbers();
if (StringUtils.isNotBlank(phoneNumbers)) {
this.sendMessage(param1);
} else {
log.info(">>>>>>>>>>>邀请人没有手机号,不发送短信通知 :{}<<<<<<<<<<<<<<", param);
}
}
/**
* 发送短信
*
* @param param
*/
public void sendMessage(SendSMSParam param) {
// code
}
调用的地方伪代码
public interface A {
String A(C c );
SendSMSParam B( . .);
}
我在 A方法中调用了B方法 ,B方法上注解了 @SendSms注解被定义成了一个切点 .但是测试的时候成功领取券码,却没有收到短信通知 .于是debug查看,发现该方法注解未生效 .检查了 Pointcut表达式没有问题,百思不得其姐,于是乎百度
失效原因 :
因为我们的AOP走了类中自调用 ,那么为什么自调用不会生效了?
在使用 Spring AOP 的时候,我们从 IOC 容器中获取的 Service Bean 对象其实都是代理对象,而不是那些 Service Bean 对象本身,也就是说获取的并不是被代理对象或代理目标当我在自己的 Service 类中使用 this 关键字嵌套调用同类中的其他方法时,由于 this 关键字引用的并不是该 Service Bean 对象的代理对象,而是其本身,故 Spring AOP 是不能拦截到这些被嵌套调用的方法的
我们知道aop都是经过动态代理来实现的,而动态代理管理的范围也一定是spring容器中的对象所调用的方法,即autowired进来的对象调用方法时,实际调用的是代理方法,然后代理方法里面会调用该类自身方法
针对this直接调用的方法会直接调用该代理对象的方法,这个this就是该类直接调用自己方法,所以不会在经过aop
解决办法
就是将被注解的那个方法放在其他类中进行(最推崇的方式)
总结
(1).在一个类内部调用时,被调用方法的 AOP 声明将不起作用。Spring 事务管理注解 @Transactional 也一样。
(2).对于基于接口动态代理的 AOP 事务增强来说,由于接口的方法都必然是 public 的,这就要求实现类的实现方法也必须是 public 的(不能是 protected、private 等),同时不能使用 static 的修饰符。所以,可以实施接口动态代理的方法只能是使用 public 或 public final 修饰符的方法,其他方法不可能被动态代理,相应的也就不能实施 AOP 增强,换句话说,即不能进行 Spring 事务增强了。
(3).基于 CGLib 字节码动态代理的方案是通过扩展被增强类,动态创建其子类的方式进行 AOP 增强植入的。由于使用 final、static、private 修饰符的方法都不能被子类覆盖,相应的,这些方法将无法实施 AOP 增强。所以方法签名必须特别注意这些修饰符的使用,以免使方法不小心成为事务管理的漏网之鱼。
(4).上面我的例子的方法符合上述条件,但注解仍然失效,主要原因是在于同一类中的方法互相调用,调用者指向当前对象,所以无论是接口代理还是 cglib 代理都无法织入增强实现。
谢谢以下几位大佬给的参考
探究Spring AOP 同类调用失效问题
aop三个坑:失效、注入对象为空、方法固定返回null
标题:记一次AOP踩的坑(注解方式AOP不生效)
作者:yifabukeshoushi
地址:https://blog.tyxgg.cn/articles/2019/12/03/1575337704737.html