springBoot Cache + aop self-invocation 问题

查看 42|回复 1
作者:bleulucaswu   

  • SpringBoot:3.0.1 + Java17 + starter-cache + starter-data-redis
  • 简单的 jpa 查询数据,缓存到 redis 中


    @EnableAspectJAutoProxy(exposeProxy = true, proxyTargetClass = true)
    @EnableCaching
    public class StartupApplication { }
    @Service
    public class AccountService {
        @Cacheable(cacheNames = "accountsActivated", sync = true)
        public List retrieveActivatedCacheable() { }
       
        public AccountDto retrieveActivatedByName(String name) {
        // 这里 call retrieveActivatedCacheable() 不会从缓存中查询
        }
  • 原因是 spring aop 实现原理是动态代理,同一个类中调用切点方法,advise 失效

    [ol]

  • aopContext.getCurrentProxy

  • ((AccountService) AopContext.currentProxy()).retrieveActivatedCacheable()
  • java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.
  • exposeProxy = true not in effect · Issue #16516 · spring-projects/spring-boot 跟这个 issue 基本一样,没有解决方案

    [/ol]
    [ol]

  • 两个方法放不同类
  • 最终方案,业务关联性强,放不同类没意义

    [/ol]
    [ol]
  • self-inject 和 Compile-time weaver Invoke Spring @Cacheable from Another Method of Same Bean | Baeldung
    [/ol]
    @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class AccountService
        @Autowired
        private AccountService self;
        @Cacheable(cacheNames = "accountsActivated", sync = true)
        public List retrieveActivatedCacheable() { }
       
        public AccountDto retrieveActivatedByName(String name) {
           self.retrieveActivatedCacheable().....
  • 报错完全搞不懂发生了什么,貌似和 java9 的 modules 有关,


    java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class demo.usul.dto.AccountDto (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; demo.usul.dto.AccountDto is in unnamed module of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @5d339696)

  • 但是我已经设置了 vm options, 或者 spring.devtools.restart.enabled=false 都没用

    --add-opens=java.base/java.time=ALL-UNNAMED
    --add-opens=java.base/java.lang=ALL-UNNAMED
    --add-opens=java.base/java.util=ALL-UNNAMED
  • 关键 self-inject ,或者想办法在自己内部拿到自己 bean 的方法都有点奇怪
  • compile-time weaver 更不推荐,要引入 aspectj 的 weaver 来编译,好像在编译过程多了一个 weaver compiler 的过程,尝试用 aspectj-maven-plugin ,但是搞不定,貌似跟 jdk17 不兼容


  • load-time weaver 使用 spring 提供的 LTW, 不需要引入 aspectj 的 weaver ,貌似是最推荐的方案, spring 原生 LTW
  • 还是失败, startupApplication 上添加注解 @EnableCaching(mode = AdviceMode.ASPECTJ)和 @EnableLoadTimeWeaving

  • 其余不变,vm 参数加上 -javaagent:/path/to/spring-instrument-6.0.13.jar
  • 没用

  • 如果换成 -javaagent:/path/to/aspectjweaver-1.9.19.jar
  • 起不来,看不懂的报错



    weaver, accountdto, public, Java

  • leo97   
    self 加上 spring 的 @Lazy 试试呢?
    private AccountService self;
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部