1. Gambaran keseluruhan
Dalam tutorial ringkas ini, anda akan belajar mengenai pelbagai jenis skop kacang dalam rangka Spring.
Skop kacang menentukan kitaran hidup dan keterlihatan kacang itu dalam konteks di mana ia digunakan.
Rangka kerja Spring versi terbaru menentukan 6 jenis ruang lingkup:
- bujang
- prototaip
- permintaan
- sesi
- permohonan
- soket web
Permintaan, sesi, aplikasi dan soket web yang disebutkan dalam empat ruang lingkup terakhir hanya tersedia dalam aplikasi yang menyedari web.
2. Skop Singleton
Mendefinisikan kacang dengan ruang lingkup tunggal bermaksud wadah membuat satu contoh kacang itu, dan semua permintaan untuk nama kacang akan mengembalikan objek yang sama, yang disimpan dalam cache. Sebarang pengubahsuaian pada objek akan tercermin dalam semua rujukan pada kacang. Skop ini adalah nilai lalai jika tidak ada skop lain yang ditentukan.
Mari buat entiti Person untuk memberi contoh konsep ruang lingkup:
public class Person { private String name; // standard constructor, getters and setters }
Selepas itu, kami menentukan kacang dengan skop singleton dengan menggunakan anotasi @Scope :
@Bean @Scope("singleton") public Person personSingleton() { return new Person(); }
Kita juga dapat menggunakan pemalar dan bukannya nilai String dengan cara berikut:
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
Sekarang kita terus menulis ujian yang menunjukkan bahawa dua objek yang merujuk pada kacang yang sama akan memiliki nilai yang sama, walaupun hanya satu daripadanya yang mengubah keadaannya, kerana kedua-duanya merujuk pada contoh 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(); }
The scopes.xml fail dalam contoh ini harus mengandungi takrif xml kacang digunakan:
3. Skop Prototaip
Kacang dengan skop prototaip akan mengembalikan contoh yang berbeza setiap kali diminta dari bekas. Ia ditentukan dengan menetapkan prototaip nilai ke anotasi @Scope dalam definisi kacang:
@Bean @Scope("prototype") public Person personPrototype() { return new Person(); }
Kami juga boleh menggunakan pemalar seperti yang kami lakukan untuk skop tunggal:
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
Kami sekarang akan menulis ujian serupa seperti sebelumnya yang menunjukkan dua objek yang meminta nama kacang yang sama dengan prototaip skop akan mempunyai keadaan yang berbeza, kerana mereka tidak lagi merujuk 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(); }
The scopes.xml fail adalah sama dengan yang dikemukakan dalam bahagian sebelum ini sambil menambah definisi xml untuk kacang dengan prototaip skop:
4. Skop Mengetahui Web
Seperti disebutkan, ada empat ruang lingkup tambahan yang hanya tersedia dalam konteks aplikasi yang sadar web. Ini jarang digunakan dalam praktik.
The permintaan skop mewujudkan contoh kacang untuk permintaan HTTP tunggal manakala s ession skop mewujudkan dengan Sesi HTTP.
Yang permohonan skop mewujudkan contoh kacang untuk kitaran hayat yang ServletContext dan WebSocket skop mewujudkan selama tertentu WebSocket sesi.
Mari buat kelas untuk digunakan untuk memberi contoh kacang:
public class HelloMessageGenerator { private String message; // standard getter and setter }
4.1. Memohon Skop
Kami dapat menentukan 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 perlu kerana, pada masa ini daripada merta daripada konteks aplikasi web, tidak ada permintaan aktif. Spring akan membuat proksi untuk disuntikkan sebagai ketergantungan, dan memberi contoh kacang sasaran ketika diperlukan dalam permintaan.
We can also use a @RequestScope composed annotation that acts as a shortcut for the above definition:
@Bean @RequestScope public HelloMessageGenerator requestScopedBean() { return new HelloMessageGenerator(); }
Next, we can define a controller that has an injected reference to the requestScopedBean. We need to access the same request twice in order to test the web specific scopes.
If we display the message each time the request is run, we can see that the value is reset to null, even though it is later changed in the method. This is because of a different bean instance being returned for each request.
@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. Session Scope
We can define the bean with session scope in a similar manner:
@Bean @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) public HelloMessageGenerator sessionScopedBean() { return new HelloMessageGenerator(); }
There's also a dedicated composed annotation we can use to simplify the bean definition:
@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 menunjukkan pelbagai kacang yang disediakan oleh Spring dan apa tujuan penggunaannya.
Pelaksanaan tutorial ini boleh didapati di projek GitHub - ini adalah projek berasaskan Eclipse, jadi mudah diimport dan dijalankan sebagaimana adanya.