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

请教大家一个关于 Spring 循环依赖的问题

  •  
  •   ak1ak · 291 天前 · 1428 次点击
    这是一个创建于 291 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Hello, all 。这里请教一个 Spring 循环依赖的问题。

    我定义了三个类:配置类 AppConfig 、业务类 AppBiz 、自动装载类 AppAutoConfiguration 。三个类的简化代码如下所示:

    // AppConfig
    @ConfigurationProperties(prefix="app")
    public class AppConfig 
    {
      private String version = “1.0.0”;
    }
    
    // AppBiz
    public class AppBiz {
      private final AppConfig config;
      public AppBiz(AppConfig config){
        this.config = config;
      }
    }
    
    // AppAutoConfiguration
    @EnableConfigurationProperties(AppConfig.class)
    public class AppAutoConfiguraion{
      
      @Resource
      private AppConfig config;
      
      @Bean
      public AppBiz buildAppBiz(){
        return new AppBiz(config);
      }
    }
    

    当我将这个项目作为一个 jar 包提供给其他系统的时候,希望通过读取配置文件 app.version 自动装载,实现一些逻辑。

    现在的情况是,我不在配置文件里写 app.version 时,项目可以正常启动,自动装配 AppAutoConfiguration ;当我在配置文件中加上 app.version,自动装载配置类就报循环依赖的问题。提示是 AppBiz 和 AppConfig 循环依赖。

    有大哥能看下上面的代码有什么问题吗。

    第 1 条附言  ·  290 天前
    18 条回复    2023-07-12 19:08:29 +08:00
    BBCCBB
        1
    BBCCBB  
       291 天前
    @Resource
    @Lazy
    private AppConfig config;

    加个 @Lazy 试试.
    BBCCBB
        2
    BBCCBB  
       291 天前
    你这只有 AppBiz 依赖 appconfig, appconfig 也没有依赖 appBiz 呀.
    zoharSoul
        3
    zoharSoul  
       291 天前
    构造函数注入改成 @autowire 注入
    ak1ak
        4
    ak1ak  
    OP
       290 天前
    @BBCCBB #1 加上 `@Lazy` 才显示具体的循环依赖信息;不加 `@Lazy` 会提示「创建 AppAutoConfiguration Bean 失败,这个 Bean 中依赖的其他 Bean 正在创建,可能有没有解决的循环依赖 」:-(
    ak1ak
        5
    ak1ak  
    OP
       290 天前
    @zoharSoul #3 你的意思是将 AppBiz 改成一个 `@Component`,然后通过 `@Autowired` 的方式注入 AppConfig 吗。这个应该行不通,我这边想做的是通过在 AppAutoConfiguration 控制是否加载 AppBiz 。也就是 buildAppBiz 方法上会有 `@Conditional` 条件
    retanoj
        6
    retanoj  
       290 天前
    AppConfig 记作 @Component 就可以了吧
    ak1ak
        7
    ak1ak  
    OP
       290 天前
    @retanoj #6 不行的。这个 AppAutoConfiguration 这个类要作为一个自动装配模块提供给其他系统调用,如果通过 `@Component` 进行标记,其他系统要通过 componentScan 扫描这个类路径,和自动装配的初衷不符。理想情况下是在其他系统通过配置文件,条件化地加载 AppAutoConfiguration 中的各个组件。
    fulln
        8
    fulln  
       290 天前
    spring 当处理构造器注入 bean 的时候,会出现这种问题, 你试下成员变量用注解注入 bean
    retanoj
        9
    retanoj  
       290 天前
    @Configuration
    @EnableConfigurationProperties(AppConfig.class)
    class AppAutoConfiguraion{}


    使用方
    @Autowired
    AppAutoConfiguraion appAutoConfiguraion;
    cppc
        10
    cppc  
       290 天前
    感觉遗漏了信息,上个 demo 看看
    ak1ak
        11
    ak1ak  
    OP
       290 天前
    @cppc #10 demo 代码在 https://pastebin.ubuntu.com/p/bcS3Kkq797/。使用的第三方库是 jasypt-spring-boot 。这个库的作用是读取一个 encryptablePropertyResolver 的 Bean 实例之后,通过判断配置的前缀是否是指定前缀,如果是,根据 EncryptablePropertyResolver#resolvePropertyValue 进行解析。(我感觉是这个依赖的问题,EncryptablePropertyResolver 依赖一个 AppConfig 的 Bean ,但是先要初始化一个 EncryptablePropertyResolver 的 Bean 来判断是否需要对 AppConfig 配置进行解密,因此出现了循环依赖。隐约感觉是这个问题,不知道怎么解决。
    ediron
        12
    ediron  
       290 天前
    AppConfig 加 @Component ,然后注入使用;
    不加的话,就得用 @EnableConfigurationProperties 注入
    matony
        13
    matony  
       290 天前
    改成 buildAppBiz(AppConfig config),把 resource 注解的 bean 删掉
    258
        14
    258  
       290 天前
    这个没有循环依赖呀
    running17
        15
    running17  
       290 天前
    单从代码看不出来有问题,建议把报错信息脱敏一下放上来看看
    ak1ak
        16
    ak1ak  
    OP
       290 天前
    @258 @running17 从这个代码的确看不出为什么会有循环依赖,所以可能就是 #11 分析的问题。

    通过 `@Resource` field 注入提示的错误:

    ![setter 注入失败]( https://s3.bmp.ovh/imgs/2023/07/12/35f7b818c71647d5.png)

    通过构造函数或者 `@Resource` + `@Lazy` 注入提示的错误:

    ![构造函数注入失败]( https://s3.bmp.ovh/imgs/2023/07/12/a8994322e6668044.png)
    wanghaoxu
        17
    wanghaoxu  
       290 天前
    简单调试了一下,循环依赖大概是这个问题
    encryptablePropertyResolver 依赖 config
    config 依赖 application.properties 文件,application.properties 文件依赖你用的那个加解密组件,
    加密组件又依赖 properties 文件

    建议新建一个 xxx.propertiesconfig 文件,这里面写初始化加解密组件的配置,让 config 依赖 xxx.properties 文件(使用 @PropertySource("classpath:xxx.properties")指定配置文件),猜测 xxx.propertiesconfig 文件不会依赖那个加密文件,不会参与加解密,试了下没有循环依赖问题了,你可以试试
    ak1ak
        18
    ak1ak  
    OP
       290 天前
    @wanghaoxu 你这种方式我没有试过,不过理论上提供一个额外的 PropertySource 似乎和直接在 application.properties 中指定没有区别 (逃。我看了下 jasypt-spring-boot GitHub 上的文档中处理自身配置的说明,是根据一个 EncryptablePropertyFilter 实例来过滤掉特定配置避免循环依赖的问题的。所以我仿照它的写法,提供一个同名的 Bean ,然后用 `@Lazy` + 构造器注入的方式引入 AppConfig 类,问题解决。感谢各位的建议!( Peace~
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1217 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 17:52 · PVG 01:52 · LAX 10:52 · JFK 13:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.