Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效)

Posted 喜东东

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效)相关的知识,希望对你有一定的参考价值。

今天在使用Spring cache的Cacheable注解的过程中遇见了一个Cacheable注解失效的问题,检查问题发生的原因是因为Spring的Cacheable注解是基于Spring AOP实现的,但是类内部方法互相调用时不会被Spring AOP拦截的,所以导致被调用方法的Cacheable注解失效,特此记录。

问题复现

@Service
public class UserServiceImpl{
    @Override
    public User detail(Long id) {
    
        // 校验信息
        if (id == null || id == 0) {
            return ApiResult.instance().fail(UserResultEnum.USER_ID_NULL);
        }
        User user = this.selectById(id);
        if (user == null) {
            return ApiResult.instance().fail(UserResultEnum.USER_NULL);
        }
        return user;
    }
  
    @Override
    @Cacheable(value = "user",condition = "#id != null", key = "‘user‘.concat(#id.toString())")
    public User selectById(Serializable id){
        return super.selectById(id);
    }
}

上述代码在使用this.selectById的时候Cacheable注解是无效的,解决办法如下:

  • 写一个工具类SpringContextUtil实现ApplicationContextAware接口
public class SpringContextUtil implements ApplicationContextAware {
    private static ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext){
        SpringContextUtil.applicationContext = applicationContext;
    }
    
    public static ApplicationContext getApplicationContext(){
        return applicationContext;
    }
    
    public static Object getBean(Class var1) throws BeansException {
        return applicationContext.getBean(var1);
    }
}
  • 在Spring Boot启动的时候将ApplicationContext注入SpringContextUtil
public class AuthServiceApplication {

    public static void main(String[] args) {
        SpringContextUtil springContextUtil = new SpringContextUtil();
        ApplicationContext applicationContext = SpringApplication.run(AuthServiceApplication.class, args);
        springContextUtil.setApplicationContext(applicationContext);
    }
}
  • 在UserServiceImpl方法中使用
@Service
public class UserServiceImpl{
    @Override
    public User detail(Long id) {
    
        // 校验信息
        if (id == null || id == 0) {
            return ApiResult.instance().fail(UserResultEnum.USER_ID_NULL);
        }
        //注入当前Bean使得调用内部方法也被SpringAOP拦截
        IUserService userService = (IUserService) SpringContextUtil.getBean(this.getClass());
        User user = userService.selectById(id);
        if (user == null) {
            return ApiResult.instance().fail(UserResultEnum.USER_NULL);
        }
        return user;
    }
  
    @Override
    @Cacheable(value = "user",condition = "#id != null", key = "‘user‘.concat(#id.toString())")
    public User selectById(Serializable id){
        return super.selectById(id);
    }
}

这样就可以解决Bean内部方法调用不被Spring AOP拦截的问题

以上是关于Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效)的主要内容,如果未能解决你的问题,请参考以下文章

不可见类有抽象父类,spring配置子类bean,注入父类,aop就可以切父类的方法

Spring AOP为啥不能拦截从对象内部调用的方法

Spring框架系列 - Spring AOP实现原理详解之AOP切面的实现

Spring AOP不拦截从对象内部调用的方法

Spring AOP不拦截从对象内部调用的方法原因

Spring AOP不拦截从对象内部调用的方法原因