在 Singleton class 中自动装配一个 Spring bean
Autowire a Spring bean in a Singleton class
我正在尝试在 Singleton class 中自动装配一个 bean,我知道避免手动自动装配总是最好的主意,但是这个 class 被用在很多地方所以我不想更改此 class.
的调用者
Runner.java
@Component
public class RunnerClass {
@Autowired
public ConfigService configService;
}
ConfigService.java
@Service
public class ConfigService {
private ConfigServiceDAO = ConfigServiceDAO.getInstance();
}
ConfigServiceDAO.java
public class ConfigServiceDAO {
//Bean I want to autowire here....
@Autowired
ConfigServiceDAOBuilder DAOBuilder
public static ConfigServiceDAO getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
public static final ConfigServiceDAO INSTANCE = new ConfigServiceDAO();
private SingletonHolder() {}
}
}
ConfigServiceDAO 中的 DAOBuilder 始终为 null,这是有道理的,因为我的理解是当手动实例化 class 时,不会发生 spring 注入。
如果我想将 ConfigServiceDAO 保留为非 spring 组件,这里的解决方案是什么?
====编辑====
我知道可以将 ConfigServiceDAO 作为 spring 组件并自动装配所有依赖项。
但是来自不同包的很多 classes 已经调用
ConfigServiceDAO.getInstance().someMethod()
所以我想正确的问题是,将 spring 组件自动装配到手动实例化的 class 的最佳方法是什么。
看了你的评论我很困惑,所以让我这样说。您所指的手动自动装配是 Spring 依赖注入方式。
每当您使用任何具有默认范围实例的 Spring 刻板印象注释时,始终是 Singleton。
您的 ConfigService class 有问题。
你把事情搞混了,你应该用@configuration 创建一个单独的配置 class 并为 class ConfigServiceDAO 创建 Bean,如下所示
@Configuration
Class Config{
@Bean
public ConfigServiceDAO configServiceDAO( ){
return ConfigServiceDAO.getInstance();
}
}
然后在 ConfigService class 中自动装配 ConfigServiceDAO。使用此 Spring 将以正确的顺序解析所有依赖项,DAOBuilder 不应为空。
我不知道你的用例,但你不能在 Spring bean 之外使用 @Autowired
注释。
但是,如果您确实需要从非 Spring 代码段访问 Spring bean,您可以像下面那样进行。然而,这是一种非常不 Spring 的设计依赖关系的方式。
import org.springframework.context.ApplicationContext;
public enum ApplicationContextHolder {
INSTANCE;
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
那么你有一个配置class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@Configuration
public class SomeConfig {
@Autowired
private ApplicationContext applicationContext;
@PostConstruct
public void init() {
ApplicationContextHolder.INSTANCE.setApplicationContext(applicationContext);
}
}
然后在您的 DAO 中 class 您将获得对您感兴趣的构建器 bean 的引用。像这样:
public class ConfigServiceDAO {
public static ConfigServiceDAO getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
public static final ConfigServiceDAO INSTANCE =
ApplicationContextHolder.INSTANCE.getApplicationContext().getBean(ConfigServiceDAOBuilder.class).buildConfigServiceDAO()
private SingletonHolder() {}
}
}
同样,这是一种非常不 Spring 的做事方式。
据我了解你想要什么:由 Spring ConfigServiceDAOBuilder
创建。之后将其注入 class ConfigServiceDAO
的非托管对象。您可以在实例化 Spring 应用程序上下文后执行此操作。 For example with CommanLineRunner
:
@Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
@Autowired
ConfigServiceDAOBuilder DAOBuilder
@Override
public void run(String...args) throws Exception {
ConfigServiceDAO.getInstance().init(DAOBuilder);
}
}
在 ConfigServiceDAO
中必须是帮助注册所有需要的 bean 的方法 init
。
Spring 仅在它自己管理的 bean 中处理 @Autowired
。
所以你有两个选择:
摆脱单例 - 如果您正在使用 spring,它在应用程序上下文中已经是单例。这是迄今为止最好的一般方法(假设调用您的单例的应用程序的其他部分也是 spring 驱动的)。我认为您不应该害怕更改 ConfigServiceDAO.getInstance.method()
- IDE 中的重构工具将完成这项工作。
如果你做不到 1,请不要在单例中使用自动装配注释 - 无论如何它都没用,相反,当你配置了应用程序上下文时(在侦听器中 spring例如,在应用程序启动时发出),通过反射手动调用 appCtx.getBean(ConfigServiceDAOBuilder.class)
和 "inject it" 来访问 ConfigServiceDAOBuilder
bean,这就是 Spring 对 [=34 的作用=] 无论如何管理 beans:
@EventListener
public void onApplicationReadyEvent(ApplicationReadyEvent event) {
ConfigServiceDAOBuilder builder =
event.getApplicationContext().getBean(ConfigServiceDAOBuilder.class);
ConfigServiceDao dao = ConfigServiceDAO.getInstance();
dao.setDaoBuilder(builder); // or alternatively by reflection
}
作为旁注,考虑使用方法 setDaoBuilder
作为私有包,以保护单例免受意外调用 setter
我正在尝试在 Singleton class 中自动装配一个 bean,我知道避免手动自动装配总是最好的主意,但是这个 class 被用在很多地方所以我不想更改此 class.
的调用者Runner.java
@Component
public class RunnerClass {
@Autowired
public ConfigService configService;
}
ConfigService.java
@Service
public class ConfigService {
private ConfigServiceDAO = ConfigServiceDAO.getInstance();
}
ConfigServiceDAO.java
public class ConfigServiceDAO {
//Bean I want to autowire here....
@Autowired
ConfigServiceDAOBuilder DAOBuilder
public static ConfigServiceDAO getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
public static final ConfigServiceDAO INSTANCE = new ConfigServiceDAO();
private SingletonHolder() {}
}
}
ConfigServiceDAO 中的 DAOBuilder 始终为 null,这是有道理的,因为我的理解是当手动实例化 class 时,不会发生 spring 注入。 如果我想将 ConfigServiceDAO 保留为非 spring 组件,这里的解决方案是什么?
====编辑==== 我知道可以将 ConfigServiceDAO 作为 spring 组件并自动装配所有依赖项。 但是来自不同包的很多 classes 已经调用 ConfigServiceDAO.getInstance().someMethod() 所以我想正确的问题是,将 spring 组件自动装配到手动实例化的 class 的最佳方法是什么。
看了你的评论我很困惑,所以让我这样说。您所指的手动自动装配是 Spring 依赖注入方式。 每当您使用任何具有默认范围实例的 Spring 刻板印象注释时,始终是 Singleton。
您的 ConfigService class 有问题。 你把事情搞混了,你应该用@configuration 创建一个单独的配置 class 并为 class ConfigServiceDAO 创建 Bean,如下所示
@Configuration
Class Config{
@Bean
public ConfigServiceDAO configServiceDAO( ){
return ConfigServiceDAO.getInstance();
}
}
然后在 ConfigService class 中自动装配 ConfigServiceDAO。使用此 Spring 将以正确的顺序解析所有依赖项,DAOBuilder 不应为空。
我不知道你的用例,但你不能在 Spring bean 之外使用 @Autowired
注释。
但是,如果您确实需要从非 Spring 代码段访问 Spring bean,您可以像下面那样进行。然而,这是一种非常不 Spring 的设计依赖关系的方式。
import org.springframework.context.ApplicationContext;
public enum ApplicationContextHolder {
INSTANCE;
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
那么你有一个配置class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@Configuration
public class SomeConfig {
@Autowired
private ApplicationContext applicationContext;
@PostConstruct
public void init() {
ApplicationContextHolder.INSTANCE.setApplicationContext(applicationContext);
}
}
然后在您的 DAO 中 class 您将获得对您感兴趣的构建器 bean 的引用。像这样:
public class ConfigServiceDAO {
public static ConfigServiceDAO getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
public static final ConfigServiceDAO INSTANCE =
ApplicationContextHolder.INSTANCE.getApplicationContext().getBean(ConfigServiceDAOBuilder.class).buildConfigServiceDAO()
private SingletonHolder() {}
}
}
同样,这是一种非常不 Spring 的做事方式。
据我了解你想要什么:由 Spring ConfigServiceDAOBuilder
创建。之后将其注入 class ConfigServiceDAO
的非托管对象。您可以在实例化 Spring 应用程序上下文后执行此操作。 For example with CommanLineRunner
:
@Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
@Autowired
ConfigServiceDAOBuilder DAOBuilder
@Override
public void run(String...args) throws Exception {
ConfigServiceDAO.getInstance().init(DAOBuilder);
}
}
在 ConfigServiceDAO
中必须是帮助注册所有需要的 bean 的方法 init
。
Spring 仅在它自己管理的 bean 中处理 @Autowired
。
所以你有两个选择:
摆脱单例 - 如果您正在使用 spring,它在应用程序上下文中已经是单例。这是迄今为止最好的一般方法(假设调用您的单例的应用程序的其他部分也是 spring 驱动的)。我认为您不应该害怕更改
ConfigServiceDAO.getInstance.method()
- IDE 中的重构工具将完成这项工作。如果你做不到 1,请不要在单例中使用自动装配注释 - 无论如何它都没用,相反,当你配置了应用程序上下文时(在侦听器中 spring例如,在应用程序启动时发出),通过反射手动调用
appCtx.getBean(ConfigServiceDAOBuilder.class)
和 "inject it" 来访问ConfigServiceDAOBuilder
bean,这就是 Spring 对 [=34 的作用=] 无论如何管理 beans:
@EventListener
public void onApplicationReadyEvent(ApplicationReadyEvent event) {
ConfigServiceDAOBuilder builder =
event.getApplicationContext().getBean(ConfigServiceDAOBuilder.class);
ConfigServiceDao dao = ConfigServiceDAO.getInstance();
dao.setDaoBuilder(builder); // or alternatively by reflection
}
作为旁注,考虑使用方法 setDaoBuilder
作为私有包,以保护单例免受意外调用 setter