스프링 레시피 CH2.5 @Scope를 붙여 POJO 스코프 지정하기

2023. 12. 11. 22:49카테고리 없음

과제

@Component 같은 애너테이션을 POJO 인스턴스에 붙이는 건 빈 생성에 관한 템플릿을 정의하는 것이지, 실제 빈 인스턴스를 정의하는 게 아니다.

getBean() 메서드로 빈을 요청하거나 다른 빈에서 참조할 때 스프링은 빈 스코프에 따라 어느 빈 인스턴스를 반환할지 결정해야 한다. 이때 기본 스코프 이외의 다른 빈 스코프를 지정할 경우가 있다.

해결책

@Scope는 빈 스코프를 지정하는 애너테이션이다. 스프링은 IoC 컨테이너에 선언한 빈마다 정확히 인스턴스 하나를 생성하고 이렇게 만들어진 인스턴스는 전체 컨테이너 스코프에 공유된다.

getBean() 메서드를 호출하거나 빈을 참조하면 이러한 유일무이한 인스턴스가 반환된다.

이 스코프가 바로 모든 빈의 기본 스코프인 singleton이다.

 

전체 스프링 빈 스코프 표

스코프 설명
singleton IoC 컨테이너당 빈 인스턴스 하나 생성
prototype 요청할 때마다 빈 인스턴스를 새로 만듦
request HTTP 요청당 하나의 빈 인스턴스 생성. 웹 애플리케이션 컨텍스트에만 해당
session HTTP 세션당 빈 인스턴스 하나를 생성. 웹 애플리케이션 컨텍스트에만 해당
globalSession 전연 HTTP 세션당 빈 인스턴스 하나를 생성. 포털 애플리케이션 컨텍스트에만 해당

풀이

쇼핑몰 애플리케이션의 카트를 예로 들어보자

 

Product를 담을 Cart 클래스

@Component
public class ShoppingCart {
    private List<Product> items = new ArrayList<>();

    public void addItem(Product item) {
        items.add(item);
    }

    public List<Product> getItems() {
        return items;
    }
}

 

상품 빈이 등록되어있는 자바 구성파일

@Configuration
@ComponentScan("com.spring.study.chapter02.shop")
public class ShopConfiguration {

    @Bean
    public Product aaa() {
        Battery p1 = new Battery("AAA", 2.5);
        p1.setRechargeable(true);
        return p1;
    }

    @Bean
    public Product cdrw() {
        Disc p2 = new Disc("CD-RW", 1.5);
        p2.setCapacity(700);
        return p2;
    }

    @Bean
    public Product dvdrw() {
        Disc p2 = new Disc("DVD-RW", 3.0);
        p2.setCapacity(700);
        return p2;
    }
}

 

 

아래 컨테이너에서 shoppingCart 빈을 IoC 컨테이너에서 두 번 호출해서 cart1과 cart2 변수에 담았는데, 두 객체는 같은 참조를 갖는다.

public static void main(String[] args) {
    ApplicationContext context =
            new AnnotationConfigApplicationContext(ShopConfiguration.class);

    Product aaa = context.getBean("aaa", Product.class);
    Product cdrw = context.getBean("cdrw", Product.class);
    Product dvdrw = context.getBean("dvdrw", Product.class);

    ShoppingCart cart1 = context.getBean("shoppingCart", ShoppingCart.class);
    cart1.addItem(aaa);
    cart1.addItem(cdrw);
    System.out.println("Shopping cart 1 contains " + cart1.getItems());

    ShoppingCart cart2 = context.getBean("shoppingCart", ShoppingCart.class);
    cart2.addItem(dvdrw);
    System.out.println("Shopping cart 2 contains " + cart2.getItems());
}

=>
Shopping cart 1 contains [Product{name='AAA', price=2.5}, Product{name='CD-RW', price=1.5}]
Shopping cart 2 contains [Product{name='AAA', price=2.5}, Product{name='CD-RW', price=1.5}, Product{name='DVD-RW', price=3.0}]

 

스프링 기본 스코프가 singleton이라서 IoC 컨테이너당 카트 인스턴스가 한 개만 생성되었으니 말이다.

getBean() 메서드 호출 시 상이한 카트 인스턴스를 가져오려면 shoppingCart 빈 스코프를 prototype으로 설정하면 된다.

스프링은 getBean() 호출할 때마다 빈 인스턴스를 새로 만들 것이다.

 

@Component
@Scope("prototype")
public class ShoppingCart {
    private List<Product> items = new ArrayList<>();

    public void addItem(Product item) {
        items.add(item);
    }

    public List<Product> getItems() {
        return items;
    }
}

 

System.out.println("Shopping cart 1 contains " + cart1.getItems());
System.out.println("Shopping cart 2 contains " + cart2.getItems());

=>
Shopping cart 1 contains [Product{name='AAA', price=2.5}, Product{name='CD-RW', price=1.5}]
Shopping cart 2 contains [Product{name='DVD-RW', price=3.0}]