1. 개요

이 빠른 사용방법(예제)에서는 Spring 프레임워크의 다양한 유형의 빈 범위에 대해 학습합니다.

빈의 범위는 우리가 사용하는 컨텍스트에서 빈의 수명 주기와 가시성을 정의합니다.

Spring 프레임워크의 최신 버전은 6가지 유형의 범위를 정의합니다.

  • 하나씩 일어나는 것
  • 원기
  • 요구
  • 세션
  • 신청
  • 웹 소켓

언급된 마지막 네 가지 범위인 request, session, applicationwebsocket 은 웹 인식 응용 프로그램에서만 사용할 수 있습니다.

2. 싱글톤 범위

싱글톤 범위 로 빈을 정의할 때 컨테이너는 해당 빈의 단일 인스턴스를 생성합니다. 해당 빈 이름에 대한 모든 요청은 캐시된 동일한 객체를 반환합니다. 객체에 대한 모든 수정 사항은 Bean에 대한 모든 참조에 반영됩니다. 다른 범위가 지정되지 않은 경우 이 범위가 기본값입니다.

범위 개념을 예시하기 위해 Person 엔터티를 만들어 보겠습니다 .

public class Person {
    private String name;

    // standard constructor, getters and setters
}

그런 다음 @Scope 어노테이션 을 사용하여 싱글톤 범위 의 빈을 정의합니다 .

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

다음과 같은 방식으로 String 값 대신 상수를 사용할 수도 있습니다 .

@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)

이제 우리는 동일한 빈을 참조하는 두 객체가 동일한 빈 인스턴스를 참조하기 때문에 둘 중 하나만 상태를 변경하더라도 동일한 값을 가질 것임을 보여주는 테스트를 작성할 수 있습니다.

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

이 예제 의 scopes.xml 파일에는 사용된 빈의 xml 정의가 포함되어야 합니다.

<?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>

3. 프로토타입 범위

프로토타입 범위가 있는 빈  은 컨테이너에서 요청할 때마다 다른 인스턴스를 반환합니다. 빈 정의에서 값 프로토타입@Scope 어노테이션으로 설정하여 정의합니다.

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

싱글톤 범위 에 대해 했던 것처럼 상수를 사용할 수도 있습니다 .

@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)

이제  프로토타입  범위를 사용하여 동일한 빈 이름을 요청하는 두 개의 객체를 보여주는 이전과 유사한 테스트를 작성할 것입니다. 더 이상 동일한 빈 인스턴스를 참조하지 않으므로 다른 상태를 갖습니다.

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

scopes.xml 파일 은 프로토타입 범위 가 있는 빈에 대한 xml 정의를 추가하는 동안 이전 섹션에서 제시된 것과 유사합니다 .

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

4. 웹 인식 범위

앞서 언급했듯이 웹 인식 애플리케이션 컨텍스트에서만 사용할 수 있는 네 가지 추가 범위가 있습니다. 우리는 실제로 이것을 덜 자주 사용합니다.

요청 범위 는 단일 HTTP 요청에 대한 빈 인스턴스를 생성하는 반면 세션 범위는 HTTP 세션에 대한 빈 인스턴스를 생성합니다.

응용 프로그램 범위 는 ServletContext 의 수명 주기에 대한 빈 인스턴스를 생성 하고 웹 소켓 범위는 특정 WebSocket 세션에 대해 빈 인스턴스를 생성합니다.

빈을 인스턴스화하는 데 사용할 클래스를 만들어 보겠습니다.

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

4.1. 요청 범위

@Scope 어노테이션 을 사용하여 요청 범위 로 빈을 정의할 수 있습니다 .

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

웹 애플리케이션 컨텍스트를 인스턴스화하는 순간에 활성 요청 이 없기 때문에 proxyMode 속성이 필요합니다. Spring은 의존성으로 주입될 프록시를 생성하고 요청에 필요할 때 타겟 빈을 인스턴스화한다.

위의 정의에 대한 바로 가기 역할을 하는 @RequestScope 구성 어노테이션을 사용할 수도 있습니다 .

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

다음으로 requestScopedBean 에 대한 참조가 삽입된 컨트롤러를 정의할 수 있습니다 . 웹 특정 범위를 테스트하려면 동일한 요청에 두 번 액세스해야 합니다.

요청이 실행될 때마다 메시지 를 표시하면 나중에 메서드에서 값이 변경되더라도 값이 null 로 재설정되는 것을 볼 수 있습니다. 이는 각 요청에 대해 다른 빈 인스턴스가 반환되기 때문입니다.

@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. 세션 범위

비슷한 방식으로 세션 범위로 빈을 정의할 수 있습니다 .

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

빈 정의를 단순화하는 데 사용할 수 있는 전용 합성 어노테이션도 있습니다.

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

다음으로 sessionScopedBean 에 대한 참조로 컨트롤러를 정의합니다 . 다시 말하지만, 메시지 필드의 값이 세션에 대해 동일 하다는 것을 보여주기 위해 두 개의 요청을 실행해야 합니다 .

이 경우 처음으로 요청이 있을 때 값 메시지null입니다. 그러나 일단 변경되면 해당 값은 전체 세션에 대해 동일한 빈 인스턴스가 반환되므로 후속 요청에 대해 유지됩니다.

@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. 적용 범위

애플리케이션 범위는 ServletContext의 라이프사이클에 대한 빈 인스턴스를 생성 합니다 .

이것은 싱글톤 스코프와 유사하지만 빈의 스코프와 관련하여 매우 중요한 차이가 있습니다.

빈이 애플리케이션 범위일 때 빈의 동일한 인스턴스는 동일한 ServletContext 에서 실행되는 여러 서블릿 기반 애플리케이션에서 공유되는 반면 싱글톤 범위의 빈은 단일 애플리케이션 컨텍스트로만 범위가 지정됩니다.

애플리케이션 범위 로 빈을 생성해 보겠습니다 .

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

요청세션 범위와 유사하게 더 짧은 버전을 사용할 수 있습니다.

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

이제 이 빈을 참조하는 컨트롤러를 생성해 보겠습니다.

@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";
    }
}

이 경우, applicationScopedBean 에 설정되면 값 메시지 는 모든 후속 요청, 세션, 심지어 동일한 ServletContext 에서 실행되는 경우 이 Bean에 액세스하는 다른 서블릿 애플리케이션에 대해서도 유지됩니다 .

4.4. 웹소켓 범위

마지막으로 websocket 범위로 빈을 생성해 보겠습니다.

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

처음 액세스할 때 WebSocket 범위 빈은 WebSocket 세션 속성에 저장됩니다. 그런 다음 전체 WebSocket 세션 동안 해당 빈에 액세스할 때마다 빈의 동일한 인스턴스가 반환 됩니다.

싱글톤 동작을 나타내지만 Web ebSocket 세션 에만 국한된다고 말할 수도 있습니다 .

5. 결론

이 기사에서 우리는 Spring이 제공하는 다양한 bean 범위와 그 용도가 무엇인지 논의했습니다.

이 기사의 구현은 GitHub 프로젝트 에서 찾을 수 있습니다 .

Generic footer banner