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

2023-07-11 19:47:53 +08:00
 ak1ak

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 循环依赖。

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

1664 次点击
所在节点    程序员
18 条回复
BBCCBB
2023-07-11 20:50:23 +08:00
@Resource
@Lazy
private AppConfig config;

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


使用方
@Autowired
AppAutoConfiguraion appAutoConfiguraion;
cppc
2023-07-12 10:57:48 +08:00
感觉遗漏了信息,上个 demo 看看
ak1ak
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
2023-07-12 12:21:23 +08:00
AppConfig 加 @Component ,然后注入使用;
不加的话,就得用 @EnableConfigurationProperties 注入
YCNQc647Cfngdp89
2023-07-12 12:57:42 +08:00
改成 buildAppBiz(AppConfig config),把 resource 注解的 bean 删掉
258
2023-07-12 13:22:04 +08:00
这个没有循环依赖呀
running17
2023-07-12 15:11:35 +08:00
单从代码看不出来有问题,建议把报错信息脱敏一下放上来看看
ak1ak
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
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
2023-07-12 19:08:29 +08:00
@wanghaoxu 你这种方式我没有试过,不过理论上提供一个额外的 PropertySource 似乎和直接在 application.properties 中指定没有区别 (逃。我看了下 jasypt-spring-boot GitHub 上的文档中处理自身配置的说明,是根据一个 EncryptablePropertyFilter 实例来过滤掉特定配置避免循环依赖的问题的。所以我仿照它的写法,提供一个同名的 Bean ,然后用 `@Lazy` + 构造器注入的方式引入 AppConfig 类,问题解决。感谢各位的建议!( Peace~

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/955949

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX