java - framework - spring injection dependencies
FactoryBeans y la configuración basada en anotaciones en Spring 3.0 (6)
Spring proporciona la interfaz FactoryBean
para permitir la inicialización no trivial de los beans. El marco proporciona muchas implementaciones de beans de fábrica y, cuando se utiliza la configuración XML de Spring, los beans de fábrica son fáciles de usar.
Sin embargo, en Spring 3.0, no puedo encontrar una manera satisfactoria de usar beans de fábrica con la configuración basada en anotaciones (née JavaConfig).
Obviamente, podría crear una instancia manual del bean de fábrica y establecer las propiedades requeridas por mí mismo, de este modo:
@Configuration
public class AppConfig {
...
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource());
factory.setAnotherProperty(anotherProperty());
return factory.getObject();
}
Sin embargo, esto fallaría si FactoryBean
implementara alguna interfaz de devolución de llamada específica de Spring, como InitializingBean
, ApplicationContextAware
, BeanClassLoaderAware
o @PostConstruct
por ejemplo. También necesito inspeccionar FactoryBean, averiguar qué interfaces de devolución de llamada implementa, luego implementar esta funcionalidad yo mismo llamando a setApplicationContext
, afterPropertiesSet()
etc.
Esto me parece incómodo y al revés: los desarrolladores de aplicaciones no deberían tener que implementar las devoluciones de llamada del contenedor IOC.
¿Alguien sabe de una mejor solución para usar FactoryBeans de Spring Annotation configs?
¿Por qué no inyectas la fábrica en tu AppConfiguration?
@Configuration
public class AppConfig {
@Resource
private SqlSessionFactoryBean factory;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
return factory.getObjectfactory();
}
}
Pero puede que no haya entendido tu pregunta correcta. Porque me parece que estás intentando algo extraño: retrocede un paso y replantea lo que realmente necesitas.
Así es como lo estoy haciendo:
@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
}
El sessionFactoryBean se crea y le suceden las cosas adecuadas del ciclo de vida posterior a la creación (afterPropertiesSet, etc.).
Tenga en cuenta que no hago referencia directamente al sessionFactoryBean como un bean. Yo autowire la sessionFactory en mis otros frijoles.
Creo que esto se resuelve mejor cuando se confía en el cableado automático. Si está utilizando la configuración de Java para los beans, esto le gustaría:
@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);
}
Entonces Spring inyectará para nosotros la instancia MyBean devuelta por myFactory (). GetObject () como lo hace con la configuración XML.
Esto también debería funcionar si está utilizando @ Inject / @ Autowire en sus clases de @ Component / @ Service, etc.
Esto es lo que estoy haciendo, y funciona:
@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);
}
Cualquier bean que haya declarado se puede pasar como un argumento a cualquier otro método @Bean (invocar nuevamente el mismo método creará una nueva instancia que no se procesará en primavera). Si declara un FactoryBean, puede usar el tipo de bean que crea como argumento para otro método @Bean y recibirá la instancia correcta. También podrías usar
@Autowired
private SqlSessionFactory sqlSessionFactory;
En cualquier lugar y funcionará también.
Por lo que entiendo, su problema es lo que quiere que un resultado de sqlSessionFactory()
sea un SqlSessionFactory
(para su uso en otros métodos), pero debe devolver SqlSessionFactoryBean
desde un método @Bean
@Bean para desencadenar devoluciones de llamada de Spring.
Se puede resolver con la siguiente solución:
@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());
}
}
El punto es que las llamadas a @Bean
Bean-métodos anotados son interceptadas por un aspecto que realiza la inicialización de los beans que se devuelven ( FactoryBean
en su caso), por lo que la llamada a sqlSessionFactoryBean()
en sqlSessionFactory()
devuelve un FactoryBean
completamente inicializado.
Spring JavaConfig tenía una clase ConfigurationSupport que tenía un método getObject () para usar con FactoryBean''s.
Lo usarías estar extendiendo
@Configuration
public class MyConfig extends ConfigurationSupport {
@Bean
public MyBean getMyBean() {
MyFactoryBean factory = new MyFactoryBean();
return (MyBean) getObject(factory);
}
}
Hay algunos antecedentes en este tema de la jira.
Con Spring 3.0, JavaConfig se trasladó a Spring Core y se decidió eliminar la clase ConfigurationSupport . El enfoque sugerido es usar ahora un patrón de construcción en lugar de fábricas.
Un ejemplo tomado del nuevo SessionFactoryBuilder
@Configuration
public class DataConfig {
@Bean
public SessionFactory sessionFactory() {
return new SessionFactoryBean()
.setDataSource(dataSource())
.setMappingLocations("classpath:com/myco/*.hbm.xml"})
.buildSessionFactory();
}
}
Algunos antecedentes here