[英]Spring Boot autowire field in singleton service/controller by request scoped bean
[英]Autowire a Spring bean in a Singleton class
我正在尝试在 Singleton 类中自动装配一个 bean,我知道避免手动自动装配总是一个最好的主意,但是这个类在很多地方被使用,所以我不想改变这个类的调用者。
跑步者.java
@Component
public class RunnerClass {
@Autowired
public 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,这是有道理的,因为我的理解是手动实例化类时,不会发生 spring 注入。 如果我想将 ConfigServiceDAO 保留为非弹簧组件,这里的解决方案是什么?
====编辑==== 我知道可以将 ConfigServiceDAO 作为弹簧组件并自动装配所有依赖项。 但是来自不同包的许多类已经调用 ConfigServiceDAO.getInstance().someMethod() 所以我想正确的问题是,将 spring 组件自动装配到手动实例化的类的最佳方法是什么。
我不知道您的用例,但您不能在 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;
}
}
然后你有一个配置类:
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 类中,您将获得对您感兴趣的构建器 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 仅在它自己管理的 bean 中处理@Autowired
。 所以你有两个选择:
摆脱单例 - 如果您使用的是 spring,它在应用程序上下文中已经是单例了。 这是迄今为止最好的方法(假设调用您的单例的其他应用程序部分也是弹簧驱动的)。 我认为您不应该害怕更改ConfigServiceDAO.getInstance.method()
- IDE 中的重构工具将完成这项工作。
如果你不能做 1,不要在单例中使用自动装配的注释 - 无论如何它都没用,相反,当你配置了应用程序上下文时(例如,在应用程序启动时 spring 发出的侦听器中),获得对ConfigServiceDAOBuilder
bean 通过调用appCtx.getBean(ConfigServiceDAOBuilder.class)
并通过反射手动“注入”,这就是 Spring 对 Spring 管理的 bean 所做的:
@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
方法setDaoBuilder
私有包,以保护单例免受某些意外调用 setter 的影响
据我了解您想要什么:由 Spring ConfigServiceDAOBuilder
创建。 之后将其注入到ConfigServiceDAO
类的非托管对象中。 您可以在实例化 Spring 应用程序上下文后执行此操作。 例如使用CommanLineRunner
:
@Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
@Autowired
ConfigServiceDAOBuilder DAOBuilder
@Override
public void run(String...args) throws Exception {
ConfigServiceDAO.getInstance().init(DAOBuilder);
}
}
在ConfigServiceDAO
中必须是方法init
来帮助注册所有需要的 bean。
看完你的评论我很困惑,所以让我这样说。 您所指的手动自动装配是 Spring 依赖注入方式。 每当您使用任何具有默认作用域实例的 Spring Stereotype 注释时,它始终是 Singleton。
您的 ConfigService 类有问题。 您正在混淆事物,您应该使用@configuration 创建一个单独的配置类并为类 ConfigServiceDAO 创建 Bean,如下所示
@Configuration
Class Config{
@Bean
public ConfigServiceDAO configServiceDAO( ){
return ConfigServiceDAO.getInstance();
}
}
然后在 ConfigService 类中自动装配 ConfigServiceDAO。 有了这个 Spring 将按正确的顺序解决所有依赖项,并且 DAOBuilder 不应该为空。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.