24

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

但是,在 Spring 3.0 中,我找不到令人满意的方法来使用带有基于注释的配置(née JavaConfig)的工厂 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但是,如果实现了任何特定于 Spring 的回调接口,例如InitializingBeanApplicationContextAwareBeanClassLoaderAware@PostConstruct例如,这将失败。我还需要检查FactoryBean,找出它实现了哪些回调接口,然后通过调用等方式自己实现这个setApplicationContext功能afterPropertiesSet()

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

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

4

6 回答 6

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,这也应该有效。

于 2012-01-26T09:42:00.210 回答
21

据我了解,您的问题是您想要的结果sqlSessionFactory()SqlSessionFactory用于其他方法),但您必须SqlSessionFactoryBean@Bean-annotated 方法返回才能触发 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-annotated 方法的调用被执行返回的 bean 的初始化的方面拦截(在您的情况下),因此对in的FactoryBean调用返回完全初始化的.sqlSessionFactoryBean()sqlSessionFactory()FactoryBean

于 2011-04-04T16:55:46.710 回答
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问题有一些背景

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

这里有一些背景

于 2012-02-20T23:19:32.557 回答
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;

任何地方,它也将工作。

于 2015-11-26T15:11:30.983 回答
0

为什么不在 AppConfiguration 中注入 Factory?

@Configuration
public class AppConfig {

    @Resource
    private SqlSessionFactoryBean factory;

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

但是我可能没有正确理解您的问题。因为在我看来你正在尝试一些奇怪的东西——退后一步,重新考虑你真正需要的是什么。

于 2011-04-04T16:40:21.497 回答
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 中。

于 2011-04-04T22:17:38.543 回答