Spring Boot 工厂 Bean 创建顺序
Posted
技术标签:
【中文标题】Spring Boot 工厂 Bean 创建顺序【英文标题】:Spring Boot Factory Bean Creation Order 【发布时间】:2019-04-03 23:43:09 【问题描述】:我正在尝试在 Spring Boot 中动态注册 Bean,但是如果尝试自动装配其中一个动态 bean,则创建 bean 的顺序总是会导致 NoSuchBeanDefinitionException
。
我的设置包括两个项目,一个 spring-boot-starter 项目和实际的 spring-boot 应用程序。
实际的应用程序注册了一个BeanDefinitionRegistryPostProcessor
,它添加了 bean 定义。实例本身是通过在启动项目中定义的另一个 bean 构建的,该项目本身将另一个 bean 作为依赖项。
为了使用动态注册的 bean,我创建了一个用 @Component
注释的类,并定义了一个期望该 bean 作为参数的构造函数。
当我通过设置@Autowired(required=false)
调试应用程序时,我可以看到在创建动态bean 之前调用了我的组件的构造函数。而且,当时连工厂 bean 都没有创建。
将带有工厂 bean 名称的 @DependsOn
添加到组件导致首先创建工厂,而不是动态 bean。
使用动态 bean 的名称设置 @DependsOn
有效,但这似乎不是解决此问题的正确方法。
为什么 Spring 以错误的顺序创建我的 bean,我能做些什么来解决这个问题?
编辑:
我能够在示例存储库中重现该问题:https://github.com/maveeee/spring-dynamic-bean-demo/
【问题讨论】:
【参考方案1】:您可以使用@Order
注释来定义带注释的组件或bean 的排序顺序。
考虑到在 Spring 4.0 之前,这个注解只用于 AspectJ 的执行顺序。 Spring 4.0 之后,支持将注入的组件排序到集合中。因此,Spring 会根据它们的 order 值注入相同类型的自动装配 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
)。
GitHub Demo
相关文章:
@Order in Spring What is the use of @Order annotation in Spring? Spring annotation @Order Order of Configuration in SpringBoot【讨论】:
我将提取相关部分并设置一个演示库 @MaVe 不客气 :) 如果您最终能够重现该问题,请不要犹豫,在这里给我留言,我会尽力帮助您。 我终于可以重现这个问题了:可以在github.com/maveeee/spring-dynamic-bean-demo看到一个示例 @MaVe 有机会时请检查此pull request 以查看它是否解决了您的问题,如果解决了问题,请告诉我,以便我为未来的读者更新答案。跨度> @MaVe 是的,确实!检查这个:***.com/a/41444078/5640649【参考方案2】:我发现我的问题是由我创建 bean 定义的方式引起的。我使用的是GenericBeanDefinition
而不是RootBeanDefinition
。使用后者允许我使用setTargetType()
而不是setBeanClass()
,这立即解决了问题并导致Spring 找出创建bean 的正确顺序,以便我可以通过@Autowired
注入动态创建的bean。
之前:
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);
之后:
var identifier = ... // Some String identifying the bean
var clazz = ... // Some class object coming from a dependency
var beanDefinition = new RootBeanDefinition();
beanDefinition.setTargetType(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中的示例代码以供进一步参考。
【讨论】:
以上是关于Spring Boot 工厂 Bean 创建顺序的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot 学习系列(09)—自定义Bean的顺序加载
Spring Boot 学习系列(09)—自定义Bean的顺序加载