FactoryBeans и конфигурация на основе аннотаций в Spring 3.0 - PullRequest
24 голосов
/ 04 апреля 2011

Spring предоставляет интерфейс FactoryBean, позволяющий нетривиальную инициализацию bean-компонентов. Фреймворк предоставляет множество реализаций фабричных bean-компонентов и - при использовании XML-конфигурации Spring - фабричные bean-компоненты просты в использовании.

Однако в Spring 3.0 я не могу найти удовлетворительный способ использования фабричных bean-компонентов с конфигурацией на основе аннотаций (ранее JavaConfig).

Очевидно, я мог бы вручную создать экземпляр фабричного компонента и установить любые необходимые свойства, например:

@Configuration
public class AppConfig {

...

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

        return factory.getObject();
    }

Однако это не получится, если FactoryBean реализует какие-либо специфичные для Spring интерфейсы обратного вызова, например, InitializingBean, ApplicationContextAware, BeanClassLoaderAware или @PostConstruct. Мне также нужно проверить FactoryBean, выяснить, какие интерфейсы обратного вызова он реализует, а затем самостоятельно реализовать эту функцию, вызвав setApplicationContext, afterPropertiesSet() и т. Д.

Это кажется мне неловким и задом наперед: разработчикам приложений не нужно реализовывать обратные вызовы контейнера IOC.

Кто-нибудь знает лучшее решение для использования FactoryBeans из конфигураций Spring Annotation?

Ответы [ 6 ]

22 голосов
/ 26 января 2012

Я думаю, что это лучше всего решается, когда вы полагаетесь на автоматическое подключение. Если вы используете конфигурацию Java для bean-компонентов, это будет выглядеть так:

@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 вставит для нас экземпляр MyBean, возвращаемый myFactory (). GetObject (), как это происходит с конфигурацией XML.

Это также должно работать, если вы используете @ Inject / @ Autowire в своих классах @ Component / @ Service и т. Д.

21 голосов
/ 04 апреля 2011

Насколько я понимаю, ваша проблема в том, что вы хотите, чтобы результат sqlSessionFactory() был SqlSessionFactory (для использования в других методах), но вы должны вернуть SqlSessionFactoryBean из @Bean -аннотированного методадля вызова обратных вызовов Spring.

Это может быть решено с помощью следующего обходного пути:

@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.

5 голосов
/ 21 февраля 2012

Spring JavaConfig имел класс ConfigurationSupport , который имел метод getObject () для использования с FactoryBean.

Вы бы использовали это расширение

@Configuration
public class MyConfig extends ConfigurationSupport {

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

В этом выпуске jira * есть некоторый фон

В 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();
    }
}

Некоторый фон здесь

2 голосов
/ 26 ноября 2015

Это то, что я делаю, и это работает:

@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 (повторный вызов того же метода создастновый экземпляр который не обработан к весне).Если вы объявите FactoryBean, вы можете использовать тип bean-компонента, который он создает, в качестве аргумента для другого метода @Bean, и он получит правильный экземпляр.Вы также можете использовать

@Autowired
private SqlSessionFactory sqlSessionFactory;

Anywhere, и это тоже будет работать.

0 голосов
/ 05 апреля 2011

Вот как я это делаю:

@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 с другими компонентами.

0 голосов
/ 04 апреля 2011

Почему вы не вводите Factory в своей конфигурации приложения?

@Configuration
public class AppConfig {

    @Resource
    private SqlSessionFactoryBean factory;

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

Но, возможно, я не правильно понял ваш вопрос.Потому что мне кажется, что вы пытаетесь что-то странное - сделайте шаг назад и переосмыслите то, что вам действительно нужно.

...