Updated:

1. 개요

빈 스코프는 빈이 존재할 수 있는 범위를 말한다. 이번에는 빈 스코프(Bean Scope)에 대해 알아보도록 하자.

  • singleton : 하나의 빈만 생성해서 공유하는 스코프로, 스프링 컨테이너의 시작부터 종료까지 유지
  • prototype : 요청 할 때마다 빈을 생성하는 스코프로, 스프링 컨테이너는 빈의 생성과 의존관계 주입까지만 관여
  • web scope
    • request : 요청이 들어오고 나갈 때 까지 유지되는 스코프
    • session : 세션이 생성되고 종료될 때 까지 유지되는 스코프
    • application : 서블릿 컨텍스트와 같은 범위로 유지되는 스코프

2. 싱글톤 스코프

싱글톤 스코프를 사용하면, 스프링 컨테이너는 항상 같은 빈을 반환하고, 스프링 컨테이너 생성 시점에 초기화 메서드가 실행된다.

[SingletonTest.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class SingletonTest {

    @Test
    void singletonBeanFind() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SingletonBean.class);

        SingletonBean singletonBean1 = ac.getBean(SingletonBean.class);
        SingletonBean singletonBean2 = ac.getBean(SingletonBean.class);
        System.out.println("singletonBean1 = " + singletonBean1);
        System.out.println("singletonBean2 = " + singletonBean2);

        assertThat(singletonBean1).isSameAs(singletonBean2);

        ac.close();
    }

    @Scope("singleton")
    static class SingletonBean {

        @PostConstruct
        public void init() {
            System.out.println("SingletonBean.init");
        }

        @PreDestroy
        public void destroy() {
            System.out.println("SingletonBean.destroy");
        }
    }
}

Line 17 : singleton 스코프 빈

[실행 결과]

3. 프로토타입 스코프

프로토타입 스코프를 사용하면, 스프링 컨테이너는 항상 새로운 빈을 생성해서 반환하는데, 스프링 컨테이너는 빈 생성, 의존관계 주입, 초기화 까지만 관여한다. 따라서 @PreDestroy와 같은 종료 메서드는 호출되지 않는다. 또한 스프링 컨테이너에서 빈을 조회할 때 생성되고, 초기화 메서드가 실행된다.

[PrototypeTest.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class PrototypeTest {

    @Test
    void prototypeBeanFind() {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);
    
    System.out.println("find prototypeBean1");
    PrototypeBean prototypeBean1 = ac.getBean(PrototypeBean.class);
    System.out.println("find prototypeBean2");
    PrototypeBean prototypeBean2 = ac.getBean(PrototypeBean.class);
    System.out.println("prototypeBean1 = " + prototypeBean1);
    System.out.println("prototypeBean2 = " + prototypeBean2);
    
    assertThat(prototypeBean1).isNotSameAs(prototypeBean2);

    ac.close();
    }

    @Scope("prototype")
    static class PrototypeBean {

        @PostConstruct
        public void init() {
            System.out.println("PrototypeBean.init");
        }

        @PreDestroy
        public void destroy() {
            System.out.println("PrototypeBean.destroy");
        }
    }
}

Line 19 : prototype 스코프 빈

[실행 결과]

4. 웹 스코프

웹 스코프는 웹 환경에서만 동작하는 스코프로, 프로토타입 스코프와는 다르게 종료 시점까지 관리하므로 종료 메서드가 호출된다.

[MyLogger.class]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {

    private String uuid;
    private String requestURL;

    public void setRequestURL(String requestURL) {
        this.requestURL = requestURL;
    }

    public void log(String message) {
        System.out.println("[" + uuid + "]" + "[" + requestURL + "] " + message);
    }

    @PostConstruct
    public void init() {
        uuid = UUID.randomUUID().toString();
        System.out.println("[" + uuid + "] request scope bean create: " + this);
    }

    @PreDestroy
    public void close() {
        System.out.println("[" + uuid + "] request scope bean close: " + this);
    }
}

Line 2 : request 스코프 빈. request 스코프 빈은 실제 요청이 오는 순간 생성되므로, 프록시를 이용하여 스프링 애플리케이션이 실행되는 시점에는 가짜 객체가 생성되도록 설정

[LogDemoService.java]

1
2
3
4
5
6
7
8
9
10
@Service
@RequiredArgsConstructor
public class LogDemoService {

    private final MyLogger myLogger;

    public void logic(String id) {
        myLogger.log("service id = " + id);
    }
}

[LogDemoController.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Controller
@RequiredArgsConstructor
public class LogDemoController {

    private final LogDemoService logDemoService;
    private final MyLogger myLogger;

    @RequestMapping("/log-demo")
    @ResponseBody
    public String logDemo(HttpServletRequest request) {
        String requestURL = request.getRequestURL().toString();

        System.out.println("myLogger = " + myLogger.getClass());
        myLogger.setRequestURL(requestURL);

        myLogger.log("controller test");
        logDemoService.logic("testId");
        return "OK";
    }
}

[실행 결과]

Updated:

Leave a comment