Spring Framework で Scala の object に @Autowired する

2018/02/28   #Spring Framework  #Scala 
このエントリーをはてなブックマークに追加

以下のような BeanFactoryAware を implements したクラスを用意。
ここは java で書いた。
このクラスの autowire(instance) メソッドで、指定した instance に対して手動で autowire できる。

package foo.bar.example;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;

public class AutowiredObjectFactory implements BeanFactoryAware {

    private AutowireCapableBeanFactory factory = null;

    private static AutowiredObjectFactory singleton = new AutowiredObjectFactory();

    public static AutowiredObjectFactory autoWiredObjectFactory() {
        return singleton;
    }

    public <T> T autowire(T instance) {
        factory.autowireBeanProperties(
                instance,
                AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE,
                true);
        return instance;
    }

    public void setBeanFactory(BeanFactory factory) throws BeansException {
        this.factory = (AutowireCapableBeanFactory) factory;
    }

}

使う側。

package foo.bar.example

import javax.sql.DataSource

import domala.jdbc.Naming
import domala.jdbc.dialect.{Dialect, H2Dialect}
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy

object DomalaConfig extends SpringTransactionConfig(
  dialect = new H2Dialect(),
  naming = Naming.SNAKE_LOWER_CASE
) {
  Class.forName("org.h2.Driver")
}

abstract class SpringTransactionConfig(
  dialect: Dialect,
  naming: Naming = Naming.NONE
) extends domala.jdbc.Config {

  @Autowired
  val dataSource: DataSource = null
  //または var dataSource: DataSource = _

  override def getDataSource: DataSource = new TransactionAwareDataSourceProxy(dataSource)

  override def getDialect: Dialect = dialect
  override def getNaming: Naming = naming
}

object DomalaConfigFactory {
  import foo.bar.example.AutowiredObjectFactory._
  def apply() = autoWiredObjectFactory().autowire(DomalaConfig)
}

上記の DomalaConfigFactory 内で以下のようにしている。

  import foo.bar.example.AutowiredObjectFactory._
  def load() = autoWiredObjectFactory().autowire(DomalaConfig)

今回はシングルトンオブジェクトに対して Autowired したオブジェクトを作りたかったので、
autowire メソッドには DomalaConfig (DomalaConfig.apply)を渡している。

これで、 DomalaConfigFactory#load を呼び出せば、
Autowired 済みの object が手に入る。

implicit lazy val config: Config = DomalaConfigFactory.load

参考: Scala & Spring: Combine the best of both worlds


別解:
How to use Spring Autowired (or manually wired) in Scala object? - Stack Overflow

object User {
    @Autowired val repo: UserRepository = null

    def instance() = this
}

@Configuration
class CompanionsConfig {
    @Bean def UserCompanion = User.instance
}

@Configuraiton なクラスに companion のインスタンスを返すメソッドを用意して、それを @Bean する。
で、上記の例だと UserCompanion を DI することで Autowired 済み object を DI できる。
なんだけどこれ、DIして使う想定ならそもそも object にしなければいいんじゃ・・・