Bagaimana cara menyuntikkan dependensi ke objek self-instantiated di Spring?

128

Katakanlah kita memiliki kelas:

public class MyClass {
    @Autowired private AnotherBean anotherBean;
}

Kemudian kami membuat objek kelas ini (atau beberapa kerangka kerja lain telah membuat instance dari kelas ini).

MyClass obj = new MyClass();

Apakah mungkin masih menyuntikkan dependensi? Sesuatu seperti:

applicationContext.injectDependencies(obj);

(Saya pikir Google Guice memiliki sesuatu seperti ini)

Igor Mukhin
sumber

Jawaban:

194

Anda dapat melakukan ini menggunakan autowireBean()metode AutowireCapableBeanFactory. Anda memberikan objek sewenang-wenang, dan Spring akan memperlakukannya seperti sesuatu yang dibuat sendiri, dan akan menerapkan berbagai bit dan potongan autowiring.

Untuk mendapatkan AutowireCapableBeanFactory, hanya autowire bahwa:

private @Autowired AutowireCapableBeanFactory beanFactory;

public void doStuff() {
   MyBean obj = new MyBean();
   beanFactory.autowireBean(obj);
   // obj will now have its dependencies autowired.
}
skaffman
sumber
Jawaban bagus (+1). Ada juga metode kedua di mana Anda dapat mempengaruhi bagaimana autowiring terjadi: static.springsource.org/spring/docs/3.0.x/javadoc-api/org/…
Sean Patrick Floyd
Tetapi bagaimana jika saya memiliki dua objek, dan autowires pertama kedua. Bagaimana pabrik kacang autowire menangani ketergantungan dalam kasus ini?
Vadim Kirilchuk
3
Ini sebenarnya pola yang buruk. Jika ini adalah bagaimana Anda benar-benar menggunakan MyBean mengapa tidak hanya memiliki konstruktor dengan AnotherBean sebagai parameter. Sesuatu seperti: codeprivate @Autowired AnotherBean bean; public void doStuff () {MyBean obj = new MyBean (bean); } code. Tampaknya seperti dengan semua anotasi ini orang menjadi benar-benar bingung dan tidak menggunakan pola dasar yang ada di java SDK sejak hari pertama: :(
Denis
3
Saya setuju - Spring telah mendefinisikan ulang seluruh bahasa. Sekarang kita menggunakan antarmuka sebagai kelas konkret, metode sebagai instance kelas dan segala macam metode rumit dan kode melakukan apa yang Anda lakukan dengan desain baru dan yang layak.
Rodney P. Barbati
@ Apakah jika MyBean memiliki dependensi yang tidak diperlukan oleh kelas aktual, Anda mendaftarkan dependensi yang sebenarnya tidak ada, hanya untuk instance kelas, jadi benar-benar tidak ada perbedaan.
Dalton
17

Anda juga dapat menandai MyClass Anda dengan anotasi @Configurable:

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

Kemudian pada saat pembuatan ia akan secara otomatis menyuntikkan dependensinya. Anda juga harus memiliki <context:spring-configured/>dalam konteks aplikasi Anda xml.

glaz666
sumber
2
Galz666, metode Anda terlihat jauh lebih bersih untuk apa yang ingin saya lakukan. Namun saya tidak bisa membuatnya bekerja. Saya tidak punya file xml dan saya menggunakan sepenuhnya konfigurasi Java. Apakah ada yang setara dengan <context:spring-configured/>?
masstroy
1
Ini adalah solusi bersih, tetapi membutuhkan sedikit usaha lebih: Anda harus menggunakan tenun waktu-beban seperti yang ditunjukkan iimuhin di atas atau menambahkan kompiler AspectJ ke proyek. Load-time seperti namanya akan menyebabkan biaya tambahan pada saat run-time.
jsosnowski
4

Baru saja mendapatkan kebutuhan yang sama dan dalam kasus saya sudah logika di dalam kelas java non Spring dikelola yang memiliki akses ke ApplicationContext. Terinspirasi oleh scaffman. Dipecahkan oleh:

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

Saya ingin berbagi solusi yang mengikuti @Configurablependekatan seperti yang brieflydisebutkan dalam jawaban @ glaz666 karena

  • The jawabannya oleh @skaffman berusia hampir 10 tahun, dan itu tidak berarti tidak cukup baik atau tidak bekerja
  • Jawaban oleh @ glaz666 singkat dan tidak benar-benar membantu saya memecahkan masalah saya tetapi, memang mengarahkan saya ke arah yang benar

Pengaturan saya

  1. Spring Boot 2.0.3 with Spring Neo4j & Aop starts(yang bagaimanapun juga tidak relevan)
  2. Instantiate bean ketika Spring Bootsiap menggunakan @Configurablependekatan (menggunakan ApplicationRunner)
  3. Gradle & Eclipse

Langkah

Saya perlu mengikuti langkah-langkah di bawah untuk membuatnya berfungsi

  1. Untuk @Configurable(preConstruction = true, autowire = Autowire.BY_TYPE, dependencyCheck = false)ditempatkan di atas Anda Beanyang akan digunakan secara manual. Dalam kasus saya Beanyang akan digunakan secara manual memiliki @Autowiredlayanan karenanya, alat untuk penjelasan di atas.
  2. Membubuhi keterangan utama Boot Spring XXXApplicaiton.java(atau file yang dijelaskan dengan @SpringBootApplication) dengan @EnableSpringConfigureddan@EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.ENABLED)
  3. Tambahkan dependensi dalam file build Anda (mis. Build.gradle atau pom.xml tergantung yang mana yang Anda gunakan) compile('org.springframework.boot:spring-boot-starter-aop')dancompile('org.springframework:spring-aspects:5.0.7.RELEASE')
  4. Baru + up Anda Beanyang dianotasi dengan @Configurablemana saja dan dependensinya harus diautomatiskan.

* Berkenaan dengan poin # 3 di atas, saya menyadari bahwa secara org.springframework.boot:spring-boot-starter-aoptransitif menarik spring-aop(seperti yang ditunjukkan di sini mavencentral ) tetapi, dalam kasus saya Eclipse gagal menyelesaikan @EnableSpringConfiguredanotasi karenanya, mengapa saya secara eksplisit menambahkan spring-aopketergantungan selain starter. Jika Anda menghadapi masalah yang sama, cukup nyatakan ketergantungan atau pergilah mencari tahu

  • Apakah ada konflik versi
  • Mengapa org.springframework.context.annotation.aspect.*tidak tersedia?
  • Apakah pengaturan IDE Anda benar
  • Dll.
Raf
sumber