我正在尝试在Spring Boot中动态注册Bean,但是,如果尝试自动装配动态Bean之一,则创建豆的顺序始终会导致NoSuchBeanDefinitionException
。
我的设置包括两个项目,一个是spring-boot-starter项目,另一个是实际的spring-boot应用程序。
实际的应用程序注册了一个BeanDefinitionRegistryPostProcessor
来添加bean定义。实例本身是通过在启动程序项目中定义的另一个bean构造的,该实例本身将另一个bean作为依赖项。
为了使用动态注册的bean,我创建了一个用@Component
注释的类,并定义了一个构造函数,期望将所述bean作为参数。
通过设置@Autowired(required=false)
调试应用程序时,可以看到在创建动态bean之前调用了组件的构造函数。而且,那时甚至还没有创建工厂bean。
在组件中添加了@DependsOn
并带有工厂bean的名称,导致首先创建工厂,而不是动态bean。
使用动态bean的名称设置@DependsOn
是可行的,但这似乎并不是解决此问题的正确方法。
为什么Spring会以错误的顺序创建我的bean,我该怎么解决?
编辑:
我能够在示例存储库中重现该问题:
https://github.com/maveeee/spring-dynamic-bean-demo/
答案 0 :(得分:1)
您可以使用@Order
注释来定义带注释的组件或bean的排序顺序。
请注意,在Spring 4.0之前,此注释仅用于AspectJ执行顺序。在Spring 4.0之后,它支持将注入的组件排序到集合中。因此,Spring将根据它们的订单值注入相同类型的自动连线的bean。
例如:
interface IBean {
String getName();
}
public class BeanX implements IBean {
public BeanX() {}
@Override
public String getName() {
return "BeanX";
}
}
public class BeanY implements IBean {
public BeanY() {}
@Override
public String getName() {
return "BeanY";
}
}
@Component
public class RandomComponent {
@Autowired
private List<IBean> beans;
@PostConstruct
public void getBeanValues() {
System.out.println("\n---@Bean---\n");
for (IBean b : beans) {
System.out.println(b.getName());
}
}
@Bean
@Order(1)
public IBean getBeanX() {
return new BeanX();
}
@Bean
@Order(0)
public IBean getBeanY() {
return new BeanY();
}
}
将打印:
---@Bean---
BeanY
BeanX
因为BeanY
的优先级较高(0
,优先级较低)比BeanX
(优先级较高的1
)优先级高。
相关文章:
答案 1 :(得分:0)
我发现我的问题是由我如何创建bean定义引起的。我使用的是GenericBeanDefinition
而不是RootBeanDefinition
。使用后者使我可以使用setTargetType()
而不是setBeanClass()
来解决问题,并导致Spring找出创建bean的正确顺序,以便我可以通过{{1 }}。
之前:
@Autowired
之后:
var identifier = ... // Some String identifying the bean
var clazz = ... // Some class object coming from a dependency
var beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(clazz);
beanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
beanDefinition.setAutowireCandidate(true);
beanDefinition.setFactoryBeanName(CONTRACT_FACTORY_BEAN_NAME);
beanDefinition.setFactoryMethodName(CONTRACT_FACTORY_METHOD_NAME);
registry.registerBeanDefinition(identifier, beanDefinition);
我将更新repository中的示例代码,以供进一步参考。