FactoryBeans and the annotation-based configuration in Spring 3.0
Solution 1
As far as I understand your problem is what you want a result of sqlSessionFactory()
to be a SqlSessionFactory
(for use in other methods), but you have to return SqlSessionFactoryBean
from a @Bean
-annotated method in order to trigger Spring callbacks.
It can be solved with the following workaround:
@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());
}
}
The point is that calls to @Bean
-annotated methods are intercepted by an aspect which performs initialization of the beans being returned (FactoryBean
in your case), so that call to sqlSessionFactoryBean()
in sqlSessionFactory()
returns a fully initialized FactoryBean
.
Solution 2
I think that this is best solved when you rely on auto-wiring. If you are using Java configuration for the beans, this would like:
@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);
}
So Spring will inject for us the MyBean instance returned by the myFactory().getObject() as it does with XML configuration.
This should also work if you are using @Inject/@Autowire in your @Component/@Service etc classes.
Solution 3
Spring JavaConfig had a ConfigurationSupport class that had a getObject() method for use with FactoryBean's.
You would use it be extending
@Configuration
public class MyConfig extends ConfigurationSupport {
@Bean
public MyBean getMyBean() {
MyFactoryBean factory = new MyFactoryBean();
return (MyBean) getObject(factory);
}
}
There is some background in this jira issue
With Spring 3.0 JavaConfig was moved into Spring core and it was decided to get rid of the ConfigurationSupport class. Suggested approach is to now use builder pattern instead of factories.
An example taken from the new SessionFactoryBuilder
@Configuration
public class DataConfig {
@Bean
public SessionFactory sessionFactory() {
return new SessionFactoryBean()
.setDataSource(dataSource())
.setMappingLocations("classpath:com/myco/*.hbm.xml"})
.buildSessionFactory();
}
}
Some background here
Solution 4
This is what I'm doing, and it works:
@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);
}
Any bean you have declared can be passed as an argument to any other @Bean method (invoking the same method again will create a new instance which is not processed by spring). If you declare a FactoryBean, you can use the bean type it creates as an argument for another @Bean method, and it will receive the right instance. You could also use
@Autowired
private SqlSessionFactory sqlSessionFactory;
Anywhere and it will work too.
tiberghien Jeffrey
I'm a software developer and co-founder of Gitter, a developer chat application. I live in London and spend some time in Cape Town, South Africa. I use a wide range of technologies: Objective-C for Mac and iOS Apps, node.js for server-side apps, Backbone.JS, Marionette.JS, for the front-end and tons and tons of other technologies.
Updated on July 18, 2022Comments
-
tiberghien Jeffrey almost 2 years
Spring provides the
FactoryBean
interface to allow non-trivial initialisation of beans. The framework provides many implementations of factory beans and -- when using Spring's XML config -- factory beans are easy to use.However, in Spring 3.0, I can't find a satisfactory way of using factory beans with the annotation-based configuration (née JavaConfig).
Obviously, I could manually instantiate the factory bean and set any required properties myself, like so:
@Configuration public class AppConfig { ... @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); factory.setDataSource(dataSource()); factory.setAnotherProperty(anotherProperty()); return factory.getObject(); }
However, this would fail if the
FactoryBean
implemented any Spring-specific callback interfaces, likeInitializingBean
,ApplicationContextAware
,BeanClassLoaderAware
, or@PostConstruct
for example. I also need to inspect the FactoryBean, find out what callback interfaces it implements, then implement this functionality myself by callingsetApplicationContext
,afterPropertiesSet()
etc.This feels awkward and back-to-front to me: application-developers should not have to implement the callbacks of the IOC container.
Does anyone know of a better solution to using FactoryBeans from Spring Annotation configs?