본문 바로가기

Spring

Spring Scope와 ObjectProvider, proxyMode

Bean Scope

스프링은 빈이라는 개념으로 자바 객체를 만들고 싱글톤화 시켜 관리하여 줍니다.

이 객체들은 스프링 IoC 컨테이너의 의해 생성되고 소멸되는 등의 관리가 이루어 집니다.

이때 bean이 관리되는 범위를 Bean scope라고 합니다.
여기서 bean이 싱글턴화 되서 관리되는 이유는 spring bean의 기본 scope 전략이 Singleton 이기 때문입니다.

 

scope 설명
singleton 하나의 Bean 정의 대해서 Spring Container 내에 단 하나의 개객체만 존재한다.
prototype 하나의 Bean 정의에 대해서 다수의 객체가 존재할 수 있다.
request 하나의 Bean 정의에 대해서 하나의 HTTP request의 생명 주기 안에 단 하나의 객체만 존재한다.
즉, 각각의 HTTP request는 자신만의 객체를 가진다.
Web-aware Spring ApplicationContext안에서만 유효하다.
session 하나의 Bean 정의에 대해서 하나의 HTTP session의 생명주기 안에 단 하나의 객체만 존재한다.
Web-aware Spring ApplicationContext안에서만 유효하다.

 

1. Singleton Scope

- Spring Bean Default 설정이며 어플리케이션 구동시 JVM 안에 각각의 Bean 객체를 하만 생성하는 것을 의미합니다.

- 스프링의 컨테이너의 시작과 종료까지 유지되는 스코프를 가지고 있습니다.

 

2. Prototype Scope

- 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고 이후 프토토타입을 받은 클라이언트과 관리하는 짧은 범위의 스코프를 말합니다.

 

※ 프트토타입 사용시에 주의할 점

프토토 타입 빈 요청 경우 스프링 컨에티너에 따라 항상 새로운 객체를 생성하며 반환해주기를 기대하지만, 싱글톤과 함께 활용할 시 의도한 대로 동작하지 않을 수 있습니다.

 

prototype bean 주의점

싱글톤 빈 내부에 프로토타입 빈 경우 싱글톤 생성 시점에 프로토타입 빈을 주입 받은 후, 해당 빈을 재사용함으로써 빈이 계속 생성되지 않는 문제가 있습니다. 이는 하단에 포스탕한 ObjectProvider로 해결 할 수 있습니다.

 

4.  Web Scope

Request scope : 각 요청에 따라 지정된 빈의 전용 인스턴스를  생성하고, 요청이 완료될 때까지 빈을 계속 사용할 수 있다.

Session scope : 웹 세션에 따라 빈 전용 인스턴스를 생성하며, 세션이 종료될 때까지 빈을 계속 사용 할 수 있다.

application scope : 서블릿 컨텔스트와 동일한 생명 주기를 가지고 있는 스코프입니다.

 

※ Request scope 주의할 점

- request 범위에 있기 때문에 요청이 있을때까지 bean의 인스턴스가 생성되지 않으며, reqquest scope를 가진  bean을 주입 받아 사용하는 클래스는 웹 어플리케이션이 시작 될 때 자동으로 인스턴스화 하는데 이때 request scope를 가진 bean을 바로 주입하는것이 아닌 rpoxy 인스턴스를 생성하여 주입하여야한다.

 

5. Provider, PrxoyMode

위에서 확인된 문제들을 해결하는 첫번째 방법은 ObjectProvider를 사용하는것입니다.

ObjectProvider는 지정한 빈을 getObject()를 호출한 시점에 스프링 컨테이너에 요청해서 제공해주는 기능이다.

즉 ObjectProvider는 DL(Dependency Lookup)의 기능을 제공해주는 것입니다.

그러나 ObjectProvider는 스프링에 의존적이라는 단점이 있습니다.

 

5.1 ObjectProvider Prototype

@Scope("singleton")
public class ClientBean {

    @Autowired
    private ObjectProvider<PrototypeBean> prototypeBeanProvider;
    
    public int logic() {
    	PrototypeBean prototypeBean = prototypeBeanProvider.getObject();
        prototypeBean.addCount();
        int count = prototypeBean.getCount();
        return count;
    }
    
    @Scope("prototype")
    static class PrototypeBean {
    	private int count;
        
        public void addCount() {
        	this.count ++;
        }
        public int getCount() {
        	return this.count;
        }
    
    }
}

 

5.2 request scope controller

@RequiredArgsConstructor
@Controller
public class CmmnController {

    private final ObjectProvider<MyLogger> provider;
    private final UserService userService;

    @GetMapping("/login")
    @ResponseBody
    public String login(HttpServletRequest request){
        String requestUrl=request.getRequestURL().toString();
        MyLogger myLogger = provider.getObject();
        myLogger.setRequestURL(requestUrl);

        myLogger.log("controller");
        userService.logic("service logic");
        return "login";
    }
}

 

5.3 request scope service

@RequiredArgsConstructor
@Service
public class UserService {

    private final ObjectProvider<MyLogger> provider;

    public void logic(String msg) {
        MyLogger myLogger = provider.getObject();
        myLogger.log(msg);
    }
}

 

 

ObjectProvider는 실제 빈 생성을 지연하여 필요한 시점에 메소드를 통해 호출함으로써 해결하는것을 볼 수 있었습니다.

 

5.4 request scope proxyMode

@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {

...
}

 

@Scope에 proxyMode를 ScopedProxyMode.TARGET_CLASS 를 추가해주자

  • 적용 대상이 인터페이스가 아닌 클래스면 TARGET_CLASS 를 선택
  • 적용 대상이 인터페이스면 INTERFACES 를 선택

이렇게 하면 프록시 클래스를 만들어두고 HTTP request와 상관 없이 프록시 클래스를 다른 빈에 미리 주입해 둘 수 있다.

이렇게 한 뒤 스프링을 동작시켜 요청을 보내보면 아까와 같이 로그가 찍힌다.

 

이렇게 함으로써 의존성 주입 당시에는 미리 프록시 객체를 채워두고 실제 필요할 당시에 빈을 주입하는 방식으로 처리된다 프록시를 구현할때는 JDK 동적 프록시, CGLib가 존재하며 해당 파트는 다루지는 않겠습니다.

 

https://velog.io/@may_yun/Spring-Bean-Scope%EC%99%80-%EC%A2%85%EB%A5%98

https://ttl-blog.tistory.com/100

https://chung-develop.tistory.com/64

https://devel-repository.tistory.com/45