2011-06-22 17 views
12

¿Cuál es la forma correcta de usar beans de fábrica en las clases @Configuration?Primavera: ¿usar beans de fábrica en la configuración?

Supongamos que tengo lo siguiente para SessionFactory:

@Bean 
public AnnotationSessionFactoryBean sessionFactory() { 
    AnnotationSessionFactoryBean factory = new AnnotationSessionFactoryBean(); 
    // set up properties etc. 
    return factory; 
} 

En este momento este método devuelve una fábrica de beans, que no implementa SessionFactory. Si lo uso en otro bean con @Autowired como SessionFactory, funciona muy bien y la obtiene de la fábrica de frijol:

@Controller 
public class MyController { 
    @Autowired SessionFactory sessionFactory; 
    // ... 
} 

supongo que eso está bien.

Sin embargo, se convierte en un problema si quiero usar la SessionFactory en la misma clase de configuración:

@Bean 
public HibernateTransactionManager transactionManager() { 
    HibernateTransactionManager man = new HibernateTransactionManager(); 
    // Ideal - doesn't work because sessionFactory() return type doesn't match: 
    // SessionFactory sessionFactory = sessionFactory(); 
    // Is this one correct? 
    // SessionFactory sessionFactory = (SessionFactory) sessionFactory().getObject(); 
    man.setSessionFactory(sessionFactory); 
    return man; 
} 

¿Cuál es la forma correcta para implementar este tipo de dependencia?

Respuesta

20

@Configuration enfoque es relativamente reciente y tiene algunos bordes ásperos. Los frijoles de fábrica son uno de ellos. Entonces, no es Right Way, al menos no conozco ninguna. Tal vez los futuros lanzamientos de Spring manejarán este caso de alguna manera. Por ahora, esta es mi forma preferida:

@Bean 
public AnnotationSessionFactoryBean sessionFactoryBean() { 
    AnnotationSessionFactoryBean factory = new AnnotationSessionFactoryBean(); 
    // set up properties etc. 
    return factory; 
} 

@Bean 
public SessionFactory sessionFactory() { 
    return (SessionFactory) sessionFactoryBean().getObject(); 
} 

Y utilizar sessionFactory() método siempre que sea necesario. Si desea llamar al sessionFactoryBean().getObject() varias veces por algún motivo (por ejemplo, cuando FactoryBean no devuelve singletons), recuerde utilizar la anotación @Scope. De lo contrario, Spring se asegurará de que sessionFactory() se invoque solo una vez y guarde en caché el resultado.

El resorte es lo suficientemente inteligente como para realizar todas las inicializaciones necesarias después de llamar al método @Bean y antes de devolver el bean. Así que InitializingBean, DisposableBean, @PostConstruct, etc. son reconocidos y tratados adecuadamente. De hecho, siempre he encontrado que llamar al afterPropertiesSet es un truco, porque es responsabilidad del contenedor.


También hay un segundo método se aconseja en Spring Datastore Document - Reference Documentation, que a primera vista parece un poco contradictorio, pero funciona muy bien:

@Resource 
private Mongo mongo; 

@Bean 
MongoFactoryBean mongo() { 
    return new MongoFactoryBean(); 
} 

Así fábrica es creado usando @Bean método, pero el grano creados por el fábrica se puede obtener utilizando el campo de autocableado. Inteligente.

+0

no se requieren afterPropertiesSet()? –

+0

No, Spring es lo suficientemente inteligente como para realizar todas las inicializaciones necesarias después de llamar al método '@ Bean' y antes de devolver el bean. Así que 'InitializingBean',' DisposableBean', '@ PostConstruct', etc. son reconocidos y tratados adecuadamente. De hecho, siempre he encontrado que llamar a 'afterPropertiesSet' es un poco complicado, porque es responsabilidad del contenedor. –

+1

Además, ver mi edición, encontré otra solución. –

0

Encontré un ejemplo de esto en el Spring forums.

@Bean 
public SessionFactory sessionFactory() { 
    AnnotationSessionFactoryBean sessionFactoryBean = 
       new AnnotationSessionFactoryBean(); 
    // ...configuration code here... 
    sessionFactoryBean.afterPropertiesSet(); 
    return sessionFactoryBean.getObject(); 
} 
+2

Esa es una mala idea. Si hace esto, el ciclo de vida de 'FactoryBean' no se administrará como debería y su aplicación no se cerrará limpiamente. En este ejemplo particular, puede evitar el problema usando '@Bean (destroyMethod =" close ")', pero no es genial. – skaffman

0

Se puede utilizar la siguiente manera:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration 
public class LoginServiceTest { 

    @Configuration 
    public static class Config { 

    @Bean 
    public HttpInvokerProxyFactoryBean loginServiceProxy() { 
     HttpInvokerProxyFactoryBean proxy = new HttpInvokerProxyFactoryBean(); 
     proxy.setServiceInterface(LoginService.class); 
     proxy.setServiceUrl("http://localhost:8080/loginService"); 
     return proxy; 
    } 

    } 

    @Inject 
    private LoginService loginService; 

    @Test 
    public void testlogin() { 
    loginService.login(...); 
    } 
} 
Cuestiones relacionadas