Come iniettare dipendenze in un oggetto autoistanziato in primavera?


128

Diciamo che abbiamo una classe:

public class MyClass {
    @Autowired private AnotherBean anotherBean;
}

Quindi abbiamo creato un oggetto di questa classe (o qualche altro framework ha creato l'istanza di questa classe).

MyClass obj = new MyClass();

È possibile iniettare ancora le dipendenze? Qualcosa di simile a:

applicationContext.injectDependencies(obj);

(Penso che Google Guice abbia qualcosa del genere)

Risposte:


194

Puoi farlo usando il autowireBean()metodo di AutowireCapableBeanFactory. Gli passi un oggetto arbitrario e Spring lo tratterà come qualcosa che ha creato da solo e applicherà i vari bit e pezzi di autowiring.

Per ottenere il AutowireCapableBeanFactory, basta autowire che:

private @Autowired AutowireCapableBeanFactory beanFactory;

public void doStuff() {
   MyBean obj = new MyBean();
   beanFactory.autowireBean(obj);
   // obj will now have its dependencies autowired.
}

Buona risposta (+1). C'è anche un secondo metodo in cui puoi influenzare come avviene l'autowiring: static.springsource.org/spring/docs/3.0.x/javadoc-api/org/…
Sean Patrick Floyd

Ma cosa succede se ho due oggetti e il primo si autowires secondo. In che modo la fabbrica di bean autowire gestisce le dipendenze nel caso?
Vadim Kirilchuk,

3
Questo è in realtà un cattivo modello. Se è così che usi davvero MyBean, perché non avere solo il costruttore con AnotherBean come parametro. Qualcosa del tipo: codeprivate @Autowired bean AnotherBean; public void doStuff () {MyBean obj = new MyBean (bean); } code. Sembra essere come con tutte queste annotazioni le persone si confondono davvero e semplicemente non usano lo schema di base che era in Java SDK dal primo giorno :(
Denis

3
Sono d'accordo: la primavera ha ridefinito l'intera lingua. Ora usiamo le interfacce come classi concrete, i metodi come istanze di classe e tutti i tipi di metodi complicati e pesanti del codice per fare quello che eri solito fare con un design nuovo e decente.
Rodney P. Barbati,

@Denis se MyBean ha dipendenze di cui la classe reale non ha bisogno, stai registrando dipendenze che in realtà non esistono, solo per esempio una classe, quindi non c'è davvero alcuna differenza.
Dalton,

17

Puoi anche contrassegnare MyClass con l'annotazione @Configurable:

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

Quindi al momento della creazione inietterà automaticamente le sue dipendenze. Dovresti anche avere <context:spring-configured/>nel tuo contesto di applicazione XML.


2
Galz666, il tuo metodo sembra molto più pulito per quello che voglio fare. Tuttavia non riesco a farlo funzionare. Non ho un file XML e sto usando la configurazione interamente Java. C'è un equivalente a <context:spring-configured/>?
masstroy,

1
Questa è una soluzione pulita, ma richiede un po 'più di sforzo: dovresti usare la tessitura a tempo di caricamento come iimuhin mostra sopra o aggiungere il compilatore AspectJ al progetto. Il tempo di caricamento come suggerisce il nome comporterà costi aggiuntivi in ​​fase di esecuzione.
jsosnowski,

4

Ho appena avuto lo stesso bisogno e nel mio caso era già la logica all'interno della classe java non gestibile Spring che aveva accesso ApplicationContext. Ispirato allo scaffman. Risolto da:

AutowireCapableBeanFactory factory = applicationContext.getAutowireCapableBeanFactory();
factory.autowireBean(manuallyCreatedInstance);

3

Volevo condividere la mia soluzione che segue l' @Configurableapproccio come brieflyindicato nella risposta @ glaz666 perché

  • La risposta di @skaffman ha quasi 10 anni e ciò non significa che non sia abbastanza buono o non funzioni
  • La risposta di @ glaz666 è breve e non mi ha davvero aiutato a risolvere il mio problema, ma mi ha indicato la giusta direzione

La mia configurazione

  1. Spring Boot 2.0.3 con Spring Neo4j & Aop starts(che è comunque irrilevante)
  2. Crea un'istanza di un bean quando Spring Bootè pronto usando l' @Configurableapproccio (usando ApplicationRunner)
  3. Gradle & Eclipse

passi

Avevo bisogno di seguire i passaggi seguenti per farlo funzionare

  1. Il @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE, dependencyCheck = false)da posizionare sopra il tuo Beanche deve essere istanziato manualmente. Nel mio caso, quello Beanche deve essere istanziato manualmente ha dei @Autowiredservizi quindi, i sostegni per l'annotazione sopra.
  2. Annota il principale di Spring Boot XXXApplicaiton.java(o il file annotato @SpringBootApplication) con @EnableSpringConfigurede@EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.ENABLED)
  3. Aggiungi le dipendenze nel tuo file di build (ad esempio build.gradle o pom.xml a seconda di quello che usi) compile('org.springframework.boot:spring-boot-starter-aop')ecompile('org.springframework:spring-aspects:5.0.7.RELEASE')
  4. New + up your Beanche è annotato @Configurableovunque e le sue dipendenze dovrebbero essere autowired.

* Per quanto riguarda al punto di cui sopra # 3, sono consapevole che il org.springframework.boot:spring-boot-starter-aoptira transitivamente il spring-aop(come mostrato qui mavencentral ), ma, nel mio caso l'Eclipse non è riuscito a risolvere i @EnableSpringConfiguredannotazioni di conseguenza, il motivo per cui ho aggiunto esplicitamente il spring-aopdipendenze oltre al motorino di avviamento. Se dovessi affrontare lo stesso problema, devi semplicemente dichiarare la dipendenza o andare all'avventura di capire

  • Esiste un conflitto di versione
  • Perché org.springframework.context.annotation.aspect.*non è disponibile
  • Il tuo IDE è configurato correttamente
  • Ecc. Ecc.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.