繁体   English   中英

在 Singleton 类中自动装配 Spring 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 所以你有两个选择:

  1. 摆脱单例 - 如果您使用的是 spring,它在应用程序上下文中已经是单例了。 这是迄今为止最好的方法(假设调用您的单例的其他应用程序部分也是弹簧驱动的)。 我认为您不应该害怕更改ConfigServiceDAO.getInstance.method() - IDE 中的重构工具将完成这项工作。

  2. 如果你不能做 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM