Spring NoSuchBeanDefinitionException

1. Ikhtisar

Pada artikel ini, kita membahas Spring org.springframework.beans.factory.NoSuchBeanDefinitionException - ini adalah pengecualian umum yang dilontarkan oleh BeanFactory saat mencoba menyelesaikan kacang yang tidak didefinisikan dalam Konteks Musim Semi.

Kami akan mengilustrasikan kemungkinan penyebab masalah ini dan solusi yang tersedia.

Dan tentu saja, pengecualian terjadi saat Anda tidak mengharapkannya; lihat daftar lengkap pengecualian dan solusi di Spring.

2. Penyebab: Jenis Bean Tidak Memenuhi Syarat […] Ditemukan karena Ketergantungan

Penyebab paling umum dari pengecualian ini hanyalah mencoba menyuntikkan kacang yang tidak ditentukan. Misalnya - BeanB memasang kabel di kolaborator - BeanA:

@Component public class BeanA { @Autowired private BeanB dependency; //... }

Sekarang, jika ketergantungan - BeanB - tidak ditentukan dalam Konteks Musim Semi, proses bootstrap akan gagal tanpa pengecualian definisi kacang seperti itu :

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.baeldung.packageB.BeanB] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

Alasannya ditunjukkan dengan jelas oleh Spring: " diharapkan setidaknya 1 biji yang memenuhi syarat sebagai kandidat autowire untuk ketergantungan ini "

Salah satu alasan BeanB mungkin tidak ada dalam konteks - jika kacang diambil secara otomatis oleh pemindaian classpath , dan jika BeanB dianotasi dengan benar sebagai kacang ( @Component , @Repository , @Service , @Controller , dll) - adalah mungkin didefinisikan dalam paket yang tidak dipindai oleh Spring :

package com.baeldung.packageB; @Component public class BeanB { ...}

Sementara pemindaian jalur kelas dapat dikonfigurasi sebagai berikut:

@Configuration @ComponentScan("com.baeldung.packageA") public class ContextWithJavaConfig { ... }

Jika kacang tidak secara otomatis dipindai oleh alih-alih ditentukan secara manual , maka BeanB sama sekali tidak ditentukan dalam Konteks Musim Semi saat ini.

3. Penyebab: Field […] di […] Diperlukan Jenis Bean […] Yang Tidak Dapat Ditemukan

Dalam aplikasi Spring Boot untuk skenario di atas, kami mendapatkan pesan yang berbeda.

Mari kita ambil contoh yang sama di mana BeanB disambungkan ke BeanA tetapi tidak ditentukan:

@Component public class BeanA { @Autowired private BeanB dependency; //... }

Jika kami mencoba menjalankan aplikasi sederhana ini, yang mencoba memuat BeanA :

@SpringBootApplication public class NoSuchBeanDefinitionDemoApp { public static void main(String[] args) { SpringApplication.run(NoSuchBeanDefinitionDemoApp.class, args); } }

Aplikasi akan gagal memulai dengan pesan kesalahan:

*************************** APPLICATION FAILED TO START *************************** Description: Field dependency in com.baeldung.springbootmvc.nosuchbeandefinitionexception.BeanA required a bean of type 'com.baeldung.springbootmvc.nosuchbeandefinitionexception.BeanB' that could not be found. Action: Consider defining a bean of type 'com.baeldung.springbootmvc.nosuchbeandefinitionexception.BeanB' in your configuration.

Di sini, com.baeldung.springbootmvc.nosuchbeandefinitionexception adalah paket untuk BeanA , BeanB dan NoSuchBeanDefinitionDemoApp .

Cuplikan untuk contoh ini dapat ditemukan di proyek Github ini.

4. Penyebab: Jenis Bean yang Memenuhi Syarat […] Tidak Ditentukan

Penyebab lain pengecualian adalah adanya dua definisi kacang dalam konteks, bukan satu. Misalnya, jika sebuah antarmuka - IBeanB diimplementasikan oleh dua kacang - BeanB1 dan BeanB2 :

@Component public class BeanB1 implements IBeanB { // } @Component public class BeanB2 implements IBeanB { // }

Sekarang, jika BeanA melakukan autowires antarmuka ini, Spring tidak akan tahu yang mana dari dua implementasi yang akan dimasukkan:

@Component public class BeanA { @Autowired private IBeanB dependency; ... }

Dan sekali lagi, ini akan menghasilkan NoSuchBeanDefinitionException yang dilemparkan oleh BeanFactory :

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.baeldung.packageB.IBeanB] is defined: expected single matching bean but found 2: beanB1,beanB2

Demikian pula, Spring dengan jelas menunjukkan alasan kegagalan kabel: "diharapkan kacang tunggal yang cocok tetapi ditemukan 2" .

Perhatikan, bagaimanapun, bahwa dalam kasus ini, pengecualian yang tepat yang dilemparkan tidak NoSuchBeanDefinitionException tetapi subclass - yang NoUniqueBeanDefinitionException . Pengecualian baru ini telah diperkenalkan di Spring 3.2.1, untuk alasan ini - untuk membedakan antara penyebab di mana definisi kacang tidak ditemukan dan yang ini - di mana beberapa definisi ditemukan dalam konteks.

Sebelum perubahan ini, pengecualian di atas adalah:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.baeldung.packageB.IBeanB] is defined: expected single matching bean but found 2: beanB1,beanB2

