Pertanyaan Melacak penyebab Spring "tidak memenuhi syarat untuk proxy otomatis"


Ketika Anda mulai bermain-main dengan hal-hal auto-proxy Spring, Anda sering mengalami perilaku ini seperti yang didokumentasikan:

Kelas yang menerapkan   Antarmuka BeanPostProcessor adalah   istimewa, sehingga mereka diperlakukan   berbeda dengan wadah. Semua   BeanPostProcessor dan mereka secara langsung   kacang direferensikan akan dipakai   saat startup, sebagai bagian dari spesial   fase startup dari   ApplicationContext, maka semua itu   BeanPostProcessors akan didaftarkan   secara terurut - dan diterapkan   semua kacang lebih lanjut. Sejak AOP   proxy otomatis diimplementasikan sebagai   BeanPostProcessor sendiri, tidak   BeanPostProcessors atau langsung   kacang dirujuk memenuhi syarat untuk   auto-proxying (dan dengan demikian tidak akan memiliki   aspek 'terjalin' ke dalamnya.

Untuk kacang seperti itu, Anda harus melihat   info log message: “Bean 'foo' tidak   memenuhi syarat untuk diproses oleh semua   BeanPostProcessors (misalnya: tidak   memenuhi syarat untuk proxy otomatis) ”.

Dengan kata lain, jika saya menulis BeanPostProcessor saya sendiri, dan kelas tersebut secara langsung mereferensikan kacang lain dalam konteksnya, maka kacang yang direferensikan itu tidak akan memenuhi syarat untuk auto-proxy, dan sebuah pesan dicatat untuk efek itu.

Masalah saya adalah bahwa melacak di mana referensi langsung itu bisa sangat sulit, karena "referensi langsung" sebenarnya bisa menjadi rantai ketergantungan transitif yang akhirnya mengambil setengah biji dalam konteks aplikasi. Semua Musim Semi memberi Anda adalah pesan info tunggal, dan itu tidak banyak membantu, di luar memberitahu Anda ketika kacang telah tertangkap di web referensi ini.

BeanPostProcessor yang saya kembangkan memang memiliki referensi langsung ke kacang lainnya, tetapi ini adalah kumpulan referensi yang sangat terbatas. Meskipun demikian, hampir setiap kacang dalam konteks saya kemudian dikeluarkan dari auto-proksi, menurut pesan log, tetapi saya tidak dapat melihat di mana ketergantungan itu terjadi.

Adakah yang menemukan cara yang lebih baik untuk melacak ini?


36
2017-07-29 17:06


asal


Jawaban:


Ikuti resep ini:

  1. Buka BeanPostProcessorChecker dalam IDE Anda (ini adalah kelas batin AbstractApplicationContext)
  2. Setel breakpoint di if (logger.isInfoEnabled()) { dalam metode postProcessAfterInitialization
  3. Jalankan kode Anda
  4. Saat Anda menekan breakpoint, cari panggilan ke getBean(String,Class<T>) di jejak tumpukan Anda.

    Salah satu panggilan ini akan mencoba membuat BeanPostProcessor. Kacang itu harus menjadi pelakunya.

Latar Belakang

Bayangkan situasi ini:

public class FooPP implements BeanPostProcessor {
    @Autowire
    private Config config;
}

Saat Musim Semi harus menciptakan config (Karena itu ketergantungan FooPP), itu memiliki masalah: Kontrak mengatakan itu semua BeanPostProcessor harus diterapkan untuk setiap kacang yang sedang dibuat. Namun saat Musim Semi membutuhkan config, setidaknya ada satu PP (yaitu FooPP) yang belum siap untuk layanan!

Ini semakin buruk ketika Anda menggunakan @Configuration kelas untuk mendefinisikan kacang ini:

@Configuration
public class BadSpringConfig {
     @Lazy @Bean public Config config() { return new Config(); }
     @Lazy @Bean public FooPP fooPP() { return new FooPP(); }
}

Setiap kelas konfigurasi adalah kacang. Itu berarti membangun pabrik kacang dari BadSpringConfig, Spring perlu menerapkan pasca-prosesor fooPP tetapi untuk melakukan itu, pertama-tama membutuhkan pabrik kacang ...

Dalam contoh ini, mungkin untuk memecahkan salah satu dependensi siklik. Anda bisa membuatnya FooPP melaksanakan BeanFactoryAware untuk menyuntikkan Spring BeanFactory ke dalam prosesor postingan. Dengan begitu, Anda tidak perlu autowiring.

Nanti di kode, Anda dapat dengan malas meminta kacang:

private LazyInit<Config> helper = new LazyInit<Config>() {

    @Override
    protected InjectionHelper computeValue() {
        return beanFactory.getBean( Config.class );
    }
};

@Override
public Object postProcessBeforeInitialization( Object bean, String beanName ) throws BeansException {
     String value = helper.get().getConfig(...);
}

(sumber untuk LazyInit)

Untuk memutus siklus antara pabrik kacang dan prosesor pasca, Anda perlu mengonfigurasi prosesor pasca dalam file konfigurasi XML. Musim semi dapat membaca itu dan membangun semua struktur tanpa menjadi bingung.


24
2017-10-30 16:34



Hanya untuk membawa beberapa penutupan untuk pertanyaan ini, keruntuhan grafik objek terinisialisasi disebabkan oleh BeanPostProcessor menggunakan @Autowired untuk mendapatkan dependensinya, dan mekanisme autowire secara efektif menyebabkan setiap definisi kacang lainnya diinisialisasi sebelum saya BeanPostProcessor mendapat kesempatan untuk memiliki suara dalam masalah ini. Solusinya adalah tidak menggunakan autowiring untuk BPP Anda.


19
2017-09-06 08:09



Tidak yakin apakah itu membantu, tetapi Eclipse IDE musim semi's tampilan grafik Sepertinya itu bisa membantu dalam memilah referensi kacang ..


4
2017-08-02 09:46