¿Cómo inyectar dependencias en un objeto auto-instanciado en Spring?

128

Digamos que tenemos una clase:

public class MyClass {
    @Autowired private AnotherBean anotherBean;
}

Luego creamos un objeto de esta clase (o algún otro marco ha creado la instancia de esta clase).

MyClass obj = new MyClass();

¿Es posible inyectar todavía las dependencias? Algo como:

applicationContext.injectDependencies(obj);

(Creo que Google Guice tiene algo como esto)

Igor Mukhin
fuente

Respuestas:

194

Puedes hacer esto usando el autowireBean()método de AutowireCapableBeanFactory. Se le pasa un objeto arbitrario, y Spring lo tratará como algo que creó por sí mismo, y aplicará los diversos bits y piezas de cableado automático.

Para hacerte con el AutowireCapableBeanFactory, solo autoconecta eso:

private @Autowired AutowireCapableBeanFactory beanFactory;

public void doStuff() {
   MyBean obj = new MyBean();
   beanFactory.autowireBean(obj);
   // obj will now have its dependencies autowired.
}
skaffman
fuente
Buena respuesta (+1). También hay un segundo método en el que puede influir en la forma en que se realiza el cableado automático: static.springsource.org/spring/docs/3.0.x/javadoc-api/org/…
Sean Patrick Floyd
Pero, ¿qué pasa si tengo dos objetos, y el primer autoconexión en segundo lugar? ¿Cómo maneja la fábrica de frijoles autowire las dependencias en el caso?
Vadim Kirilchuk
3
Esto es realmente un mal patrón. Si así es como realmente usa MyBean, ¿por qué no simplemente tener un constructor con AnotherBean como parámetro? Algo así como: codeprivado @Autowired AnotherBean bean; public void doStuff () {MyBean obj = new MyBean (bean); } code. Parece ser como con todas estas anotaciones, la gente se confunde mucho y simplemente no usa el patrón básico que estaba en Java SDK desde el día 1. :(
Denis
3
Estoy de acuerdo: Spring ha redefinido todo el idioma. Ahora usamos interfaces como clases concretas, métodos como instancias de clase y todo tipo de métodos complicados y pesados ​​para hacer lo que solía hacer con un diseño nuevo y decente.
Rodney P. Barbati
@Denis si MyBean tiene dependencias que la clase real no necesita, está registrando dependencias que en realidad no existen, solo para instanciar una clase, por lo que realmente no hay diferencia.
Dalton
17

También puede marcar su MyClass con la anotación @Configurable:

@Configurable
public class MyClass {
   @Autowired private AnotherClass instance
}

Luego, en el momento de la creación, inyectará automáticamente sus dependencias. También debe tener <context:spring-configured/>en su aplicación el contexto xml.

glaz666
fuente
2
Galz666, su método se ve mucho más limpio para lo que quiero hacer. Sin embargo, no puedo hacer que funcione. No tengo un archivo xml y estoy usando completamente la configuración de Java. ¿Hay un equivalente a <context:spring-configured/>?
Masstroy
1
Esta es una solución limpia, pero requiere un poco más de esfuerzo: debe usar el tejido de tiempo de carga como muestra iimuhin arriba o agregar el compilador AspectJ al proyecto. El tiempo de carga, como su nombre indica, causará un costo adicional en tiempo de ejecución.
jsosnowski
4

Acabo de tener la misma necesidad y en mi caso ya era la lógica dentro de la clase Java no manejable de Spring a la que tenía acceso ApplicationContext. Inspirado en scaffman. Resuelto por:

AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
factory.autowireBean(manuallyCreatedInstance);
rand0m86
fuente
3

Quería compartir mi solución que sigue el @Configurableenfoque como se brieflymenciona en la respuesta @ glaz666 porque

  • La respuesta de @skaffman tiene casi 10 años, y eso no significa que no sea lo suficientemente bueno o que no funcione
  • La respuesta de @ glaz666 es breve y realmente no me ayudó a resolver mi problema, pero sí me indicó la dirección correcta

Mi configuración

  1. Spring Boot 2.0.3 con Spring Neo4j & Aop starts(que de todos modos es irrelevante)
  2. Instanciar un bean cuando Spring Bootesté listo usando el @Configurableenfoque (usando ApplicationRunner)
  3. Gradle & Eclipse

Pasos

Necesitaba seguir los pasos a continuación para que funcione

  1. El @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE, dependencyCheck = false)que se colocará encima de usted Beanse instanciará manualmente. En mi caso, la Beaninstancia que se va a crear manualmente tiene @Autowiredservicios, por lo tanto, los accesorios de la anotación anterior.
  2. Anote el XXXApplicaiton.javaarchivo principal de Spring Boot (o el archivo que está anotado @SpringBootApplication) con el @EnableSpringConfiguredy@EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.ENABLED)
  3. Agregue las dependencias en su archivo de compilación (es decir, build.gradle o pom.xml dependiendo de cuál use) compile('org.springframework.boot:spring-boot-starter-aop')ycompile('org.springframework:spring-aspects:5.0.7.RELEASE')
  4. Nuevo + tu Beanque está anotado en @Configurablecualquier lugar y sus dependencias deben conectarse automáticamente.

* En lo que respecta al punto n. ° 3 anterior, soy consciente de que el tirón de formaorg.springframework.boot:spring-boot-starter-aop transitiva spring-aop(como se muestra aquí principalmente ) pero, en mi caso, el Eclipse no pudo resolver las @EnableSpringConfiguredanotaciones, por lo tanto, por qué agregué explícitamente la spring-aopdependencia además del iniciador. Si enfrenta el mismo problema, simplemente declare la dependencia o emprenda la aventura de descubrir

  • ¿Hay un conflicto de versión?
  • Por qué org.springframework.context.annotation.aspect.*no está disponible
  • ¿Su IDE está configurado correctamente?
  • Etcétera etcétera.
Raf
fuente