FactoryBeans和Spring 3.0中基于注释的配置

时间:2011-04-04 15:57:12

标签: java spring inversion-of-control spring-annotations

Spring提供了FactoryBean接口,允许对bean进行非平凡的初始化。该框架提供了工厂bean的许多实现,并且 - 当使用Spring的XML配置时 - 工厂bean很容易使用。

但是,在Spring 3.0中,我找不到一种令人满意的方法来使用带有基于注释的配置的工厂bean(néeJavaConfig)。

显然,我可以手动实例化工厂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的回调接口,例如InitializingBeanApplicationContextAwareBeanClassLoaderAware@PostConstruct,则会失败。我还需要检查FactoryBean,找出它实现的回调接口,然后通过调用setApplicationContextafterPropertiesSet()等自己实现此功能。

这对我来说感觉很尴尬和反复:应用程序开发人员不应该实现IOC容器的回调。

有没有人知道使用Spring Annotation配置中的FactoryBeans的更好解决方案?

6 个答案:

答案 0 :(得分:22)

我认为,当您依赖自动布线时,这是最好的解决方案。如果您对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将为我们注入myFactory()。getObject()返回的MyBean实例,就像它配置XML配置一样。

如果您在@ Component / @ Service等类中使用@Inject / @ Autowire,这也应该有效。

答案 1 :(得分:21)

据我所知,您的问题是您希望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

答案 2 :(得分:5)

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

一些背景here

答案 3 :(得分:2)

这就是我正在做的事情,它有效:

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

任何地方,它也会起作用。

答案 4 :(得分:0)

为什么不在AppConfiguration中注入Factory?

@Configuration
public class AppConfig {

    @Resource
    private SqlSessionFactoryBean factory;

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

但是我可能不明白你的问题是正确的。因为在我看来你正在尝试一些奇怪的东西 - 退后一步,重新考虑你真正需要的东西。

答案 5 :(得分:0)

以下是我的表现:

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