Spring's Bean internal method call cannot use AOP facet (CacheAble annotation is invalid)

Keywords: Java Spring

Spring's Bean internal method call cannot use AOP facet (CacheAble annotation is invalid)

Preface

Today, in the process of using Spring cache's Cacheable annotation, we encountered a problem of invalidation of Cacheable annotation. The reason for checking the problem is that Spring's Cacheable annotation is implemented based on Spring AOP, but when methods inside the class call each other, they will not be blocked by Spring AOP, so the Cacheable annotation of the called method is invalid. It is hereby recorded.

Problem recurrence

@Service
public class UserServiceImpl{
    @Override
    public User detail(Long id) {
    
        // Verification information
        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);
    }
}

The Cacheable annotation is invalid when using this.selectById in the above code. The solution is as follows:

  • Write a tool class SpringContextUtil to implement the ApplicationContextAware interface
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);
    }
}
  • Inject ApplicationContext into SpringContextUtil when Spring Boot starts
public class AuthServiceApplication {

    public static void main(String[] args) {
        SpringContextUtil springContextUtil = new SpringContextUtil();
        ApplicationContext applicationContext = SpringApplication.run(AuthServiceApplication.class, args);
        springContextUtil.setApplicationContext(applicationContext);
    }
}
  • Use in UserServiceImpl method
@Service
public class UserServiceImpl{
    @Override
    public User detail(Long id) {
    
        // Verification information
        if (id == null || id == 0) {
            return ApiResult.instance().fail(UserResultEnum.USER_ID_NULL);
        }
        //Inject the current Bean so that the internal method of the call is also intercepted by spring AOP
        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);
    }
}

This can solve the problem that the internal method calls of Bean are not blocked by Spring AOP

Posted by Peuplarchie on Fri, 01 May 2020 22:01:28 -0700