Salah satu solusi untuk masalah ini adalah dengan menggunakan anotasi @Qualifier untuk menentukan secara tepat nama kacang yang ingin kita kawat:

@Component public class BeanA { @Autowired @Qualifier("beanB2") private IBeanB dependency; ... }

Sekarang Spring memiliki cukup informasi untuk membuat keputusan tentang kacang mana yang akan diinjeksi - BeanB1 atau BeanB2 (nama default BeanB2 adalah beanB2 ).

5. Penyebab: Tidak Ada Nama Bean […] Yang Ditentukan

Sebuah NoSuchBeanDefinitionException juga dapat dilemparkan ketika kacang yang tidak didefinisikan adalah diminta oleh nama dari konteks Spring:

@Component public class BeanA implements InitializingBean { @Autowired private ApplicationContext context; @Override public void afterPropertiesSet() { context.getBean("someBeanName"); } }

Dalam kasus ini, tidak ada definisi kacang untuk "someBeanName" - yang mengarah ke pengecualian berikut:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'someBeanName' is defined

Sekali lagi, Spring dengan jelas dan ringkas menunjukkan alasan kegagalan: " Tidak ada kacang bernama X yang ditentukan ".

6. Penyebab: Proxied Beans

Ketika bean dalam konteks di-proxy-kan menggunakan mekanisme JDK Dynamic Proxy, maka proxy tersebut tidak akan memperluas bean target (bagaimanapun, ia akan mengimplementasikan interface yang sama).

Karena itu, jika kacang diinjeksi oleh sebuah antarmuka, ia akan disambungkan dengan benar. Namun jika kacang disuntikkan oleh kelas yang sebenarnya, maka Spring tidak akan menemukan definisi kacang yang cocok dengan kelas - karena proxy sebenarnya tidak memperluas kelas.

Alasan yang sangat umum mengapa kacang dapat diproksikan adalah dukungan transaksional Musim Semi - yaitu kacang yang diberi anotasi @Transactional .

Misalnya, jika ServiceA memasukkan ServiceB , dan kedua layanan tersebut bersifat transaksional, memasukkan definisi kelas tidak akan berfungsi:

@Service @Transactional public class ServiceA implements IServiceA{ @Autowired private ServiceB serviceB; ... } @Service @Transactional public class ServiceB implements IServiceB{ ... }

Dua layanan yang sama, kali ini dimasukkan dengan benar oleh antarmuka , akan baik-baik saja:

@Service @Transactional public class ServiceA implements IServiceA{ @Autowired private IServiceB serviceB; ... } @Service @Transactional public class ServiceB implements IServiceB{ ... }

7. Kesimpulan

Tutorial ini membahas contoh kemungkinan penyebab umum NoSuchBeanDefinitionException - dengan fokus pada cara menangani pengecualian ini dalam praktiknya.

Penerapan semua contoh pengecualian ini dapat ditemukan di proyek GitHub - ini adalah proyek berbasis Eclipse, jadi semestinya mudah untuk mengimpor dan menjalankannya apa adanya.

Terakhir, daftar lengkap pengecualian dan solusi di Spring mungkin merupakan sumber yang baik untuk menandai.