본문 바로가기
웹개발/spring

스프링 Bean의 개념과 Bean Scope 종류

by heavenLake 2022. 2. 16.
반응형

 

 

 

 원본사이트

https://cantcoding.tistory.com/47

 

스프링 Bean,Singleton,Bean Scope

스프링 빈(Spring Bean)이란? 스프링 IoC(Inversion of Control) 컨테이너에 의해서 관리되고 애플리케이션의 핵심을 이루는 객체들을 스프링 빈(Beans)이라고 한다. 빈은 스프링 컨테이너에 의해서 인스턴

cantcoding.tistory.com

 

스프링 빈(Spring Bean)이란?

스프링 IoC(Inversion of Control) 컨테이너에 의해서 관리되고 애플리케이션의 핵심을 이루는 자바객체들을스프링 빈(Beans)이라고 한다. 빈은 스프링 컨테이너에 의해서 인스턴스화 되어 조립되고 관리됩니다.스프링 컨테이너가 관리해준다는 점을 제외하면 일반 자바 객체이다.

 

Singleton

클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴이다.

그래서 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야 한다.

코드에서 private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 막아야 한다.

하지만 스프링 컨테이너를 사용하면 컨테이너에 등록되는 빈들을 알아서 싱글톤으로 관리해준다.

 

스프링 핵심기본원리/김영한

(클라이언트들이 스프링 빈을 요구할 때 같은 참조값을 가진 빈 객체를 반환해준다.)

 싱글톤 패턴에서 주의점

  • 하나의 인스턴스를 생성해서 공유하는 형식이므로 객체의 상태를 유지하게(stateful) 설계하면 안된다.
  • 특정 클라이언트에 의존적이거나 값을 변경할 수 있는 필드가 있으면 안된다.
  • 가급적 읽기만 가능해야 한다.
  • 필드 대신 공유되지 않는 지역변수,파라미터,ThreadLocal을 쓰자.(예를 들어 필드를 반환하는게 아닌 파라미터를 반환하자)

 

빈 스코프란?

Scope의 뜻대로 빈이 존재할 수 있는 범위를 말한다. 스프링 빈은 기본적으로 싱글톤 스코프가 적용된다.

스프링은 다양한 종류의 스코프를 지원하는데 각 스코프를 분석해보자.

 

1.싱글톤: 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프

 

2.프로토타입: 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고

더는 관리하지 않는 매우 짧은 범위의 스코프. @Scope("prototype")로 선언한다.

(의존관계 까지만 주입하고 보내주기 때문에 @PreDestroy를 볼 수 없음)

스프링 핵심기본원리/김영한

 

싱글톤과 프로토타입 빈을 같이 사용할 때 주의할 점.

우리는 보통 싱글톤과 프로토타입 빈을 같이 사용하게 되는데 여기서 문제가 생긴다.

clientBean은 싱글톤이고 prototypeBean은 프로토타입이고 호출할 때 마다 값이 1씩 증가한다고 가정.

싱글톤 빈은 스프링 컨테이너에 등록할 때 의존관계 주입을 위해 PrototypeBean을 요청해서 주입 받는다.

이런 방식이면 클라이언트들이 싱글톤인 clientBean을 사용할 때 마다 다른 객체가 생성되길 바라는 프로토타입의 효과를 보지 못한다.(이미 주입이 완료된 상태인 싱글톤 빈을 공유해서 사용하기 때문에)

 

PrototypeBean이 공유되어서 count가 공유되어 사용되는 문제가 발생한다.

해결방법

싱글톤 빈과 함께 사용시 Provider로 문제 해결한다.

ObjectProvider <prototypeBean>Provider를 선언하면

getObject()메서드를 실행할 때 마다 빈을 만들어준다.

(Provider가 직접 의존관계를 찾고있으므로 Dependency Lookup(DL)이라고 한다.)

 

프로토타입 스코프를 주입받을 때 매번 새로운 스코프를 받기위해 ObjectProvider를 사용하는 코드로 바꾼 것(오른쪽)

스프링에서 제공하는 ObjectFactory, ObjectProvider를 사용하는 방식도 있고

