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

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()等自己实现这个function。

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

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

据我所知,你的问题是你希望sqlSessionFactory()的结果是一个SqlSessionFactory (用于其他方法),但你必须从@Bean -annotated方法返回SqlSessionFactoryBean才能触发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的初始化(在你的情况下是FactoryBean ),因此在sqlSessionFactoryBean()中调用sqlSessionFactory()返回一个完全初始化的FactoryBean

我认为,当您依赖自动布线时,这是最好的解决方案。 如果您对bean使用Java配置,则需要:

 @Bean MyFactoryBean myFactory() { // this is a spring FactoryBean for MyBean // ie something that implements FactoryBean return new MyFactoryBean(); } @Bean MyOtherBean myOther(final MyBean myBean) { return new MyOtherBean(myBean); } 

所以Spring将为我们注入myFactory()。getObject()返回的MyBean实例,就像它配置XML一样。

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

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

一些背景在这里

这就是我正在做的,它的工作原理:

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

任何地方,它也会工作。

为什么不在AppConfiguration中注入Factory?

 @Configuration public class AppConfig { @Resource private SqlSessionFactoryBean factory; @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { return factory.getObjectfactory(); } } 

但是我可能不明白你的问题是正确的。 因为在我看来你正在尝试一些奇怪的事情 – 退后一步,重新思考你真正需要什么。

我是这样做的:

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