Подтвердить что ты не робот

FactoryBeans и конфигурация на основе аннотаций в Spring 3.0

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

Однако в Spring 3.0 я не могу найти удовлетворительный способ использования factory beans с конфигурацией на основе аннотаций (née JavaConfig).

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

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

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

4b9b3361

Ответ 1

Насколько я понимаю, ваша проблема заключается в том, что вы хотите, чтобы результат 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 -независимых методов перехватываются аспектом, который выполняет инициализацию возвращаемого beans (FactoryBean в вашем случае), поэтому вызов sqlSessionFactoryBean() в sqlSessionFactory() возвращает полностью инициализированный FactoryBean.

Ответ 2

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

@Bean
MyFactoryBean myFactoy()
{ 
    // 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 и т.д.

Ответ 3

Spring В JavaConfig был класс ConfigurationSupport, у которого был метод getObject() для использования с FactoryBean's.

Вы бы использовали его для расширения

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

Немного фона здесь

Ответ 4

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

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

В любом месте, и он тоже будет работать.

Ответ 5

Почему вы не вставляете Factory в AppConfiguration?

@Configuration
public class AppConfig {

    @Resource
    private SqlSessionFactoryBean factory;

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

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

Ответ 6

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

@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 напрямую. Я autowire sessionFactory в другой beans.