자바표준에서 제공하는 JSR-330 Provider를 사용하는 방식도 있다.

 

 

3.웹 관련 스코프

 

웹 스코프는 웹 환경에서만 동작한다. @Scope("request")로 선언가능

웹 스코프는 프로토타입과 다르게 스프링이 해당 스코프의 종료시점까지 관리한다.

따라서 종료 메서드가 호출된다(@PreDestroy,@PostConstruct 둘다 가능)

 

 

  • request: HTTP 요청 하나가 들어오고 나갈 때 까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고, 관리
  • session: HTTP Session과 동일한 생명주기를 가지는 스코프
  • application: 서블릿 컨텍스트( ServletContext )와 동일한 생명주기를 가지는 스코프
  • websocket: 웹 소켓과 동일한 생명주기를 가지는 스코프

request 스코프에서 생각해야할 점.

request스코프 빈은 HTTP request가 들어와야 생성할 수 있다.

따라서 미리 의존관계를 주입시켜주기 위해 프로토타입에서 활용했던 Provider을 사용하거나

프록시 방식을 써야 한다.

다음과 같이 프록시를 설정해주면 HTTP request의 여부와 상관없이  프록시 클래스를 빈에 미리 주입해 두고 필요할 때마다 사용 가능하게 해준다.(CGLIB이라는 라이브러리로 바이트코드를 조작해서 가짜 프록시 객체를 넣어놓는다)

 

사실 Provider를 사용하든, 프록시를 사용하든 핵심 아이디어는 진짜 객체 조회를 꼭 필요한 시점까지 지연 처리 한다는 점이다.

 

 

스코프 사용시 주의점

  • 스코프마다 동작 방법이 다르므로 주의해서 사용해야 한다.
  • 특별한 scope는 꼭 필요한 곳에만 최소화해서 사용하자, 무분별하게 사용하면 유지보수하기 어려워 진다

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 원본사이트

https://velog.io/@probsno/Bean-%EC%8A%A4%EC%BD%94%ED%94%84%EB%9E%80

 

Bean 스코프란?

말그대로 빈이 사용되어지는 범위(?)인데, 빈이 앱이 구동되는 동안 한개만 만들어서 쓸 것인지 HTTP요청마다 생성해서 쓸 것인지 등등를 결정하는 것이 스코프임. 보통의 스프링 빈은 스프링 앱

velog.io

 

 

 

 

1. Bean 스코프

빈 스코프는 무엇인가?

말그대로 빈이 사용되어지는 범위(?)인데, 빈이 앱이 구동되는 동안 한개만 만들어서 쓸 것인지 HTTP요청마다 생성해서 쓸 것인지 등등를 결정하는 것이 스코프임.
보통의 스프링 빈은 스프링 앱이 구동 될때 한번에 ApplicationContext에서 한 번에 모두 생성해서 하나의 클래스는 한 개의 빈만 가지지만 (Singleton), 때에 따라서는 HTTP요청마다 (Request) 다른 빈을 생성해서 쓸건지, 매번 사용될 때 마다 (Prototype) 빈을 생성해서 쓸건지 설정해서 쓸 수도 있다.

빈 스코프의 종류는??


링크
spring docs에서 가져온 건데 불친절하게 해석하자면,,,,

ScopeDescription
singleton (기본값) 스프링 IoC 컨테이너당 하나의 인스턴스만 사용 - 한마디로 앱이 구동되는 동안 하나만 쓴다는 거임
prototype 매번 새로운 빈을 정의해서 사용
request HTTP라이프 사이클 마다 한개의 빈을 사용, web-aware 컨택스트에서만 사용가능 - ex. Applicaiton context
session HTTP 세션마다 하나의 빈을 사용, web-aware 컨택스트에서만 사용가능 - ex. Applicaiton context
application ServeltContext라이프사이클 동안 한개의 빈만 사용, web-aware 컨택스트에서만 사용가능 - ex. Applicaiton context
websocket websocket 라이프사이클 안에서 한개의 빈만 사용, web-aware 컨택스트에서만 사용가능 - ex. Applicaiton context

