在Singleton类中自动装配Spring bean

时间:2020-02-03 23:52:26

标签: java spring spring-boot dependency-injection autowired

我正在尝试在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组件自动连接到手动实例化的类的最佳方法是什么。

4 个答案:

答案 0 :(得分:2)

Spring仅在它自己管理的bean中处理@Autowired。 因此,您有两种选择:

  1. 摆脱单例-如果您使用的是spring,则它在应用程序上下文中已经是单例。到目前为止,这通常是最好的方法(假设调用单例的应用程序的其他部分也受弹簧驱动)。我不认为您应该担心更改ConfigServiceDAO.getInstance.method()-IDE中的重构工具可以完成这项工作。

  2. 如果您不能这样做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。

相关问题