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

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

  •  
  •   ak1ak · 2023-07-11 19:47:53 +08:00 · 1644 次点击
    这是一个创建于 502 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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 条附言  ·  2023-07-12 11:28:30 +08:00
    18 条回复    2023-07-12 19:08:29 +08:00
    BBCCBB
        1
    BBCCBB  
       2023-07-11 20:50:23 +08:00
    @Resource
    @Lazy
    private AppConfig config;

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


    使用方
    @Autowired
    AppAutoConfiguraion appAutoConfiguraion;
    cppc
        10
    cppc  
       2023-07-12 10:57:48 +08:00
    感觉遗漏了信息,上个 demo 看看
    ak1ak
        11
    ak1ak  
    OP
       2023-07-12 11:25:26 +08:00
    @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  
       2023-07-12 12:21:23 +08:00
    AppConfig 加 @Component ,然后注入使用;
    不加的话,就得用 @EnableConfigurationProperties 注入
    YCNQc647Cfngdp89
        13
    YCNQc647Cfngdp89  
       2023-07-12 12:57:42 +08:00
    改成 buildAppBiz(AppConfig config),把 resource 注解的 bean 删掉
    258
        14
    258  
       2023-07-12 13:22:04 +08:00
    这个没有循环依赖呀
    running17
        15
    running17  
       2023-07-12 15:11:35 +08:00
    单从代码看不出来有问题,建议把报错信息脱敏一下放上来看看
    ak1ak
        16
    ak1ak  
    OP
       2023-07-12 15:55:00 +08:00
    @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  
       2023-07-12 16:42:00 +08:00
    简单调试了一下,循环依赖大概是这个问题
    encryptablePropertyResolver 依赖 config
    config 依赖 application.properties 文件,application.properties 文件依赖你用的那个加解密组件,
    加密组件又依赖 properties 文件

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