使用AnnotationConfigApplicationContext时配置属性未加载及上下文初始化器生效问题
看起来你踩了原生Spring容器和Spring Boot容器的功能差异坑——直接用AnnotationConfigApplicationContext不会自动启用Spring Boot那套开箱即用的配置机制,咱们一步步来解决这两个问题:
一、配置属性未加载的原因与解决方法
你用SpringApplicationBuilder启动MyTestApplication时,Spring Boot会默默帮你做一堆幕后工作:比如自动加载application.yml到环境中、启用@ConfigurationProperties的绑定处理器、注册相关自动配置后置处理器等等。但AnnotationConfigApplicationContext是原生Spring的容器,它完全不知道这些Spring Boot特有的功能,所以MyConfiguration虽然能被实例化,但属性根本不会被自动绑定。
这里有两种靠谱的解决思路:
方法1:借助SpringApplication构建上下文(推荐)
这种方式最贴近你原来的启动逻辑,能100%复用原有上下文的所有配置,还能直接拿到AnnotationConfigApplicationContext实例:
// 完全复用原来的启动逻辑,包括WebApplicationType和初始化器 SpringApplication app = new SpringApplication(MyTestApplication.class); app.setWebApplicationType(WebApplicationType.NONE); app.addInitializers(new ImportantContextInitializer()); // 启动后获取上下文 AnnotationConfigApplicationContext ctx = (AnnotationConfigApplicationContext) app.run(); // 用完记得关闭上下文 // ctx.close();
方法2:手动给原生容器补全Spring Boot特性
如果你一定要纯手动使用AnnotationConfigApplicationContext,得手动补上Spring Boot帮你做的三件事:
- 把
application.yml加载到容器环境中 - 注册处理
@ConfigurationProperties绑定的核心后置处理器 - 确保你的配置类被正确注册
代码示例:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); // 1. 加载application.yml到容器环境 YamlPropertySourceLoader loader = new YamlPropertySourceLoader(); Resource resource = new ClassPathResource("application.yml"); try { PropertySource<?> propertySource = loader.load("applicationYml", resource).get(0); ctx.getEnvironment().getPropertySources().addLast(propertySource); } catch (IOException e) { throw new RuntimeException("加载application.yml失败", e); } // 2. 注册Spring Boot配置属性绑定的核心处理器 ctx.register(ConfigurationPropertiesBindingPostProcessorRegistrar.class); // 3. 注册你的主配置类(MyTestApplication或MyServicesConfiguration都可以) ctx.register(MyTestApplication.class); // 后续处理上下文初始化器后再refresh
二、让ImportantContextInitializer生效的方法
原生AnnotationConfigApplicationContext不会自动识别Spring Boot的ApplicationContextInitializer,你需要在refresh()之前手动把它添加到容器的初始化器列表中:
// 在refresh前添加初始化器 ctx.addApplicationContextInitializer(new ImportantContextInitializer()); // 然后执行上下文刷新 ctx.refresh();
如果你用上面的方法1(借助SpringApplication),直接调用app.addInitializers()就能和原来的启动逻辑完全一致,不用手动处理。
完整手动实现的示例代码
把两个问题的解决逻辑整合到一起,完整的手动实现代码如下:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); // 加载application.yml到环境 YamlPropertySourceLoader loader = new YamlPropertySourceLoader(); Resource resource = new ClassPathResource("application.yml"); try { PropertySource<?> propertySource = loader.load("applicationYml", resource).get(0); ctx.getEnvironment().getPropertySources().addLast(propertySource); } catch (IOException e) { throw new RuntimeException("加载application.yml失败", e); } // 注册配置属性绑定处理器 ctx.register(ConfigurationPropertiesBindingPostProcessorRegistrar.class); // 添加上下文初始化器 ctx.addApplicationContextInitializer(new ImportantContextInitializer()); // 注册主配置类 ctx.register(MyTestApplication.class); // 刷新上下文 ctx.refresh();
最后提个小建议:如果不是业务必须要用原生AnnotationConfigApplicationContext,优先用方法1的SpringApplication方式,它能帮你避免手动补全特性时遗漏细节(比如Spring Boot的环境后置处理器、配置属性校验器等),减少踩坑概率。
内容来源于stack exchange

