Memahami Spring @Autowired use

309

Saya membaca dokumentasi referensi spring 3.0.x untuk memahami anotasi Spring Autowired:

3.9.2 @Autowired dan @Inject

Saya tidak dapat memahami contoh di bawah ini. Apakah kita perlu melakukan sesuatu dalam XML agar bisa berfungsi?

CONTOH 1

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

CONTOH 2

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
                    CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

Bagaimana kedua kelas dapat diterapkan secara otomatis dengan mengimplementasikan antarmuka yang sama dan menggunakan kelas yang sama?

Contoh:

class Red implements Color
class Blue implements Color

class myMainClass{
    @Autowired 
    private Color color;

    draw(){
        color.design(); 
    } 
}

Metode desain mana yang akan dipanggil? Bagaimana cara memastikan metode desain kelas Merah akan dipanggil dan bukan Biru?

Pertanyaan Baru
sumber

Jawaban:

542

TL; DR

@Autowired annotation membuat Anda perlu melakukan pengkabelan sendiri dalam file XML (atau cara lain) dan hanya menemukan bagi Anda apa yang perlu disuntikkan di mana, dan melakukannya untuk Anda.

Penjelasan lengkap

The @Autowiredpenjelasan memungkinkan Anda untuk melewatkan konfigurasi tempat lain apa untuk menyuntikkan dan hanya melakukannya untuk Anda. Dengan asumsi paket com.mycompany.moviesAnda adalah Anda harus meletakkan tag ini di XML (file konteks aplikasi) Anda:

<context:component-scan base-package="com.mycompany.movies" />

Tag ini akan melakukan pemindaian otomatis. Dengan asumsi setiap kelas yang harus menjadi kacang diberi penjelasan dengan penjelasan yang benar seperti @Component(untuk kacang sederhana) atau @Controller(untuk kontrol servlet) atau @Repository(untuk DAOkelas) dan kelas-kelas ini berada di suatu tempat di bawah paket com.mycompany.movies, Spring akan menemukan semua ini dan membuat kacang untuk masing-masing. Ini dilakukan dalam 2 pemindaian kelas - pertama kali hanya mencari kelas yang perlu menjadi kacang dan memetakan injeksi yang perlu dilakukan, dan pada pemindaian kedua itu menyuntikkan kacang. Tentu saja, Anda dapat mendefinisikan kacang Anda dalam file XML yang lebih tradisional atau dengan kelas @Configuration (atau kombinasi ketiganya).

The @Autowiredpenjelasan mengatakan musim semi di mana suntikan perlu terjadi. Jika Anda menaruhnya di atas metode, setMovieFinderia memahami (dengan awalan set+ @Autowiredpenjelasan) bahwa kacang perlu disuntikkan. Dalam pemindaian kedua, Spring mencari kacang jenis MovieFinder, dan jika menemukan kacang seperti itu, ia menyuntikkannya ke metode ini. Jika menemukan dua kacang seperti itu, Anda akan mendapatkan Exception. Untuk menghindarinya Exception, Anda dapat menggunakan @Qualifieranotasi dan memberi tahu yang mana dari kedua kacang yang akan disuntikkan dengan cara berikut:

@Qualifier("redBean")
class Red implements Color {
   // Class code here
}

@Qualifier("blueBean")
class Blue implements Color {
   // Class code here
}

Atau jika Anda lebih suka menyatakan kacang di XML Anda, itu akan terlihat seperti ini:

<bean id="redBean" class="com.mycompany.movies.Red"/>

<bean id="blueBean" class="com.mycompany.movies.Blue"/>

Dalam @Autowireddeklarasi, Anda juga perlu menambahkan @Qualifieruntuk memberi tahu yang mana dari dua kacang warna yang akan disuntikkan:

@Autowired
@Qualifier("redBean")
public void setColor(Color color) {
  this.color = color;
}

Jika Anda tidak ingin menggunakan dua anotasi ( @Autowireddan @Qualifier) Anda dapat menggunakan @Resourceuntuk menggabungkan keduanya:

@Resource(name="redBean")
public void setColor(Color color) {
  this.color = color;
}

The @Resource(Anda dapat membaca beberapa data tambahan tentang hal itu di komentar pertama pada jawaban ini) suku cadang Anda menggunakan dua penjelasan dan sebagai gantinya Anda hanya menggunakan satu.

Saya hanya akan menambahkan dua komentar lagi:

  1. Praktik yang baik akan digunakan @Injectbukan @Autowiredkarena tidak spesifik Musim Semi dan merupakan bagian dari JSR-330standar .
  2. Praktik bagus lainnya adalah meletakkan @Inject/ @Autowiredpada konstruktor alih-alih metode. Jika Anda meletakkannya di konstruktor, Anda dapat memvalidasi bahwa kacang yang disuntikkan tidak nol dan gagal cepat ketika Anda mencoba memulai aplikasi dan menghindari NullPointerExceptionketika Anda benar-benar harus menggunakan kacang.

Pembaruan : Untuk melengkapi gambar, saya membuat pertanyaan baru tentang @Configurationkelas.