2. ProtoType 스코프를 사용하는 방법

그렇다면 스프링 빈 스코프를 어떻게 정의하고 사용할 수 있을까?

@Component
@Scope(value = "prototype")
public class ProtoType {
}

바로 컴포넌트로 등록 하면서, @Scope를 붙여주면 된다. value의 값으로는 위에서 언급한 값이 Scope이름이 String으로 들어가게 되는데...

  • @Scope(value = "prototype")
  • @Scope(value = "singleton")
  • @Scope(value = "request")
  • @Scope(value = "session")
  • @Scope(value = "application")
  • @Scope(value = "websocket")
    이렇게 쓰면 된다

하지만 , 문제가 발생하는 경우가 있음...

바로 Sinlgeton 스코프의 빈이 prototype의 빈을 주입받는 경우임
말로하면 이해가 안가니까 직관적으로 코드로 보자면

@Component
public class Single {

    @Autowired
    ProtoType protoType;


    public ProtoType getProtoType() {

    }
}
@Component
@Scope(value = "prototype")
public class ProtoType {

}

코드에서 보는 것 과 같이 싱글톤 스코프의 빈이 프로토타입 빈을 주입받으면 싱글톤의 프로토타입 빈은 매번 바뀌지 않고 같은 빈이 쓰임

@Component
public class AppRunner implements ApplicationRunner {

    @Autowired
    Single single;

    @Autowired
    ProtoType protoType;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("---singleton---");
        System.out.println(context.getBean(protoType.getClass()));
        System.out.println(context.getBean(protoType.getClass()));
        System.out.println(context.getBean(protoType.getClass()));

        System.out.println("---prototype---");
        System.out.println(single);
        System.out.println(single);
        System.out.println(single);

        System.out.println("---prototype by singleton---");
        System.out.println(single.getProtoType());
        System.out.println(single.getProtoType());
        System.out.println(single.getProtoType());
    }
}

결과는...


이렇게 프로토타입이지만 매번 같은 빈을 사용함

이유는???

singleton 빈 ApplicationContext가 처음 앱을 구동할때 빈을 만들고 빈을 주입해서 앱이 종료될 때 까지 계속 사용 되기 때문에 singleton 빈 안에 있는 prototype 빈도 처음 주입된 채로 그대로 사용된다.

그렇다면 해결 방법은?!

두가지 방법이 있는데,,,
1. proxy mode를 이용하는 방법

@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ProtoType {
}

프로토타입으로 쓸 빈에 @Scope 어노태이션에 proxyMode = ScopedProxyMode.TARGET_CLASS를 넣어주면 된다. 만약에 해당 오브잭트가 클래스가 아니라 인터페이스라면 proxyMode = ScopedProxyMode.INTERFACES도 쓸 수가 있다.

2. ObjectProvider객체를 사용하는 방법

@Component
public class Single {

    @Autowired
    ObjectProvider<ProtoType> protoType;

    public ProtoType getProtoType() {
        return protoType.getIfAvailable();
    }
}

ObjectProvider< >를 용해서 매번 빈을 주입하는 방법 도 있다. 하지만,
1번이 POJO를 유지하기 때문에 기선이형은 프록시모드를 이용한 방법을 추천해 줬음!

3. Outro

proxy mode가 어떻게 작동하는지 간단하게 설명하자면,


그림에서 처럼 ApplicationConxtext가 빈을 처음에 생성할 때 proto 빈을 주입받는게 아니라 proto 클래스를 상속받은(타입이 같은) proxy클래스를 만들어서 빈으로 등록하고 proxy클래스에서 내부적으로 매번 새로운 proto빈을 사용하게 끔 설계 되어있다.

 

 

반응형

'웹개발 > spring' 카테고리의 다른 글

스프링 properties 읽어오기  (0) 2022.02.17
스프링 Filter 와 Listener  (0) 2022.02.16
스프링 필터 (필터와 인터셉터 차이점)  (0) 2022.02.16
스프링 Listener  (0) 2022.02.16
스프링 Annotation  (0) 2022.02.16

댓글