Catatan: Ini dimaksudkan sebagai jawaban kanonik untuk masalah umum.
Saya memiliki @Service
kelas Spring ( MileageFeeCalculator
) yang memiliki @Autowired
bidang ( rateService
), tetapi bidang tersebut adalah null
ketika saya mencoba menggunakannya. Log menunjukkan bahwa baik MileageFeeCalculator
kacang dan MileageRateService
kacang sedang dibuat, tetapi saya mendapatkan NullPointerException
setiap kali saya mencoba memanggil mileageCharge
metode pada kacang layanan saya. Mengapa Spring tidak mengotomasi lapangan?
Kelas pengontrol:
@Controller
public class MileageFeeController {
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
MileageFeeCalculator calc = new MileageFeeCalculator();
return calc.mileageCharge(miles);
}
}
Kelas layanan:
@Service
public class MileageFeeCalculator {
@Autowired
private MileageRateService rateService; // <--- should be autowired, is null
public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile()); // <--- throws NPE
}
}
Kacang layanan yang harus diotomatiskan MileageFeeCalculator
tetapi tidak:
@Service
public class MileageRateService {
public float ratePerMile() {
return 0.565f;
}
}
Ketika saya mencoba GET /mileage/3
, saya mendapatkan pengecualian ini:
java.lang.NullPointerException: null
at com.chrylis.example.spring_autowired_npe.MileageFeeCalculator.mileageCharge(MileageFeeCalculator.java:13)
at com.chrylis.example.spring_autowired_npe.MileageFeeController.mileageFee(MileageFeeController.java:14)
...
F
disebut di dalam konstruktor kacang lainS
. Dalam hal ini, lewati kacang yang diperlukanF
sebagai parameter untukS
konstruktor kacang lainnya dan beri keterangan pada konstruktorS
dengan@Autowire
. Ingat untuk membubuhi keterangan kelas kacang pertamaF
dengan@Component
.Jawaban:
Bidang yang dianotasi
@Autowired
adalahnull
karena Spring tidak tahu tentang salinanMileageFeeCalculator
yang Anda buat dengannew
dan tidak tahu untuk autowire itu.Kontainer Spring Inversion of Control (IoC) memiliki tiga komponen logis utama: registri (disebut Windows 7)
ApplicationContext
) komponen (kacang) yang tersedia untuk digunakan oleh aplikasi, sistem konfigurator yang menyuntikkan dependensi objek ke dalamnya dengan mencocokkan dependensi dengan kacang dalam konteks, dan pemecah ketergantungan yang dapat melihat konfigurasi banyak kacang yang berbeda dan menentukan bagaimana membuat instantiate dan mengkonfigurasinya dalam urutan yang diperlukan.Kontainer IoC bukanlah sihir, dan tidak memiliki cara untuk mengetahui tentang objek-objek Java kecuali jika Anda memberitahukannya. Ketika Anda menelepon
new
, JVM instantiate salinan objek baru dan menyerahkannya langsung kepada Anda - tidak pernah melewati proses konfigurasi. Ada tiga cara untuk mengonfigurasi kacang Anda.Saya telah memposting semua kode ini, menggunakan Spring Boot untuk diluncurkan, di proyek GitHub ini ; Anda dapat melihat proyek yang berjalan penuh untuk setiap pendekatan untuk melihat semua yang Anda butuhkan untuk membuatnya bekerja. Tag dengan
NullPointerException
:nonworking
Suntikkan kacang Anda
Opsi yang paling disukai adalah membiarkan Spring mengotomatiskan semua kacang Anda; ini membutuhkan jumlah kode paling sedikit dan merupakan yang paling bisa dikelola. Untuk membuat autowiring berfungsi seperti yang Anda inginkan, autowire juga
MileageFeeCalculator
seperti ini:Jika Anda perlu membuat instance baru objek layanan Anda untuk permintaan yang berbeda, Anda masih bisa menggunakan injeksi dengan menggunakan lingkup bean Spring .
Tag yang berfungsi dengan menyuntikkan
@MileageFeeCalculator
objek layanan:working-inject-bean
Gunakan @Configurable
Jika Anda benar-benar membutuhkan objek yang dibuat dengan
new
autowired, Anda dapat menggunakan@Configurable
anotasi Spring bersama dengan tenun waktu kompilasi AspectJ untuk menyuntikkan objek Anda. Pendekatan ini menyisipkan kode ke konstruktor objek Anda yang memberitahu Spring bahwa itu dibuat sehingga Spring dapat mengkonfigurasi instance baru. Ini membutuhkan sedikit konfigurasi dalam build Anda (seperti kompilasi denganajc
) dan menyalakan penangan konfigurasi runtime Spring (@EnableSpringConfigured
dengan sintaks JavaConfig). Pendekatan ini digunakan oleh sistem Rekaman Aktif Roo untuk memungkinkannew
instance dari entitas Anda untuk mendapatkan informasi persistensi yang diperlukan disuntikkan.Tag yang berfungsi dengan menggunakan
@Configurable
pada objek layanan:working-configurable
Pencarian kacang manual: tidak disarankan
Pendekatan ini hanya cocok untuk berinteraksi dengan kode lawas dalam situasi khusus. Hampir selalu lebih disukai untuk membuat kelas adaptor tunggal yang dapat diautow oleh autovire dan kode lawas dapat dipanggil, tetapi dimungkinkan untuk secara langsung menanyakan konteks aplikasi Spring untuk sebuah kacang.
Untuk melakukan ini, Anda memerlukan kelas yang Spring dapat memberikan referensi ke
ApplicationContext
objek:Kemudian kode lawas Anda dapat memanggil
getContext()
dan mengambil kacang yang dibutuhkan:Tag yang berfungsi secara manual mencari objek layanan dalam konteks Spring:
working-manual-lookup
sumber
@Configuration
kacang, di mana metode untuk membuat turunan dari kelas kacang tertentu dijelaskan dengan@Bean
.@Bean
metode saat menggunakan Spring Proxy AOP?@Autowired MileageFeeCalculator calc
. Adakah pikiran?ApplicationContext
. Beberapa pengguna (yang saya tutup sebagai duplikat) tidak memahami ini.Jika Anda tidak membuat kode aplikasi web, pastikan kelas Anda di mana @Autowiring dilakukan adalah spring bean. Biasanya, wadah pegas tidak akan menyadari kelas yang mungkin kita anggap sebagai kacang pegas. Kita harus memberi tahu wadah Musim Semi tentang kelas musim semi kita.
Ini dapat dicapai dengan mengkonfigurasi dalam appln-contxt atau cara yang lebih baik adalah dengan membubuhi keterangan kelas sebagai @Component dan tolong jangan membuat kelas beranotasi menggunakan operator baru. Pastikan Anda mendapatkannya dari Appln-context seperti di bawah ini.
sumber
Sebenarnya, Anda harus menggunakan Object yang dikelola JVM atau Object yang dikelola Spring untuk memanggil metode. dari kode di atas di kelas controller Anda, Anda membuat objek baru untuk memanggil kelas layanan Anda yang memiliki objek kabel otomatis.
jadi tidak akan bekerja seperti itu.
Solusinya menjadikan MileageFeeCalculator ini sebagai objek kabel otomatis di Kontroler itu sendiri.
Ubah kelas Controller Anda seperti di bawah ini.
sumber
Saya pernah mengalami masalah yang sama ketika saya tidak terbiasa
the life in the IoC world
. The@Autowired
bidang salah satu dari kacang saya adalah nol pada saat runtime.Akar penyebabnya adalah, alih-alih menggunakan kacang yang dibuat secara otomatis yang dikelola oleh wadah Spring IoC (yang
@Autowired
bidangnyaindeed
disuntikkan dengan benar), saya adalahnewing
contoh saya sendiri dari jenis kacang itu dan menggunakannya. Tentu saja@Autowired
bidang ini nol karena Spring tidak punya kesempatan untuk menyuntikkannya.sumber
Masalah Anda baru (pembuatan objek dalam gaya java)
Dengan penjelasan
@Service
,@Component
,@Configuration
kacang diciptakan dalamkonteks aplikasi Spring ketika server dimulai. Tetapi ketika kita membuat objek menggunakan operator baru objek tidak terdaftar dalam konteks aplikasi yang sudah dibuat. Sebagai contoh kelas Employee.java yang saya gunakan.
Lihat ini:
sumber
Saya baru ke Spring, tetapi saya menemukan solusi yang berfungsi ini. Tolong beritahu saya jika itu cara yang tidak pantas.
Saya membuat Spring menyuntikkan
applicationContext
kacang ini:Anda dapat meletakkan kode ini juga di kelas aplikasi utama jika Anda mau.
Kelas-kelas lain dapat menggunakannya seperti ini:
Dengan cara ini setiap kacang dapat diperoleh oleh objek apa pun dalam aplikasi (juga diperkuat dengan
new
) dan dengan cara statis .sumber
Sepertinya ini adalah kasus yang jarang terjadi, tetapi inilah yang terjadi pada saya:
Kami menggunakan
@Inject
alih-alih@Autowired
yang standar javaee didukung oleh Spring. Setiap tempat itu berfungsi dengan baik dan kacang disuntikkan dengan benar, bukan satu tempat. Suntikan kacang tampaknya samaAkhirnya kami menemukan bahwa kesalahannya adalah kami (sebenarnya, fitur lengkap otomatis Eclipse) diimpor
com.opensymphony.xwork2.Inject
alih - alihjavax.inject.Inject
!Jadi untuk meringkas, make yakin bahwa penjelasan Anda (
@Autowired
,@Inject
,@Service
, ...) memiliki paket yang benar!sumber
Saya pikir Anda telah melewatkan untuk menginstruksikan musim semi untuk memindai kelas dengan anotasi.
Anda dapat menggunakan
@ComponentScan("packageToScan")
pada kelas konfigurasi aplikasi pegas Anda untuk menginstruksikan pegas untuk memindai.@Service, @Component
anotasi dll menambahkan deskripsi meta.Spring hanya menyuntikkan instance dari kelas-kelas yang dibuat sebagai kacang atau ditandai dengan anotasi.
Kelas-kelas yang ditandai dengan anotasi perlu diidentifikasi oleh pegas sebelum menyuntikkan,
@ComponentScan
menginstruksikan mencari pegas untuk kelas-kelas yang ditandai dengan anotasi. Ketika Spring menemukannya@Autowired
mencari kacang terkait, dan menyuntikkan contoh yang diperlukan.Menambahkan anotasi saja, tidak memperbaiki atau memfasilitasi injeksi ketergantungan, Spring perlu tahu ke mana harus mencari.
sumber
<context:component-scan base-package="com.mypackage"/>
kebeans.xml
file sayaJika ini terjadi di kelas tes, pastikan Anda tidak lupa memberi anotasi pada kelas.
Misalnya, di Spring Boot :
Beberapa waktu berlalu ...
Spring Boot terus berkembang . Tidak lagi diperlukan untuk menggunakan
@RunWith
jika Anda menggunakan versi JUnit yang benar .Agar
@SpringBootTest
dapat bekerja sendiri, Anda harus menggunakan@Test
dari JUnit5 bukan JUnit4 .Jika Anda mendapatkan konfigurasi ini salah, pengujian Anda akan dikompilasi, tetapi
@Autowired
dan@Value
bidang (misalnya) akannull
. Karena Spring Boot beroperasi dengan sihir, Anda mungkin memiliki beberapa jalan untuk secara langsung men-debug kegagalan ini.sumber
@Value
akan menjadi nol saat digunakan denganstatic
bidang.Solusi lain akan menelepon:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
Ke konstruktor MileageFeeCalculator seperti ini:
sumber
UPDATE: Orang-orang yang benar-benar pintar dengan cepat menunjukkan jawaban ini , yang menjelaskan keanehan, yang dijelaskan di bawah ini
JAWABAN ASLI:
Saya tidak tahu apakah itu membantu siapa pun, tetapi saya terjebak dengan masalah yang sama bahkan ketika melakukan hal-hal yang tampaknya benar. Dalam metode Utama saya, saya memiliki kode seperti ini:
dan dalam
token.xml
file saya sudah punya garisSaya perhatikan bahwa package.path tidak ada lagi, jadi saya baru saja memutuskan untuk selamanya.
Dan setelah itu, NPE mulai masuk. Dalam
pep-config.xml
aku punya hanya 2 kacang:dan kelas SomeAbac memiliki properti yang dinyatakan sebagai
untuk beberapa alasan yang tidak diketahui, pengaturan adalah null di init (), ketika
<context:component-scan/>
elemen tidak ada sama sekali, tetapi ketika itu ada dan memiliki beberapa bs sebagai basePackage, semuanya berfungsi dengan baik. Baris ini sekarang terlihat seperti ini:dan itu berhasil. Mungkin seseorang dapat memberikan penjelasan, tetapi bagi saya itu sudah cukup sekarang)
sumber
<context:component-scan/>
secara implisit memungkinkan yang<context:annotation-config/>
diperlukan untuk@Autowired
bekerja.Ini adalah biang kerok dari memberikan NullPointerException
MileageFeeCalculator calc = new MileageFeeCalculator();
Kami menggunakan Spring - tidak perlu membuat objek secara manual. Pembuatan objek akan diurus oleh wadah IoC.sumber
Anda juga bisa memperbaiki masalah ini menggunakan anotasi @Service pada kelas layanan dan meneruskan bean classA yang diperlukan sebagai parameter ke konstruktor kacang classB lainnya dan menjelaskan konstruktor classB dengan @Autowired. Cuplikan sampel di sini:
sumber
Apa yang belum disebutkan di sini dijelaskan dalam artikel ini di paragraf "Urutan eksekusi".
Setelah "mengetahui" bahwa saya harus membubuhi keterangan kelas dengan @Component atau turunannya @Service atau @Repository (saya kira ada lebih banyak), untuk mengotomatiskan komponen lain di dalamnya, saya tersadar bahwa komponen lain ini masih nol di dalam konstruktor komponen induk.
Menggunakan @PostConstruct memecahkan itu:
dan:
sumber
Ini hanya berlaku untuk unit test.
Kelas Layanan saya memiliki anotasi layanan dan itu adalah
@autowired
kelas komponen lain. Ketika saya menguji komponen kelas datang nol. Karena untuk kelas layanan saya membuat objek menggunakannew
Jika Anda menulis unit test pastikan Anda tidak membuat objek menggunakan
new object()
. Gunakan sebagai ganti injectMock.Ini memperbaiki masalah saya. Berikut ini tautan yang bermanfaat
sumber
Juga perhatikan bahwa jika, untuk alasan apa pun, Anda membuat metode dalam bentuk
@Service
asfinal
, kacang autowired yang akan Anda akses darinya akan selalu adanull
.sumber
Dengan kata-kata sederhana ada dua alasan utama untuk menjadi
@Autowired
bidangnull
KELAS ANDA BUKAN Kacang SPRING.
LAPANGAN BUKAN Kacang.
sumber
Tidak sepenuhnya terkait dengan pertanyaan, tetapi jika injeksi bidang adalah nol, injeksi berbasis konstruktor masih akan berfungsi dengan baik.
sumber