Avi
sumber
6
Hanya untuk melengkapi jawaban Anda yang luar biasa: '@Resource' adalah bagian dari standar JSR-250 dan memiliki semantik tambahan di atas dan di atas injeksi sederhana (Seperti yang Anda katakan '@Autowired' berasal dari Spring; dan '@Inject' adalah bagian dari JSR-330) :)
Ignacio Rubio
Jika MovieFinderadalah Antarmuka, dan kami memiliki kacang untuk MovieFinderImpl(bean id = movieFinder), Spring akan otomatis menyuntikkannya berdasarkan jenis atau nama?
Jaskey
@ jaskey - tergantung pada apakah Anda menggunakan @Qualifier. Jika Anda - menurut nama, jika tidak - berdasarkan jenis. Berdasarkan jenis akan bekerja hanya jika Anda hanya memiliki satu jenis kacang MovieFinderdalam konteks Anda. Lebih dari 1 akan menghasilkan pengecualian.
Avi
@ Avi, jawaban yang luar biasa. Tetapi saya tidak mengerti bagaimana cara kerja @Autowiredanotasi pada preparemetode pada Contoh 2 . Ini menginisialisasi MovieRecommendertetapi, secara teknis, BUKAN setter.
Karan Chadha
@KaranChadha - Ini @Autowiredjuga berfungsi untuk konstruktor. Ia menemukan dependensi yang diperlukan dan menyuntikkannya ke konstruktor.
Avi
21

Tidak ada dalam contoh mengatakan bahwa "kelas mengimplementasikan antarmuka yang sama". MovieCatalogadalah tipe dan CustomerPreferenceDaotipe lain. Musim semi dapat dengan mudah membedakan mereka.

Pada Musim Semi 2.x, kabel biji sebagian besar terjadi melalui ID kacang atau nama. Ini masih didukung oleh Spring 3.x tetapi sering kali, Anda akan memiliki satu contoh kacang dengan jenis tertentu - sebagian besar layanan adalah lajang. Membuat nama untuk itu membosankan. Jadi Spring mulai mendukung "autowire by type".

Apa yang ditunjukkan contoh adalah berbagai cara yang dapat Anda gunakan untuk menyuntikkan kacang ke dalam bidang, metode, dan konstruktor.

XML sudah berisi semua informasi yang dibutuhkan Spring karena Anda harus menentukan nama kelas yang sepenuhnya memenuhi syarat di setiap kacang. Anda harus sedikit berhati-hati dengan antarmuka, meskipun:

Autowiring ini akan gagal:

 @Autowired
 public void prepare( Interface1 bean1, Interface1 bean2 ) { ... }

Karena Java tidak menyimpan nama parameter dalam kode byte, Spring tidak dapat membedakan antara dua kacang lagi. Cara mengatasinya adalah dengan menggunakan @Qualifier:

 @Autowired
 public void prepare( @Qualifier("bean1") Interface1 bean1,
     @Qualifier("bean2")  Interface1 bean2 ) { ... }
Aaron Digulla
sumber
@ Aaron Digulla Itu bagus. Namun saya ingin tahu bagaimana Anda memanggil fungsi prepare, parameter mana yang akan digunakan untuk memanggil fungsi ini?
Nguyen Quang Anh
@NguyenQuangAnh Saya tidak memanggil metode, Spring akan melakukannya ketika kacang dibuat. Ini terjadi persis ketika @Autowiredbidang disuntikkan. Pegas kemudian akan melihat bahwa parameter diperlukan dan akan menggunakan aturan yang sama yang digunakan untuk injeksi lapangan untuk menemukan parameter.
Aaron Digulla
5

Ya, Anda dapat mengonfigurasi file xml konteks servlet Spring untuk menetapkan kacang Anda (yaitu, kelas), sehingga dapat melakukan injeksi otomatis untuk Anda. Namun, harap dicatat, bahwa Anda harus melakukan konfigurasi lain untuk menjalankan dan menjalankan Spring dan cara terbaik untuk melakukannya, adalah dengan mengikuti tutorial ke atas.

Setelah Musim Semi Anda dikonfigurasi mungkin, Anda dapat melakukan hal berikut dalam file xml konteks Musim Semi servlet Anda untuk Contoh 1 di atas untuk bekerja (harap ganti nama paket com.movies dengan apa nama paket yang sebenarnya dan jika ini adalah pihak ke-3 kelas, maka pastikan file jar yang sesuai ada di classpath):

<beans:bean id="movieFinder" class="com.movies.MovieFinder" />

atau jika kelas MovieFinder memiliki konstruktor dengan nilai primitif, maka Anda bisa seperti ini,

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg value="100" />
</beans:bean>

atau jika kelas MovieFinder memiliki konstruktor yang mengharapkan kelas lain, maka Anda bisa melakukan sesuatu seperti ini,

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg ref="otherBeanRef" />
</beans:bean>

... di mana ' otherBeanRef ' adalah kacang lain yang memiliki referensi ke kelas yang diharapkan.

Cem Sultan
sumber
4
Mendefinisikan semua kabel dalam XML hanya melewatkan seluruh ide@Autowired
Avi