spring - ApplicationContext registerBean 自动装配失败但 getBean 在 Spring 5 中工作
spring - ApplicationContext registerBean autowiring fails but getBean works in Spring 5
我正在使用使用动态 bean 注册的配置 class:
@Configuration
public class ConfigClass {
@Autowired
private GenericApplicationContext applicationContext;
@PostConstruct
private void init() {
System.out.println("init");
applicationContext.registerBean("exService", ExecutorService.class, () -> Executors.newFixedThreadPool(10), bd -> bd.setAutowireCandidate(true));
System.out.println("init done");
}
}
如果我尝试自动装配 bean,应用程序启动失败并出现错误 Field exService in com.example.DemoApplication required a bean of type 'java.util.concurrent.ExecutorService' that could not be found.
从日志中我可以看到在错误之前没有调用 config class 上的 init 方法,因为没有打印出两个系统输出语句。
但是,当我使用 applicationContext.getBean(ExecutorService.class)
时,它确实可以正常工作。
我能把 bean 拿到 Autowire 吗?
我故意不使用 @Bean
注解,因为我需要根据特定条件动态注册 bean。
这可能是因为您在上下文初始化阶段的中间注册了您的 bean。如果您的目标 bean 在调用 ConfigClass
@PostConstruct
之前初始化并且 auto-wires ExecutorService
则根本没有可用的 bean。
您可以尝试强制初始化顺序:
@Component
@DependsOn("configClass")
public class MyComponent
@Autowired
private ExecutorService executorService;
然而,使用 BeanFactoryPostProcessor
和 BeanDefinitionBuilder
:
注册一个 bean 定义会更清晰
@Component
public class MyBeanRegistration implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) {
BeanDefinitionRegistry reg = (BeanDefinitionRegistry) bf;
reg.registerBeanDefinition("exService",
BeanDefinitionBuilder
.rootBeanDefinition(ExecutorService.class)
.setFactoryMethod("newWorkStealingPool")
.getBeanDefinition());
}
}
实际上,我编写了处理此类问题的小型基础架构。这是如何做到这一点的想法:
- 创建一个 class(我们称之为 MyClass),将 ExecutorService.class 封装为 属性 并将其声明为 Bean (@Component)。甚至在此之前创建一个接口(让我们称之为 MyInterface),你的新 class 将实现
- 使用方法 MyInterface getInstance(String) 创建一个工厂 class(我们称它为 MyFactory),返回一个接口实例。在该工厂中创建一个静态 属性 Map[String, MyInterface] 和 public 静态方法,允许您将接口的实例添加到此映射
- 在 MyClass 中创建一个构造函数,该构造函数最后将使用您的 class 名称 ("MyClass")
的键将新创建的自身实例放入工厂中的映射中
现在 诀窍是,当 Spring 启动并初始化时,它会创建所有的 bean。当您的 MyClass 将被创建时,其构造函数会将其实例放入工厂中。所以现在你可以在代码的任何地方调用:
MyInterface myInterface = MyFactory.getInstance("MyClass");
你得到了你的 bean,而不用担心实例化它。 Spring 已经为您完成了。巨大的额外好处是 non-intrucivness - 你不必明确地使用 Spring classes
你可以这样做:
@Resource
@Lazy
private ExecutorService executorService;
有效。
我正在使用使用动态 bean 注册的配置 class:
@Configuration
public class ConfigClass {
@Autowired
private GenericApplicationContext applicationContext;
@PostConstruct
private void init() {
System.out.println("init");
applicationContext.registerBean("exService", ExecutorService.class, () -> Executors.newFixedThreadPool(10), bd -> bd.setAutowireCandidate(true));
System.out.println("init done");
}
}
如果我尝试自动装配 bean,应用程序启动失败并出现错误 Field exService in com.example.DemoApplication required a bean of type 'java.util.concurrent.ExecutorService' that could not be found.
从日志中我可以看到在错误之前没有调用 config class 上的 init 方法,因为没有打印出两个系统输出语句。
但是,当我使用 applicationContext.getBean(ExecutorService.class)
时,它确实可以正常工作。
我能把 bean 拿到 Autowire 吗?
我故意不使用 @Bean
注解,因为我需要根据特定条件动态注册 bean。
这可能是因为您在上下文初始化阶段的中间注册了您的 bean。如果您的目标 bean 在调用 ConfigClass
@PostConstruct
之前初始化并且 auto-wires ExecutorService
则根本没有可用的 bean。
您可以尝试强制初始化顺序:
@Component
@DependsOn("configClass")
public class MyComponent
@Autowired
private ExecutorService executorService;
然而,使用 BeanFactoryPostProcessor
和 BeanDefinitionBuilder
:
@Component
public class MyBeanRegistration implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) {
BeanDefinitionRegistry reg = (BeanDefinitionRegistry) bf;
reg.registerBeanDefinition("exService",
BeanDefinitionBuilder
.rootBeanDefinition(ExecutorService.class)
.setFactoryMethod("newWorkStealingPool")
.getBeanDefinition());
}
}
实际上,我编写了处理此类问题的小型基础架构。这是如何做到这一点的想法:
- 创建一个 class(我们称之为 MyClass),将 ExecutorService.class 封装为 属性 并将其声明为 Bean (@Component)。甚至在此之前创建一个接口(让我们称之为 MyInterface),你的新 class 将实现
- 使用方法 MyInterface getInstance(String) 创建一个工厂 class(我们称它为 MyFactory),返回一个接口实例。在该工厂中创建一个静态 属性 Map[String, MyInterface] 和 public 静态方法,允许您将接口的实例添加到此映射
- 在 MyClass 中创建一个构造函数,该构造函数最后将使用您的 class 名称 ("MyClass") 的键将新创建的自身实例放入工厂中的映射中
现在 诀窍是,当 Spring 启动并初始化时,它会创建所有的 bean。当您的 MyClass 将被创建时,其构造函数会将其实例放入工厂中。所以现在你可以在代码的任何地方调用:
MyInterface myInterface = MyFactory.getInstance("MyClass");
你得到了你的 bean,而不用担心实例化它。 Spring 已经为您完成了。巨大的额外好处是 non-intrucivness - 你不必明确地使用 Spring classes
你可以这样做:
@Resource
@Lazy
private ExecutorService executorService;
有效。