Panduan Cepat untuk Spring Bean Scopes

1. Ikhtisar

Dalam tutorial singkat ini, Anda akan belajar tentang berbagai jenis cakupan kacang dalam framework Spring.

Ruang lingkup kacang mendefinisikan siklus hidup dan visibilitas kacang itu dalam konteks penggunaannya.

Versi terbaru kerangka kerja Spring mendefinisikan 6 jenis cakupan:

  • tunggal
  • prototipe
  • permintaan
  • sidang
  • aplikasi
  • websocket

Empat cakupan terakhir yang menyebutkan permintaan, sesi, aplikasi, dan websocket hanya tersedia dalam aplikasi web-aware.

2. Lingkup Singleton

Mendefinisikan kacang dengan lingkup tunggal berarti wadah membuat satu contoh kacang itu, dan semua permintaan untuk nama kacang itu akan mengembalikan objek yang sama, yang di-cache. Modifikasi apa pun pada objek akan tercermin dalam semua referensi ke kacang. Cakupan ini adalah nilai default jika tidak ada cakupan lain yang ditentukan.

Mari buat entitas Person untuk mencontohkan konsep cakupan:

public class Person { private String name; // standard constructor, getters and setters }

Setelah itu, kami mendefinisikan kacang dengan lingkup tunggal dengan menggunakan anotasi @Scope :

@Bean @Scope("singleton") public Person personSingleton() { return new Person(); }

Kita juga bisa menggunakan konstanta sebagai ganti nilai String dengan cara berikut:

@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)

Sekarang kita melanjutkan untuk menulis tes yang menunjukkan bahwa dua objek yang merujuk ke kacang yang sama akan memiliki nilai yang sama, meskipun hanya satu dari mereka yang mengubah statusnya, karena keduanya mereferensikan instance kacang yang sama:

private static final String NAME = "John Smith"; @Test public void givenSingletonScope_whenSetName_thenEqualNames() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("scopes.xml"); Person personSingletonA = (Person) applicationContext.getBean("personSingleton"); Person personSingletonB = (Person) applicationContext.getBean("personSingleton"); personSingletonA.setName(NAME); Assert.assertEquals(NAME, personSingletonB.getName()); ((AbstractApplicationContext) applicationContext).close(); }

File scopes.xml dalam contoh ini harus berisi definisi xml dari kacang yang digunakan:

3. Lingkup Prototipe

Kacang dengan ruang lingkup prototipe akan mengembalikan contoh yang berbeda setiap kali diminta dari wadah. Ini ditentukan dengan menyetel prototipe nilai ke anotasi @Scope dalam definisi kacang:

@Bean @Scope("prototype") public Person personPrototype() { return new Person(); }

Kita juga bisa menggunakan konstanta seperti yang kita lakukan untuk lingkup tunggal:

@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)

Kami sekarang akan menulis tes serupa seperti sebelumnya yang menunjukkan dua objek yang meminta nama kacang yang sama dengan prototipe cakupan akan memiliki status berbeda, karena mereka tidak lagi mengacu pada contoh kacang yang sama:

private static final String NAME = "John Smith"; private static final String NAME_OTHER = "Anna Jones"; @Test public void givenPrototypeScope_whenSetNames_thenDifferentNames() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("scopes.xml"); Person personPrototypeA = (Person) applicationContext.getBean("personPrototype"); Person personPrototypeB = (Person) applicationContext.getBean("personPrototype"); personPrototypeA.setName(NAME); personPrototypeB.setName(NAME_OTHER); Assert.assertEquals(NAME, personPrototypeA.getName()); Assert.assertEquals(NAME_OTHER, personPrototypeB.getName()); ((AbstractApplicationContext) applicationContext).close(); } 

File scopes.xml mirip dengan yang disajikan di bagian sebelumnya sambil menambahkan definisi xml untuk kacang dengan lingkup prototipe :

4. Cakupan Sadar Web

Seperti yang disebutkan, ada empat cakupan tambahan yang hanya tersedia dalam konteks aplikasi yang sadar web. Ini jarang digunakan dalam praktik.

Ruang lingkup permintaan membuat instance kacang untuk permintaan HTTP tunggal sementara lingkup esion membuat untuk Sesi HTTP.

The aplikasi lingkup menciptakan contoh kacang untuk siklus hidup dari ServletContext dan WebSocket lingkup menciptakan itu untuk tertentu WebSocket sesi.

Mari buat kelas yang akan digunakan untuk membuat instance kacang:

