我正在尝试在Singleton类中自动装配一个bean,我知道这始终是避免手动自动装配的最佳主意,但是此类在很多地方都在使用,因此我不想更改此调用者课。
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,这是有道理的,因为我的理解是,当手动实例化该类时,不会发生弹簧注入。 如果我要将ConfigServiceDAO保留为非spring组件,那么这里的解决方案是什么?
====编辑==== 我知道有可能使ConfigServiceDAO作为spring组件并自动装配所有依赖项。 但是来自不同软件包的许多类已经调用了 ConfigServiceDAO.getInstance()。someMethod() 所以我想正确的问题是,将spring组件自动连接到手动实例化的类的最佳方法是什么。
答案 0 :(得分:2)
Spring仅在它自己管理的bean中处理@Autowired
。
因此,您有两种选择:
摆脱单例-如果您使用的是spring,则它在应用程序上下文中已经是单例。到目前为止,这通常是最好的方法(假设调用单例的应用程序的其他部分也受弹簧驱动)。我不认为您应该担心更改ConfigServiceDAO.getInstance.method()
-IDE中的重构工具可以完成这项工作。
如果您不能这样做1,请不要在单例中使用自动装配注释-无论如何,它都是无用的,而是在您配置了应用程序上下文时(例如,在应用程序启动时发出的监听器中) ,通过调用ConfigServiceDAOBuilder
获得对appCtx.getBean(ConfigServiceDAOBuilder.class)
bean的访问权,并通过反射手动“注入”,这是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
作为私有包,以保护单例避免意外调用设置器。
答案 1 :(得分:1)
我不知道您的用例,但是您不能在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的处理方式。
答案 2 :(得分:1)
据我了解,您想要什么:在Spring ConfigServiceDAOBuilder
之前创建。之后,将其注入到类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
中必须使用方法init
,该方法有助于注册所有需要的bean。
答案 3 :(得分:0)
阅读您的评论后我很困惑,因此让我这样说。您所说的手动自动装配是Spring依赖注入方式。 每当您在默认范围实例中使用任何Spring构造型注释时,始终为Singleton。
您的ConfigService类有问题。 您正在混淆,您应该使用@configuration创建一个单独的配置类,并为ConfigServiceDAO类创建Bean,如下所示:
@Configuration
Class Config{
@Bean
public ConfigServiceDAO configServiceDAO( ){
return ConfigServiceDAO.getInstance();
}
}
然后将ConfigServiceDAO自动连接到ConfigService类中。借助Spring,它将以正确的顺序解决所有依赖关系,并且DAOBuilder不应为null。