Spring Bean이 존재할 수 있는 범위를 뜻한다.
SCOPE_SINGLETON
: 기본값. "singleton"
SCOPE_PROTOTYPE
: "prototype"
SCOPE_REQUEST
: Web Request 당 하나의 Bean을 생성한다. "request"
SCOPE_SESSION
: Web Session 당 하나의 Bean을 생성한다. "session"
SCOPE_APPLICATION
: Web의 ServletContext와 같은 범위로 유지되는 Scope. "application"
@Scope
어노테이션 설정@Scope("prototype")
@Component
public class PrototypeBean {
}
import java.beans.BeanProperty;
@Scope("prototype")
@Bean
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
@AliasFor("scopeName")
String value() default "";
/**
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory#SCOPE_PROTOTYPE
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory#SCOPE_SINGLETON
* @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
* @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
*/
@AliasFor("value")
String scopeName() default "";
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
public enum ScopedProxyMode {
DEFAULT,
NO,
/**
* JDK dynamic proxy
*/
INTERFACES,
/**
* Create a class-based proxy (uses CGLIB).
*/
TARGET_CLASS
}
Spring Container에 요청할 때 마다 새로운 Bean을 생성한다.
Spring Container는 Prototype Scope Bean을 생성하고 의존관계를 주입하고 초기화 메서드 실행까지만 관여한다.
종료 메서드가 호출되지 않는다.
@PreDestory
같은 종료 메서드가 호출되지 않는다.ApplicationContext::getBean
메서드를 사용하여 Prototype Scope Bean을 조회한다.
@RequiredArgsConstructor
static class SingletonBean {
private final ApplicationContext applicationContext;
public int logic() {
PrototypeBean prototypeBean = applicationContext.getBean(PrototypeBean.class);
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
@Getter
@Scope("prototype")
static class PrototypeBean {
private int count = 0;
public void addCount() {
count++;
}
@PostConstruct
public void init() {
System.out.println(this.getClass().getSimpleName() + " init... " + this);
}
@PreDestroy
public void destroy() {
System.out.println(this.getClass().getSimpleName() + " destroy");
}
}
ObjectProvider<T>
: ObjectFactory<T>
를 상속하고 편리한 기능들을 추가 제공한다.@FunctionalInterface
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}
public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
T getObject(Object... args) throws BeansException;
@Nullable
T getIfAvailable() throws BeansException;
default T getIfAvailable(Supplier<T> defaultSupplier) throws BeansException {
T dependency = getIfAvailable();
return (dependency != null ? dependency : defaultSupplier.get());
}
default void ifAvailable(Consumer<T> dependencyConsumer) throws BeansException {
T dependency = getIfAvailable();
if (dependency != null) {
dependencyConsumer.accept(dependency);
}
}
@Nullable
T getIfUnique() throws BeansException;
default T getIfUnique(Supplier<T> defaultSupplier) throws BeansException {
T dependency = getIfUnique();
return (dependency != null ? dependency : defaultSupplier.get());
}
default void ifUnique(Consumer<T> dependencyConsumer) throws BeansException {
T dependency = getIfUnique();
if (dependency != null) {
dependencyConsumer.accept(dependency);
}
}
@Override
default Iterator<T> iterator() {
return stream().iterator();
}
default Stream<T> stream() {
throw new UnsupportedOperationException("Multi element access not supported");
}
default Stream<T> orderedStream() {
throw new UnsupportedOperationException("Ordered element access not supported");
}
}
코드 예시
@RequiredArgsConstructor
static class SingletonBean {
private final ObjectProvider<PrototypeBean> objectProvider;
public int logic() {
PrototypeBean prototypeBean = objectProvider.getObject();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
@Scope("prototype")
static class PrototypeBean {
@Getter
private int count = 0;
public void addCount() {
count++;
}
@PostConstruct
public void init() {
System.out.println(this.getClass().getSimpleName() + " init... " + this);
}
@PreDestroy
public void destroy() {
System.out.println(this.getClass().getSimpleName() + " destroy");
}
}
javax.inject:javax.inject:1
라이브러리를 추가해야 한다.public interface Provider<T> {
T get();
}
코드 예시
@RequiredArgsConstructor
static class SingletonBean {
private final Provider<PrototypeBean> provider;
public int logic() {
PrototypeBean prototypeBean = provider.get();
prototypeBean.addCount();
return prototypeBean.getCount();
}
}
@Getter
@Scope("prototype")
static class PrototypeBean {
private int count = 0;
public void addCount() {
count++;
}
@PostConstruct
public void init() {
System.out.println(this.getClass().getSimpleName() + " init... " + this);
}
@PreDestroy
public void destroy() {
System.out.println(this.getClass().getSimpleName() + " destroy");
}
}
@Lookup
을 활용하여 Dependency Lookup 서비스를 사용할 수 있다.@RequiredArgsConstructor
class SingletonBean {
@Lookup
public PrototypeBean getPrototypeBean() {
return null;
}
}
getPrototypeBean()
메서드를 오버라이딩한다.@RequiredArgsConstructor
class SingletonBean {
@Lookup
public PrototypeBean getPrototypeBean() {
return new PrototypeBean();
}
}
getPrototypeBean()
메서드를 오버라이딩한다.@RequiredArgsConstructor
abstract class SingletonBean {
@Lookup
public abstract PrototypeBean getPrototypeBean();
}
@PreDestroy
같은 종료 메서드가 호출된다.request
: HTTP 요청 하나가 들어오고 나갈 때 까지 유지되는 Scope, 각각의 HTTP 요청마다 별도의 Bean 인스턴스가 생성되고, 관리된다.session
: HTTP Session과 동일한 생명주기를 가지는 Scopeapplication
: ServletContext와 동일한 생명주기를 가지는 ScopewebSocket
: Web Socket과 동일한 생명주기를 가지는 ScopeApplicationContext::getBean
ObjectFactory<T>
, ObjectProvider<T>
Provider
@Lookup
어노테이션
@Component
@Scope("request")
@Slf4j
public class MyLogger {
private String uuid;
@Setter
private String requestURL;
@PostConstruct
public void init() {
uuid = java.util.UUID.randomUUID().toString();
log.info("[{}] request scope bean create: {}", uuid, this);
}
@PreDestroy
public void close() {
log.info("[{}] request scope bean close: {}", uuid, this);
}
public void log(String message) {
log.info("[{}][{}] {}", uuid, requestURL, message);
}
}
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final ObjectProvider<MyLogger> myLoggerProvider;
public void logic(String id) {
MyLogger myLogger = myLoggerProvider.getObject();
myLogger.log("service id = " + id);
}
}
proxyMode = ScopedProxyMode.TARGET_CLASS
옵션을 사용한다.proxyMode = ScopedProxyMode.INTERFACES
옵션을 사용한다.@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {
// ...
}
코드 예시
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Slf4j
public class MyLogger {
// ...
}
@Service
@RequiredArgsConstructor
public class LogDemoService {
private final MyLogger myLogger;
public void logic(String id) {
myLogger.log("service id = " + id);
}
}