V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
bleulucaswu
V2EX  ›  Java

springBoot Cache + aop self-invocation 问题

  •  
  •   bleulucaswu · 2023-10-17 04:31:36 +08:00 · 1479 次点击
    这是一个创建于 395 天前的主题,其中的信息可能已经有所发展或是发生改变。
    • 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<AccountDto> retrieveActivatedCacheable() { }
        
        public AccountDto retrieveActivatedByName(String name) { 
        // 这里 call retrieveActivatedCacheable() 不会从缓存中查询
        }
    
    • 原因是 spring aop 实现原理是动态代理,同一个类中调用切点方法,advise 失效

    1. 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 基本一样,没有解决方案

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

    1. self-inject 和 Compile-time weaver Invoke Spring @Cacheable from Another Method of Same Bean | Baeldung
    @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) 
    public class AccountService
        @Autowired
        private AccountService self;
    
        @Cacheable(cacheNames = "accountsActivated", sync = true)
        public List<AccountDto> 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
        • 起不来,看不懂的报错
    8 条回复    2023-10-17 11:15:30 +08:00
    leo97
        1
    leo97  
       2023-10-17 04:54:41 +08:00 via Android
    self 加上 spring 的 @Lazy 试试呢?

    private AccountService self;
    wenbinwu
        2
    wenbinwu  
       2023-10-17 05:04:09 +08:00   ❤️ 1
    > 最终方案,业务关联性强,放不同类没意义
    虽然这么说,可整一个 CachedAccountService 放所有 cached 方法不就完了
    搁这事死磕浪费时间干啥
    wenbinwu
        3
    wenbinwu  
       2023-10-17 05:23:12 +08:00
    关于 aspectj-maven-plugin , 用 dev.aspectj 的 https://github.com/dev-aspectj/aspectj-maven-plugin#history
    xuanbg
        4
    xuanbg  
       2023-10-17 07:05:41 +08:00
    class java.util.LinkedHashMap cannot be cast to class
    zjp
        5
    zjp  
       2023-10-17 08:55:44 +08:00 via Android
    自己注入自己,或者从容器手动获取自己都可以,真没必要死磕
    jakes
        6
    jakes  
       2023-10-17 09:00:29 +08:00 via iPhone
    同意二楼。这是最简单最常用的方法,死磕范式没什么意义。
    banmuyutian
        7
    banmuyutian  
       2023-10-17 09:47:12 +08:00
    self 加个 @Lazy 就可以了,再不行从 SpringContext 里拿
    bleulucaswu
        8
    bleulucaswu  
    OP
       2023-10-17 11:15:30 +08:00
    @wenbinwu 试过这个 plugin ,花半天搞定了跟 lombok 兼容问题,找了个能支持 java17 的 aspectjrt runtime, 跑起来了,没用,不知道问题在哪... 所以我觉得这种 compile-time weaver 应该是最不推荐的方案

    因为有空,就死磕了一下,没法子了,这问题超出我的能力范围了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2058 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 16:19 · PVG 00:19 · LAX 08:19 · JFK 11:19
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.