Skop Custom pada Musim Bunga

1. Gambaran keseluruhan

Di luar kotak, Spring menyediakan dua lingkup kacang standar ( "singleton" dan "prototaip" ) yang dapat digunakan dalam aplikasi Spring, ditambah tiga lingkup kacang tambahan ( "permintaan" , "sesi" , dan "globalSession" ) untuk digunakan hanya dalam aplikasi yang sedar web.

Skop kacang standard tidak boleh diganti, dan secara amnya dianggap sebagai amalan yang tidak baik untuk mengatasi ruang lingkup yang sedar tentang web. Namun, anda mungkin mempunyai aplikasi yang memerlukan kemampuan yang berbeza atau tambahan dari yang terdapat di ruang lingkup yang disediakan.

Sebagai contoh, jika anda mengembangkan sistem multi-penyewa, anda mungkin ingin memberikan contoh berasingan kacang atau set kacang tertentu untuk setiap penyewa. Spring menyediakan mekanisme untuk membuat ruang lingkup khusus untuk senario seperti ini.

Dalam tutorial cepat ini, kami akan menunjukkan cara membuat, mendaftar, dan menggunakan skop khusus dalam aplikasi Spring .

2. Membuat Kelas Skop Custom

Untuk membuat ruang lingkup khusus, kita mesti menerapkan antara muka Skop . Dengan berbuat demikian, kita juga harus memastikan bahawa pelaksanaannya selamat dari benang kerana skop dapat digunakan oleh beberapa kilang kacang pada masa yang sama.

2.1. Menguruskan Objek dan Panggilan Balik

Salah satu perkara pertama yang perlu dipertimbangkan ketika melaksanakan kelas Skop khusus adalah bagaimana anda menyimpan dan menguruskan benda-benda yang diliputi dan panggilan balik pemusnahan. Ini dapat dilakukan dengan menggunakan peta atau kelas khusus, misalnya.

Untuk artikel ini, kami akan melakukan ini dengan selamat menggunakan utas menggunakan peta yang disegerakkan.

Mari mula menentukan kelas skop khas kami:

public class TenantScope implements Scope { private Map scopedObjects = Collections.synchronizedMap(new HashMap()); private Map destructionCallbacks = Collections.synchronizedMap(new HashMap()); ... }

2.2. Mengambil Objek dari Skop

Untuk mendapatkan objek dengan nama dari skop kami, mari kita laksanakan kaedah getObject . Seperti yang dinyatakan oleh JavaDoc, jika objek bernama tidak ada dalam ruang lingkup, kaedah ini mesti membuat dan mengembalikan objek baru .

Dalam pelaksanaan kami, kami memeriksa untuk melihat apakah objek bernama di peta kami. Sekiranya ada, kami mengembalikannya, dan jika tidak, kami menggunakan ObjectFactory untuk membuat objek baru, menambahkannya ke peta kami, dan mengembalikannya:

@Override public Object get(String name, ObjectFactory objectFactory) { if(!scopedObjects.containsKey(name)) { scopedObjects.put(name, objectFactory.getObject()); } return scopedObjects.get(name); }

Dari lima kaedah yang ditentukan oleh antara muka Skop , hanya kaedah get yang diperlukan untuk menerapkan sepenuhnya perilaku yang dijelaskan. Empat kaedah lain adalah pilihan dan mungkin membuang UnsupportedOperationException jika mereka tidak memerlukan atau tidak dapat menyokong fungsi.

2.3. Mendaftar Panggilan Pemusnahan

Kita juga mesti melaksanakan kaedah registerDestructionCallback . Kaedah ini memberikan panggilan balik yang akan dilaksanakan ketika objek bernama dihancurkan atau jika ruang lingkup itu sendiri dimusnahkan oleh aplikasi:

@Override public void registerDestructionCallback(String name, Runnable callback) { destructionCallbacks.put(name, callback); }

2.4. Mengeluarkan Objek dari Skop

Seterusnya, mari kita laksanakan kaedah hapus , yang menghilangkan objek yang dinamakan dari ruang lingkup dan juga membuang panggil balik pemusnahan yang didaftarkan, mengembalikan objek yang dikeluarkan:

@Override public Object remove(String name) { destructionCallbacks.remove(name); return scopedObjects.remove(name); }

Perhatikan bahawa adalah tanggungjawab pemanggil untuk benar-benar melaksanakan panggilan balik dan memusnahkan objek yang dikeluarkan .

2.5. Mendapatkan ID Perbualan

Sekarang, mari kita laksanakan kaedah getConversationId . Sekiranya ruang lingkup anda menyokong konsep ID perbualan, anda akan mengembalikannya di sini. Jika tidak, konvensyen adalah mengembalikan nol :

@Override public String getConversationId() { return "tenant"; }

