1. Gambaran keseluruhan
Spring Framework dilengkapi dengan dua bekas IOC - BeanFactory dan ApplicationContext . The BeanFactory adalah versi yang paling asas bekas IOC, dan ApplicationContext memanjangkan ciri-ciri BeanFactory .
Dalam tutorial ringkas ini, kita akan memahami perbezaan ketara antara dua bekas IOC ini dengan contoh praktikal.
2. Pemuatan Malas vs Pemuatan Bersemangat
BeanFactory memuat kacang mengikut permintaan, sementara ApplicationContext memuatkan semua kacang semasa permulaan . Oleh itu, BeanFactory ringan berbanding dengan ApplicationContext . Mari kita fahami dengan contoh.
2.1. Pemuatan Malas Dengan BeanFactory
Katakan kita mempunyai kelas kacang tunggal yang disebut Pelajar dengan satu kaedah:
public class Student { public static boolean isBeanInstantiated = false; public void postConstruct() { setBeanInstantiated(true); } //standard setters and getters }
Kami akan menentukan kaedah postConstruct () sebagai kaedah init dalam fail konfigurasi BeanFactory kami , ioc-container-difference-example.xml :
Sekarang, mari tulis kes ujian yang mencipta BeanFactory untuk memeriksa sama ada memuat kacang Pelajar :
@Test public void whenBFInitialized_thenStudentNotInitialized() { Resource res = new ClassPathResource("ioc-container-difference-example.xml"); BeanFactory factory = new XmlBeanFactory(res); assertFalse(Student.isBeanInstantiated()); }
Di sini, yang Student objek tidak dimulakan . Dengan kata lain, hanya BeanFactory yang dimulakan . Kacang ditakrifkan dalam kita BeanFactory akan dimuatkan hanya apabila kita jelas memanggil getBean () kaedah .
Mari periksa inisialisasi kacang Pelajar kami di mana kami secara manual memanggil kaedah getBean () :
@Test public void whenBFInitialized_thenStudentInitialized() { Resource res = new ClassPathResource("ioc-container-difference-example.xml"); BeanFactory factory = new XmlBeanFactory(res); Student student = (Student) factory.getBean("student"); assertTrue(Student.isBeanInstantiated()); }
Di sini, kacang Pelajar berjaya dimuat. Oleh itu, BeanFactory hanya memuatkan kacang apabila diperlukan.
2.2. Bersemangat Memuatkan Dengan ApplicationContext
Sekarang, mari kita gunakan ApplicationContext di tempat BeanFactory.
Kami hanya akan menentukan ApplicationContext, dan ia akan memuatkan semua kacang dengan serta-merta dengan menggunakan strategi pemuatan yang bersemangat:
@Test public void whenAppContInitialized_thenStudentInitialized() { ApplicationContext context = new ClassPathXmlApplicationContext("ioc-container-difference-example.xml"); assertTrue(Student.isBeanInstantiated()); }
Di sini, objek Pelajar dibuat walaupun kita belum memanggil kaedah getBean () .
ApplicationContext dianggap sebagai wadah IOC yang berat kerana strategi memuatnya dengan bersungguh-sungguh memuat semua kacang semasa permulaan. BeanFactory ringan sebagai perbandingan dan sangat berguna dalam sistem yang kekurangan memori. Walaupun begitu, kita akan melihat di bahagian seterusnya mengapa ApplicationContext lebih disukai untuk kebanyakan kes penggunaan .
3. Ciri Aplikasi Perusahaan
ApplicationContext meningkatkan BeanFactory dengan gaya yang lebih berorientasikan rangka kerja dan menyediakan beberapa ciri yang sesuai untuk aplikasi perusahaan.
Sebagai contoh, ia menyediakan fungsi pesanan (i18n atau pengantarabangsaan) , fungsi penerbitan acara , suntikan ketergantungan berdasarkan anotasi , dan penyatuan yang mudah dengan ciri-ciri Spring AOP .
Selain itu, ApplicationContext menyokong hampir semua jenis ruang kacang, tetapi BeanFactory hanya menyokong dua ruang lingkup - Singleton dan Prototype . Oleh itu, selalu lebih baik menggunakan ApplicationContext semasa membina aplikasi perusahaan yang kompleks.
4. Pendaftaran automatik BeanFactoryPostProcessor dan BeanPostProcessor
The ApplicationContext secara automatik mendaftarkan BeanFactoryPostProcessor dan BeanPostProcessor pada permulaan. Sebaliknya, BeanFactory tidak mendaftarkan antara muka ini secara automatik.
4.1. Pendaftaran di BeanFactory
Untuk memahami, mari tulis dua kelas.
Pertama, kami mempunyai kelas CustomBeanFactoryPostProcessor , yang menerapkan BeanFactoryPostProcessor :
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor { private static boolean isBeanFactoryPostProcessorRegistered = false; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory){ setBeanFactoryPostProcessorRegistered(true); } // standard setters and getters }
Di sini, kami telah menukar kaedah postProcessBeanFactory () untuk memeriksa pendaftarannya.
Kedua, kami mempunyai kelas lain, CustomBeanPostProcessor , yang menerapkan BeanPostProcessor :
public class CustomBeanPostProcessor implements BeanPostProcessor { private static boolean isBeanPostProcessorRegistered = false; @Override public Object postProcessBeforeInitialization(Object bean, String beanName){ setBeanPostProcessorRegistered(true); return bean; } //standard setters and getters }
Di sini, kami telah menukar kaedah postProcessBeforeInitialization () untuk memeriksa pendaftarannya.
Juga, kami telah mengkonfigurasi kedua-dua kelas dalam fail konfigurasi ioc-container-difference-example.xml kami :
Mari lihat kes ujian untuk memeriksa sama ada kedua-dua kelas ini didaftarkan secara automatik semasa permulaan:
@Test public void whenBFInitialized_thenBFPProcessorAndBPProcessorNotRegAutomatically() { Resource res = new ClassPathResource("ioc-container-difference-example.xml"); ConfigurableListableBeanFactory factory = new XmlBeanFactory(res); assertFalse(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered()); assertFalse(CustomBeanPostProcessor.isBeanPostProcessorRegistered()); }
Seperti yang dapat kita lihat dari ujian kami, pendaftaran automatik tidak berlaku .
Sekarang, mari kita lihat kes ujian yang menambahkannya secara manual di BeanFactory :
@Test public void whenBFPostProcessorAndBPProcessorRegisteredManually_thenReturnTrue() { Resource res = new ClassPathResource("ioc-container-difference-example.xml"); ConfigurableListableBeanFactory factory = new XmlBeanFactory(res); CustomBeanFactoryPostProcessor beanFactoryPostProcessor = new CustomBeanFactoryPostProcessor(); beanFactoryPostProcessor.postProcessBeanFactory(factory); assertTrue(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered()); CustomBeanPostProcessor beanPostProcessor = new CustomBeanPostProcessor(); factory.addBeanPostProcessor(beanPostProcessor); Student student = (Student) factory.getBean("student"); assertTrue(CustomBeanPostProcessor.isBeanPostProcessorRegistered()); }
Di sini, kami menggunakan kaedah postProcessBeanFactory () untuk mendaftarkan CustomBeanFactoryPostProcessor dan kaedah addBeanPostProcessor () untuk mendaftarkan CustomBeanPostProcessor . Kedua-dua mereka berjaya mendaftar dalam kes ini.
4.2. Pendaftaran dalam ApplicationContext
Seperti yang kita maklum sebelumnya, ApplicationContext mendaftarkan kedua-dua kelas secara automatik tanpa menulis kod tambahan.
Mari sahkan kelakuan ini dalam ujian unit:
@Test public void whenAppContInitialized_thenBFPostProcessorAndBPostProcessorRegisteredAutomatically() { ApplicationContext context = new ClassPathXmlApplicationContext("ioc-container-difference-example.xml"); assertTrue(CustomBeanFactoryPostProcessor.isBeanFactoryPostProcessorRegistered()); assertTrue(CustomBeanPostProcessor.isBeanPostProcessorRegistered()); }
As we can see, automatic registration of both classes is successful in this case.
Therefore, it's always advisable to use ApplicationContext because Spring 2.0 (and above) heavily uses BeanPostProcessor.
It's also worth noting that if you're using the plain BeanFactory, then features like transactions and AOP will not take effect (at least not without writing extra lines of code). This may lead to confusion because nothing will look wrong with the configuration.
5. Conclusion
In this article, we've seen the key differences between ApplicationContext and BeanFactory with practical examples.
The ApplicationContext comes with advanced features, including several that are geared towards enterprise applications, while the BeanFactory comes with only basic features. Therefore, it's generally recommended to use the ApplicationContext, and we should use BeanFactory only when memory consumption is critical.
As always, the code for the article is available over on GitHub.