public class HelloMessageGenerator { private String message; // standard getter and setter }

4.1. Cakupan Permintaan

Kita dapat mendefinisikan kacang dengan ruang lingkup permintaan menggunakan anotasi @Scope :

@Bean @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator requestScopedBean() { return new HelloMessageGenerator(); }

The ProxyMode atribut diperlukan karena, pada saat Instansiasi konteks aplikasi web, tidak ada permintaan aktif. Spring akan membuat proxy untuk disuntikkan sebagai dependensi, dan membuat instance bean target saat dibutuhkan dalam permintaan.

Kita juga dapat menggunakan anotasi yang dibuat @RequestScope yang bertindak sebagai pintasan untuk definisi di atas:

@Bean @RequestScope public HelloMessageGenerator requestScopedBean() { return new HelloMessageGenerator(); }

Selanjutnya, kita dapat menentukan pengontrol yang memiliki referensi yang dimasukkan ke requestScopedBean . Kita perlu mengakses permintaan yang sama dua kali untuk menguji cakupan khusus web.

Jika kita menampilkan pesan setiap kali permintaan dijalankan, kita dapat melihat bahwa nilainya disetel ulang ke nol , meskipun kemudian diubah dalam metode. Ini karena contoh kacang yang berbeda dikembalikan untuk setiap permintaan.

@Controller public class ScopesController { @Resource(name = "requestScopedBean") HelloMessageGenerator requestScopedBean; @RequestMapping("/scopes/request") public String getRequestScopeMessage(final Model model) { model.addAttribute("previousMessage", requestScopedBean.getMessage()); requestScopedBean.setMessage("Good morning!"); model.addAttribute("currentMessage", requestScopedBean.getMessage()); return "scopesExample"; } }

4.2. Cakupan Sesi

Kita dapat mendefinisikan kacang dengan cakupan sesi dengan cara yang sama:

@Bean @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator sessionScopedBean() { return new HelloMessageGenerator(); }

Ada juga anotasi tersusun khusus yang dapat kita gunakan untuk menyederhanakan definisi kacang:

@Bean @SessionScope public HelloMessageGenerator sessionScopedBean() { return new HelloMessageGenerator(); }

Next, we define a controller with a reference to the sessionScopedBean. Again, we need to run two requests in order to show that the value of the message field is the same for the session.

In this case, when the request is made for the first time, the value message is null. But once, it is changed, then that value is retained for subsequent requests as the same instance of the bean is returned for the entire session.

@Controller public class ScopesController { @Resource(name = "sessionScopedBean") HelloMessageGenerator sessionScopedBean; @RequestMapping("/scopes/session") public String getSessionScopeMessage(final Model model) { model.addAttribute("previousMessage", sessionScopedBean.getMessage()); sessionScopedBean.setMessage("Good afternoon!"); model.addAttribute("currentMessage", sessionScopedBean.getMessage()); return "scopesExample"; } }

4.3. Application Scope

The application scope creates the bean instance for the lifecycle of a ServletContext.

This is similar to the singleton scope but there is a very important difference with regards to the scope of the bean.

When beans are application scoped the same instance of the bean is shared across multiple servlet-based applications running in the same ServletContext, while singleton-scoped beans are scoped to a single application context only.

Let's create the bean with application scope:

@Bean @Scope( value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator applicationScopedBean() { return new HelloMessageGenerator(); }

Analogously as for the request and session scopes, we can use a shorter version:

@Bean @ApplicationScope public HelloMessageGenerator applicationScopedBean() { return new HelloMessageGenerator(); }

Now, let's create a controller that references this bean:

@Controller public class ScopesController { @Resource(name = "applicationScopedBean") HelloMessageGenerator applicationScopedBean; @RequestMapping("/scopes/application") public String getApplicationScopeMessage(final Model model) { model.addAttribute("previousMessage", applicationScopedBean.getMessage()); applicationScopedBean.setMessage("Good afternoon!"); model.addAttribute("currentMessage", applicationScopedBean.getMessage()); return "scopesExample"; } }

In this case, value message once set in the applicationScopedBean will be retained for all subsequent requests, sessions and even for a different servlet application that will access this bean, provided it is running in the same ServletContext.

4.4. WebSocket Scope

Finally, let's create the bean with websocket scope:

@Bean @Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator websocketScopedBean() { return new HelloMessageGenerator(); }

WebSocket-scoped beans when first accessed are stored in the WebSocket session attributes. The same instance of the bean is then returned whenever that bean is accessed during the entire WebSocket session.

We can also say that it exhibits singleton behavior but limited to a WebSocket session only.

5. Conclusion

Kami telah mendemonstrasikan berbagai cakupan kacang yang disediakan oleh Spring dan tujuan penggunaan mereka.

Implementasi tutorial ini dapat ditemukan di proyek GitHub - ini adalah proyek berbasis Eclipse, jadi semestinya mudah untuk mengimpor dan menjalankannya apa adanya.