2.6. Menyelesaikan Objek Kontekstual

Akhir sekali, mari kita laksanakan kaedah resolContextualObject . Sekiranya ruang lingkup anda menyokong pelbagai objek kontekstual, anda akan mengaitkan masing-masing dengan nilai kunci, dan anda akan mengembalikan objek yang sesuai dengan parameter kunci yang disediakan . Jika tidak, konvensyen adalah mengembalikan nol :

@Override public Object resolveContextualObject(String key) { return null; }

3. Mendaftar Skop Custom

Untuk membuat wadah Spring mengetahui skop baru anda, anda perlu mendaftarkannya melalui kaedah registerScope pada contoh ConfigurableBeanFactory . Mari kita lihat definisi kaedah ini:

void registerScope(String scopeName, Scope scope);

Parameter pertama, lingkupName , digunakan untuk mengenal pasti / menentukan skop dengan nama uniknya. Parameter kedua, lingkup , adalah contoh sebenarnya dari implementasi Skop khusus yang ingin Anda daftarkan dan gunakan.

Mari buat BeanFactoryPostProcessor tersuai dan daftarkan skop khas kami menggunakan ConfigurableListableBeanFactory :

public class TenantBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException { factory.registerScope("tenant", new TenantScope()); } }

Sekarang, mari tulis kelas konfigurasi Spring yang memuat pelaksanaan BeanFactoryPostProcessor kami :

@Configuration public class TenantScopeConfig { @Bean public static BeanFactoryPostProcessor beanFactoryPostProcessor() { return new TenantBeanFactoryPostProcessor(); } }

4. Menggunakan Skop Custom

Sekarang setelah kami mendaftarkan skop kebiasaan kami, kami dapat menerapkannya pada kacang mana pun seperti yang kami lakukan dengan kacang lain yang menggunakan ruang lingkup selain singleton (skop lalai) - dengan menggunakan anotasi @Scope dan menentukan skop khusus kami mengikut nama.

Mari buat kelas TenantBean yang ringkas - kami akan mengumumkan kacang jenis penyewa jenis ini sebentar lagi:

public class TenantBean { private final String name; public TenantBean(String name) { this.name = name; } public void sayHello() { System.out.println( String.format("Hello from %s of type %s", this.name, this.getClass().getName())); } }

Perhatikan bahawa kami tidak menggunakan anotasi @Component dan @Scope peringkat kelas pada kelas ini.

Sekarang, mari kita tentukan sebilangan kacang yang disewa dalam kelas konfigurasi:

@Configuration public class TenantBeansConfig { @Scope(scopeName = "tenant") @Bean public TenantBean foo() { return new TenantBean("foo"); } @Scope(scopeName = "tenant") @Bean public TenantBean bar() { return new TenantBean("bar"); } }

5. Menguji Skop Custom

Mari tulis ujian untuk melaksanakan konfigurasi skop kustom kami dengan memuatkan ApplicationContext , mendaftarkan kelas Konfigurasi kami , dan mengambil kacang yang disewa penyewa kami:

@Test public final void whenRegisterScopeAndBeans_thenContextContainsFooAndBar() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); try{ ctx.register(TenantScopeConfig.class); ctx.register(TenantBeansConfig.class); ctx.refresh(); TenantBean foo = (TenantBean) ctx.getBean("foo", TenantBean.class); foo.sayHello(); TenantBean bar = (TenantBean) ctx.getBean("bar", TenantBean.class); bar.sayHello(); Map foos = ctx.getBeansOfType(TenantBean.class); assertThat(foo, not(equalTo(bar))); assertThat(foos.size(), equalTo(2)); assertTrue(foos.containsValue(foo)); assertTrue(foos.containsValue(bar)); BeanDefinition fooDefinition = ctx.getBeanDefinition("foo"); BeanDefinition barDefinition = ctx.getBeanDefinition("bar"); assertThat(fooDefinition.getScope(), equalTo("tenant")); assertThat(barDefinition.getScope(), equalTo("tenant")); } finally { ctx.close(); } }

Hasil daripada ujian kami adalah:

Hello from foo of type org.baeldung.customscope.TenantBean Hello from bar of type org.baeldung.customscope.TenantBean

6. Kesimpulannya

Dalam tutorial ringkas ini, kami menunjukkan cara menentukan, mendaftar, dan menggunakan skop khusus pada musim bunga.

Anda boleh membaca lebih lanjut mengenai ruang lingkup khusus dalam Rangka Rangka Musim Semi. Anda juga dapat melihat pelaksanaan Spring dari pelbagai kelas Skop di repositori Spring Framework di GitHub.

Seperti biasa, anda boleh mendapatkan contoh kod yang digunakan dalam artikel ini di projek GitHub.