ejemplo - para que sirve spring
De todos modos, @Autowire un bean que requiere argumentos constructor? (8)
Estoy usando Spring 3.0.5 y estoy usando la anotación @Autowire para los miembros de mi clase tanto como sea posible. Uno de los beans que necesito para autoaumentar requiere argumentos para su constructor. Revisé los documentos de Spring, pero parece que no puedo encontrar ninguna referencia sobre cómo anotar los argumentos de los constructores.
En XML, puedo usarlo como parte de la definición de bean. ¿Hay un mecanismo similar para la anotación @Autowire?
Ex:
@Component
public class MyConstructorClass{
String var;
public MyConstructorClass( String constrArg ){
this.var = var;
}
...
}
@Service
public class MyBeanService{
@Autowired
MyConstructorClass myConstructorClass;
....
}
En este ejemplo, ¿cómo especifico el valor de "constrArg" en MyBeanService con la anotación @Autowire? ¿Hay alguna manera de hacer esto?
Gracias,
Eric
En este ejemplo, ¿cómo especifico el valor de "constrArg" en
MyBeanService
con la anotación@Autowire
? ¿Hay alguna manera de hacer esto?
No, no en la forma en que te refieres. El bean que representa MyConstructorClass
debe ser configurable sin requerir ninguno de sus beans de cliente, por lo que MyBeanService
no tiene voz en cómo se configura MyConstructorClass
.
Esto no es un problema de autoenvío, el problema aquí es cómo Spring MyConstructorClass
instancia de MyConstructorClass
, dado que MyConstructorClass
es un @Component
(y está utilizando el escaneo de componentes, y por lo tanto no especifica un MyConstructorClass
explícitamente en su configuración).
Como dijo @Sean, una respuesta aquí es usar @Value
en el parámetro constructor, de modo que Spring obtenga el valor del constructor de una propiedad del sistema o archivo de propiedades. La alternativa es que MyBeanService
instancia directamente MyConstructorClass
, pero si lo haces, MyConstructorClass
ya no es un Spring Bean.
Bueno, de vez en cuando me encuentro con la misma pregunta. Hasta donde yo sé, uno no puede hacer eso cuando uno quiere agregar parámetros dinámicos al constructor. Sin embargo, el patrón de fábrica puede ayudar.
public interface MyBean {
// here be my fancy stuff
}
public interface MyBeanFactory {
public MyBean getMyBean(/* bean parameters */);
}
@Component
public class MyBeanFactoryImpl implements MyBeanFactory {
@Autowired
WhateverIWantToInject somethingInjected;
public MyBean getMyBean(/* params */) {
return new MyBeanImpl(/* params */);
}
private class MyBeanImpl implements MyBean {
public MyBeanImpl(/* params */) {
// let''s do whatever one has to
}
}
}
@Component
public class MyConsumerClass {
@Autowired
private MyBeanFactory beanFactory;
public void myMethod() {
// here one has to prepare the parameters
MyBean bean = beanFactory.getMyBean(/* params */);
}
}
Ahora, MyBean
no es un grano de primavera per se, pero está lo suficientemente cerca. La Inyección de Dependencia funciona, aunque inyecte la fábrica y no el grano en sí, uno tiene que inyectar una nueva fábrica sobre su propia implementación MyBean
si quiere reemplazarla.
Además, MyBean
tiene acceso a otros beans, ya que puede tener acceso a los autocables de la fábrica.
Y uno podría querer agregar algo de lógica a la función getMyBean
, que es un esfuerzo extra que permito, pero desafortunadamente no tengo una mejor solución. Dado que el problema generalmente es que los parámetros dinámicos provienen de una fuente externa, como una base de datos o interacción del usuario, por lo tanto debo instanciar ese bean solo a mitad de ejecución, solo cuando esa información esté disponible, por lo que la Factory
debería ser bastante adecuada. .
La mayoría de las respuestas son bastante antiguas, por lo que podría no haber sido posible en aquel entonces, pero en realidad hay una solución que satisface todos los casos de uso posibles.
Así que bien, sé que las respuestas son:
- No proporciona un componente real de Spring (el diseño de la fábrica)
- o no se ajusta a todas las situaciones (usando
@Value
tienes que tener el valor en un archivo de configuración en alguna parte)
La solución para resolver esos problemas es crear el objeto manualmente utilizando ApplicationContext
:
@Component
public class MyConstructorClass
{
String var;
public MyConstructorClass() {}
public MyConstructorClass(String constrArg) {
this.var = var;
}
}
@Service
public class MyBeanService implements ApplicationContextAware
{
private static ApplicationContext applicationContext;
MyConstructorClass myConstructorClass;
public MyBeanService()
{
// Creating the object manually
MyConstructorClass myObject = new MyConstructorClass("hello world");
// Initializing the object as a Spring component
AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
factory.autowireBean(myObject);
factory.initializeBean(myObject, myObject.getClass().getSimpleName());
}
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
applicationContext = context;
}
}
Esta es una solución genial porque:
- Te da acceso a todas las funcionalidades de Spring en tu objeto (
@Autowired
obviamente, pero también@Async
por ejemplo), - Puede usar cualquier fuente para sus argumentos de constructor (archivo de configuración, valor calculado, valor codificado, ...),
- Solo requiere agregar algunas líneas de código sin tener que cambiar nada.
- También se puede usar para crear dinámicamente un número desconocido de instancias de una clase administrada por Spring (lo estoy usando para crear múltiples ejecutores asincrónicos sobre la marcha, por ejemplo)
Lo único que debe tener en cuenta es que debe tener un constructor que no tome argumentos (y que pueda estar vacío) en la clase que desea instanciar (o un constructor @Autowired
si lo necesita).
Necesita usar @Autowired y @Value. Consulte esta post para obtener más información sobre este tema.
Necesitas la anotación @Value
.
Un caso de uso común es asignar valores de campo predeterminados utilizando expresiones de estilo
"#{systemProperties.myProp}"
.
public class SimpleMovieLister {
private MovieFinder movieFinder;
private String defaultLocale;
@Autowired
public void configure(MovieFinder movieFinder,
@Value("#{ systemProperties[''user.region''] }"} String defaultLocale) {
this.movieFinder = movieFinder;
this.defaultLocale = defaultLocale;
}
// ...
}
Ver: Lenguaje de Expresión> Configuración de Anotación
Para ser más claro: en su escenario, conectaría dos clases, MybeanService
y MyConstructorClass
, algo como esto:
@Component
public class MyBeanService implements BeanService{
@Autowired
public MybeanService(MyConstructorClass foo){
// do something with foo
}
}
@Component
public class MyConstructorClass{
public MyConstructorClass(@Value("#{some expression here}") String value){
// do something with value
}
}
Actualización: si necesita varias instancias diferentes de MyConstructorClass
con valores diferentes, debe usar las anotaciones de Calificador
Otra alternativa, si ya tiene una instancia del objeto creado y desea agregarla como una dependencia @autowired para inicializar todas las variables internas de @autowired, podría ser la siguiente:
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
public void doStuff() {
YourObject obj = new YourObject("Value X", "etc");
autowireCapableBeanFactory.autowireBean(obj);
}
También puede configurar su componente de esta manera:
package mypackage;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConstructorClassConfig {
@Bean
public MyConstructorClass myConstructorClass(){
return new myConstructorClass("foobar");
}
}
}
Con la anotación Bean
, le está diciendo a Spring que registre el bean devuelto en BeanFactory
.
Para que pueda conectarlo como desee.
Una alternativa sería, en lugar de pasar los parámetros al constructor, tenerlos como getter y setters y luego, en @PostConstruct, inicializar los valores como lo desee. En este caso, Spring creará el bean usando el constructor predeterminado. Un ejemplo está debajo
@Component
public class MyConstructorClass{
String var;
public void setVar(String var){
this.var = var;
}
public void getVar(){
return var;
}
@PostConstruct
public void init(){
setVar("var");
}
...
}
@Service
public class MyBeanService{
//field autowiring
@Autowired
MyConstructorClass myConstructorClass;
....
}