web-dev-qa-db-ja.com

Spring 3.0のFactoryBeansと注釈ベースの構成

SpringはFactoryBeanインターフェースを提供して、豆の重要な初期化を可能にします。フレームワークは、ファクトリBeanの多くの実装を提供し、SpringのXML構成を使用すると、ファクトリBeanは使いやすくなります。

しかし、Spring 3.0では、アノテーションベースの構成(néeJavaConfig)でファクトリーBeanを使用する満足のいく方法を見つけることができません。

明らかに、手動でファクトリーBeanをインスタンス化し、必要なプロパティを自分で設定することもできます。

_@Configuration
public class AppConfig {

...

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource());
        factory.setAnotherProperty(anotherProperty());

        return factory.getObject();
    }
_

ただし、FactoryBeanが、InitializingBeanApplicationContextAwareBeanClassLoaderAware、または_@PostConstruct_ for_例。また、FactoryBeanを検査し、それが実装するコールバックインターフェースを見つけ、setApplicationContextafterPropertiesSet()などを呼び出してこの機能を自分で実装する必要があります。

これは、私にとってはぎこちなく前向きです。アプリケーション開発者は、IOCコンテナのコー​​ルバックを実装する必要はありません。

Spring Annotationの構成からFactoryBeanを使用するためのより良いソリューションを知っている人はいますか?

24

私が理解している限り、あなたの問題はsqlSessionFactory()の結果をSqlSessionFactory(他のメソッドで使用するため)にしたいものですが、SqlSessionFactoryBeanを返す必要がありますSpringコールバックをトリガーするための_@Bean_- annotatedメソッド。

次の回避策で解決できます。

_@Configuration 
public class AppConfig { 
    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactoryBean sqlSessionFactoryBean() { ... }

    // FactoryBean is hidden behind this method
    public SqlSessionFactory sqlSessionFactory() {
        try {
            return sqlSessionFactoryBean().getObject();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    @Bean
    public AnotherBean anotherBean() {
        return new AnotherBean(sqlSessionFactory());
    }
}
_

ポイントは、_@Bean_アノテーション付きメソッドの呼び出しが、返されるBean(この場合はFactoryBean)の初期化を実行するアスペクトによってインターセプトされるため、sqlSessionFactoryBean()の呼び出しがsqlSessionFactory()では、完全に初期化されたFactoryBeanを返します。

21
axtavt

これは、自動配線に頼るときに最もよく解決されると思います。 BeanにJava構成を使用している場合、これは次のようになります。

@Bean
MyFactoryBean myFactory()
{ 
    // this is a spring FactoryBean for MyBean
    // i.e. something that implements FactoryBean<MyBean>
    return new MyFactoryBean();
}

@Bean
MyOtherBean myOther(final MyBean myBean)
{
    return new MyOtherBean(myBean);
}

したがって、Springは、XML構成の場合と同様に、myFactory()。getObject()によって返されたMyBeanインスタンスを挿入します。

これは、@ Component/@ Serviceなどのクラスで@ Inject/@ Autowireを使用している場合にも機能します。

22
tsachev

Spring JavaConfigには ConfigurationSupport クラスがあり、FactoryBeanで使用するgetObject()メソッドがありました。

あなたはそれを拡張して使うでしょう

@Configuration
public class MyConfig extends ConfigurationSupport {

    @Bean
    public MyBean getMyBean() {
       MyFactoryBean factory = new MyFactoryBean();
       return (MyBean) getObject(factory);
    }
}

これにはいくつかの背景があります jira issue

Spring 3.0では、JavaConfigがSpringコアに移動され、 ConfigurationSupport クラスを削除することが決定されました。推奨されるアプローチは、今ではファクトリーの代わりにビルダーパターンを使用することです。

新しい SessionFactoryBuilder からの例

@Configuration
public class DataConfig {
    @Bean
    public SessionFactory sessionFactory() {
        return new SessionFactoryBean()
           .setDataSource(dataSource())
           .setMappingLocations("classpath:com/myco/*.hbm.xml"})
           .buildSessionFactory();
    }
}

背景 ここ

5
objects

これは私がやっていることであり、うまくいきます:

@Bean
@ConfigurationProperties("dataSource")
public DataSource dataSource() { // Automatically configured from a properties file
    return new BasicDataSource();
}

@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource); // Invoking dataSource() would get a new instance which won't be initialized
    factory.setAnotherProperty(anotherProperty());
    return factory;
}


@Bean
public AnotherBean anotherBean(SqlSessionFactory sqlSessionFactory) { // This method receives the SqlSessionFactory created by the factory above
    return new AnotherBean(sqlSessionFactory);
}

宣言したBeanは、引数として他の@Beanメソッドに渡すことができます(同じメソッドを再度呼び出すと、Springによって処理されない新しいインスタンスが作成されます)。 FactoryBeanを宣言すると、作成したBeanタイプを別の@Beanメソッドの引数として使用でき、正しいインスタンスを受け取ります。あなたも使うことができます

@Autowired
private SqlSessionFactory sqlSessionFactory;

どこでも、それはうまくいきます。

2
Triqui

ここに私がそれをやっている方法があります:

@Bean
def sessionFactoryBean: AnnotationSessionFactoryBean = {
  val sfb = new AnnotationSessionFactoryBean

  sfb.setDataSource(dataSource)
  sfb.setPackagesToScan(Array("com.foo.domain"))

  // Other configuration of session factory bean
  // ...

  return sfb
}

@Bean
def sessionFactory: SessionFactory = {
   return sessionFactoryBean.getObject
}

SessionFactoryBeanが作成され、適切な作成後のライフサイクルが発生します(afterPropertiesSetなど)。

SessionFactoryBeanを直接Beanとして参照しないことに注意してください。 sessionFactoryを他のBeanに自動配線します。

0
sourcedelica

AppConfigurationにファクトリを注入しないのはなぜですか?

@Configuration
public class AppConfig {

    @Resource
    private SqlSessionFactoryBean factory;

    @Bean 
    public SqlSessionFactory sqlSessionFactory() throws Exception {
       return factory.getObjectfactory();    
    }    
}

しかし、あなたの質問が正しく理解できなかった可能性があります。何か奇妙なことをしようとしているように見えるので、少し戻って、本当に必要なものを再考してください。

0
Ralph