【问题标题】:Dependency Injection into your Singleton依赖注入到你的单例中
【发布时间】:2011-02-21 02:10:33
【问题描述】:

我有一个单例,它有一个注入弹簧的 Dao(简化如下):

public class MyService<T> implements Service<T> {
    private final Map<String, T> objects;
    private static MyService instance;

    MyDao myDao;

    public void set MyDao(MyDao myDao) {
        this. myDao = myDao;
    }

    private MyService() {
        this.objects = Collections.synchronizedMap(new HashMap<String, T>());
        // start a background thread that runs for ever
    }

    public static synchronized MyService getInstance() {
        if(instance == null) {
            instance = new MyService();
        }
        return instance;
    }

    public void doSomething() {
        myDao.persist(objects);
    }
}

我的 spring 配置可能如下所示:

 <bean id="service" class="MyService" factory-method="getInstance"/>

但这会在启动期间实例化 MyService。

是否有编程方式将 MyDao 的依赖注入到 MyService 中,但不让 spring 管理 MyService?

基本上我希望能够从我的代码中做到这一点:

MyService.getInstance().doSomething();

让 spring 为我注入 MyDao。

【问题讨论】:

    标签: spring dependency-injection singleton


    【解决方案1】:

    这里有一个解决方案,用静态工厂方法创建一个类:

    public class MyService {
        private static MyService instance;
    
        private MyDao myDao;
    
        public static MyService createInstance(final MyDao myDao) {
          instance = new MyService(myDao);
          return instance;
        }
    
        private MyService(final MyDao myDao) {
          this.myDao = myDao;
        }
    
        public static synchronized MyService getInstance() {
          return instance;
        }
    
        public void doSomething() {
          // just do it!
          myDao.justDoIt();
        }
    }
    

    并使用spring来初始化它:

      <bean class="my.path.MyService" factory-method="createInstance" scope="singleton">
        <constructor-arg ref="reference.to.myDao" />
      </bean>
    

    现在你应该可以做到了:

    MyService.getInstance().doSomething();
    

    没有任何问题。

    【讨论】:

      【解决方案2】:

      如果你想要一个单例,为什么不在 Spring 配置中定义一个类,它会自动成为一个单例(默认情况下)。

      为避免在启动时进行初始化,您是否查看过 Spring lazy initialisation ?基本上你需要:

      lazy-init="true"
      

      在你的 bean 定义中。

      【讨论】:

      • 你必须在需要的地方使用 ApplicationContext 来获取 bean?
      • 我会通常在初始化时获取我的一个顶级 bean,然后根据该顶级 bean 的需要引用更多 bean。但你可以按照你的建议去做。
      【解决方案3】:

      正如其他人所说,您应该让 spring 管理您的单例,但如果您想自己管理它们并让 spring 注入依赖项,请执行以下操作:

      applicationContext.getAutowireCapableBeanFactory().autowireBean(yourService);
      

      【讨论】:

        【解决方案4】:

        我相信 FactoryBean 接口对您来说是一个不错的选择。当您需要执行一些初始化逻辑时,这是一个非常好的选择。例如,在单独的线程中启动内存数据库或一些后台进程。

        您可以在reference documentation 中了解更多信息。

        一个示例演示了我如何实例化一个数据库并在每次有人想要一个来自 FactoryBean 实现的 bean 时返回一个数据源。

        @PostConstruct
        void init() {
            embeddedDatabase = new EmbeddedDatabaseBuilder().addScript(schemaPath)
                 .addScript(dataPath).setType(embeddedDatabaseType).build();
        }
        
        
        public DataSource getObject() throws Exception {
            return embeddedDatabase;
        }
        

        这使得工厂逻辑和返回对象之间的松耦合成为可能。它在内部被 Spring 框架大量使用。

        如果你希望它在你第一次使用它时被初始化,那么将惰性初始化设置为 true。

        如果您希望代码与 Spring 容器交互,另一种选择是创建一个实现 ApplicationContextAware 接口的工厂。然后你可以这样做:

        myDao = context.getBean(MyDao.class);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-11-11
          • 1970-01-01
          • 2015-05-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多