Tags:

Hiểu rõ Spring Bean Scope

Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu khái niệm về Bean Scope trong Spring. Scope của một bean là nơi định nghĩa vòng đời, và cách nó được khởi tạo và quản lý bởi IoC trong một ngữ cảnh cụ thể.

Spring định nghĩa 6 kiểu Scope như sau:

  • singleton
  • prototype
  • request
  • session
  • application
  • websocket

Trong đấy, các scope như request, session, applicationwebsocket chỉ được sử dụng trong các ứng dụng web.

Sau đây chúng ta sẽ cùng nhau tìm hiểu từng scope một trong Spring để xem cách sử dụng chúng như thế nào.

Singleton Scope

Một Bean được định nghĩa với singleton scope sẽ khiến IoC container khởi tạo duy nhất một instance cho bean đó và nó được sử dụng trong tất cả các yêu cầu đến bean này.

Bất kỳ một thay đổi nào trên các singleton scope bean đều sẽ ảnh hưởng đến tất cả những nơi đang tham chiếu đến nó. Đây cũng chính là scope mặc định trong Spring khi bạn không chỉ định một scope nào cụ thể. 

Giả sử có Person class sau:

public class Person {
    private String name;

    // standard constructor, getters and setters
}

Tiếp theo chúng ta sẽ tạo một bean của Persion class với singleton scope thông qua @Scope annotation.

@Bean
@Scope("singleton")
public Person personSingleton() {
    return new Person();
}
Chúng ta cũng có thể sử dụng một hằng số thay vì giá trị String theo cách sau
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)

Giờ đây chúng ta sẽ tiến hành kiểm thử để xem khi 2 object tham chiến đến personSingleton bean đã được tạo ở trên thì có cùng các giá trị hay không, và khi 1 trong 2 tiến hành sữa đổi giá trị trong  personSingleton thì cả 2 đều nhìn thấy được những thay đổi này hay không.

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();
}

Tệp scopes.xml trong ví dụ này phải chứa các định nghĩa XML của các bean được sử dụng:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="personSingleton" class="org.baeldung.scopes.Person" scope="singleton"/>    
</beans>

Prototype Scope

Một bean với prototype scope sẽ trả về các instance khác nhau mỗi khi có một yêu mới sử dụng chúng đến IoC container. Chúng ta có thể định nghĩa một bean với prototype scope như sau:

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

Ngoài ra chúng ta có thể sử dụng hẳng số sau để thay thế String tránh nhầm lẫn hay sai xót.

@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)

Bây giờ chúng ta có thể viết một unit test đơn giản để kiểm tra xem với 2 object tham chiếu đến các personPrototype bean, thì mỗi object trả về từ IoC là khác nhau, do vậy việc chúng ta thay đổi giá trị trên một personPrototype bean sẽ không ảnh hưởng đến personPrototype bean của object còn lại.

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();
}
Chúng ta có scopes.xml được cấu hình như sau cho ví dụ trên.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="personPrototype" class="org.baeldung.scopes.Person" scope="prototype"/>  
</beans>

Scope trong Web application

Như đã đề cập ở trên, chúng ta có 4 bean scope chỉ được sử dụng trong các ứng dụng web. Và chúng cũng thường ít được sử dụng trong thực tế. Bây giờ chúng ta sẽ cùng nhau điểm qua các khái niệm cơ bản của những scope này.

Request Scope khởi tạo một bean instance cho một HTTP request trong khi Session Scope khởi tạo một instance cho một HTTP Session.

Application Scope khởi tạo một bean instance cho một vòng đời của một ServletContext và WebSocket Scope khởi tạo cho một WebSocket session.

Trước khi bắt đầu tìm hiểu các tạo ra các bean với các scope được chỉ định trên thì mình sẽ cần tạo ra một class sử dụng xuyên suốt trong các phần sau

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

Request Scope

Chúng ta có thể tạo ra một bean với request scope bằng cách sử dụng @Scope annotation như sau:

 

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

Trong đó thuộc tính proxyMode được dùng trong trường hợp, tạo thời điểm khởi chạy ứng dụng web và khởi tạo Application Context chúng ta sẽ không có bất kỳ một request nào đến ứng dụng, do vậy Spring sẽ tạo một proxy để đưa vào làm dependency đại diện cho , và nó sẽ khởi tạo một bean thật sự khi có một request gửi đến trong thời gian sau đó.

Ở đoạn code trên để khởi tạo một request bean khá rườm rà, nên Spring cũng đã biết ý mà tạo ra @RequestScope tương đồng để tạo ra một request scope.

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

 Session Scope

Tương tự như request scope chúng ta có thể khởi tạo session bean thông qua @Scope annotation

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

Spring cũng cung cấp @SessionScope để tối giản việc tạo ra một session scope

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

Application Scope

Application scope khởi tạo một bean instance cho một vòng đời của ServletContext. Nó tương tự như singleton scope nhưng có một điểm khác biệt rất quan trọng giữa chúng. 

Khi mà application bean sử dụng một instance cho nhiều ứng dụng đang chạy trong cùng một ServletContext thì singleton bean được sử dụng trong một application context của một ứng dụng nhất định.

Chúng ta có thể khởi tạo một application context bean với @Scope annotation như sau:

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

Tương tự như request và session scope chúng ta có thể khởi tạo một application scope đơn giản với @ApplicationScope.

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

WebSocket Scope

Cuối cùng chúng ta có thể tạo một websocket scope bean như sau

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

Các bean WebSocket scope khi được truy cập lần đầu sẽ được lưu trữ trong thuộc tính WebSocket session. Một bean sau đó sẽ được sử dụng bất cứ khi có một yêu cầu truy cập đến WebSocket session này.

Kết bài

Qua bài biết trên chúng ta đã tìm hiểu được các loại bean scope khác nhau trong Spring rồi nhé. Trong thực tế mình thấy khá ít bạn để ý đến khái niệm này, vì thông thường chúng ta chỉ dùng bean scope mặc định là singleton là đủ rồi. Ngay cả bản thân mình làm Spring Boot được tầm 1 năm mới bắt đầu có những khái niệm đầu tiên. Hãy đọc và enjoy để biết thêm một kiến thức mới nhé.

Nguồn tham khảo

https://www.baeldung.com/spring-bean-scopes

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x