我正在定义条件,稍后我将检查这些条件以动态加载服务接口的两个实现之一。
@Component
public class IsPolicyEnabled implements Condition {
@Autowired
private MyProperties props;
@Override
public boolean matches(ConditionContext arg0, AnnotatedTypeMetadata arg1) {
return props.isPolicyEnabled();
}
}
和
@Component
public class MyProperties {...}
和
@Service
@Conditional(IsPolicyEnabled.class)
public class ServiceA implements Service {...}
但是,我遇到了运行时错误。
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: java.lang.NullPointerException
at com.xyz.utils.IsPolicyEnabled.matches(IsPolicyEnabled.java:9)
at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:108)
at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:88)
at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:71)
at org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.isConditionMatch(ClassPathScanningCandidateComponentProvider.java:515)
基本上,它无法初始化在条件实现中自动连接的 props 对象。这是不允许的吗?
如何在条件实现中自动连接另一个依赖项,因为我的条件评估依赖于该依赖项提供的值?
注册 bean 定义之前立即检查条件 [...]
Condition
, Spring Framework 5.0.8.RELEASE API 文档
您不能将 Bean 注入Condition
实例中,因为上下文中还没有 Bean 定义1。
此外,您不应该在Condition
类中使用 bean:
条件必须遵循与
BeanFactoryPostProcessor
相同的限制,并注意永远不要与 Bean 实例交互。
Condition
, Spring Framework 5.0.8.RELEASE API 文档
您应该重新考虑设计,因为
[...]我的条件评估取决于该依赖关系提供的值。
表示不太对劲。
1准确地说,春天已经注册了一些豆子来满足自己的需要。
有两个问题:
1( 条件类没有注入
解决方案是从 ConditionContext 中检索 bean:
@Component
public class IsPolicyEnabled implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MyProperties props = context.getBeanFactory().getBean(MyProperties.class);
return props.isPolicyEnabled();
}
}
2( 条件初始化发生得很早
尝试从 ConditionContext 检索 bean 失败并显示NoSuchBeanDefinitionException: No qualifying bean of type ...
,因为条件检查发生在 Spring 生命周期的早期。
一个解决方案是有两个 Spring 上下文:
- 仅定义
MyProperties
的父上下文 - 一个子上下文,它定义应用程序的其余部分,并将 parentContext 作为其父级
因此,当调用条件时,MyProperties
已经在父上下文中创建:
ApplicationContext parentContext = new AnnotationConfigApplicationContext(MyProperties.class);
ApplicationContext childContext = new AnnotationConfigApplicationContext();
childContext.setParent(parent);
childContext.register(ApplicationConfiguration.class);
childContext.refresh();
Service service = childContext.getBean(Service.class);
// do something with service