이번 시간에는 Bean의 Scope
에 대해 자세히 알아보도록 하겠습니다. AOP, MVC를 포스팅하다가 갑자기 왜 다시 IoC 컨테이너에 관련해서 포스팅을 하냐면.. 제 얕은 지식으로 각각의 파트들이 끝난줄 알았지만, 공부를 할 수록 더 많은 것을 알게되면서 더 많은 정보를 포스팅 해야겠다는 생각이 들어서 입니다.
1. Bean의 Scope이란?
Bean의 Scope
은 Bean의 생성방식을 결정하는 것
입니다. 예를 들자면 Scope에 따라서 Bean이 Application당 1개만 생성되거나, 필요할 때마다 새로 생성을 한다던지 하는 그런한 방식을 의미합니다.
2. Bean의 Scope 종류
2.1 Singleton
어디서 많이 들어보셨을 것입니다. 저는 디자인 패턴을 공부하면서 먼저 접하게 되었던 단어 입니다. 간단히 말씀드리자면 어플리케이션이 동작하는동안 단 한개만
만들어진다는 의미입니다. Spring에서의 Bean들은 별도의 설정이 없다면 기본적으로
Singleton으로 생성이 됩니다. 즉, 지금까지 생성했던 Bean들은 모두 Singleton으로 주입이 되었던 것입니다. 예제를 통해 조금 더 자세히 알아보겠습니다.
@Component
public class Single { ... }
바로 위 Bean의 Scope
이 Singleton 입니다.
@Component
public class Test {
@Autowired
Single single;
public Single getSingle(){
return single;
}
}
테스트를 위해서 하나의 Class를 더 생성했습니다. Test Class에서는 Single 객체를 @Autowired
어노테이션을 통해 주입을 받습니다. getSingle()
메소드를 가지고 있군요.
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
Single single;
@Autowired
Test test;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(single);
System.out.println(test.getSingle());
}
}
실행을 위한 runner Class입니다. 앞서 생성한 Single, Test Class를 @Autowired
어노테이션을 통해 주입받고 있습니다. 실행시에는 주입받은 Single
객체를 출력하고, 주입받은 test
객체를 통해 single을 얻어와 또 출력하도록 했습니다. 결과는?
네, 당연히 두 경우 모두 같은 객체를 출력하는 것을 볼 수 있습니다.
2.2 Prototype
다음은 Prototype Scope
입니다. Prototype
의 경우에는 Bean을 IoC컨테이너로 부터 받아올 때마다 매번 새로운 객체
를 생성하게 됩니다. 역시 예제를 통해 알아보도록 하겠습니다.
@Component
@Scope("prototype")
public class Proto { ... }
Prototype Scope
을 가지는 Proto
Class 입니다. 클래스 상단에 @Component
어노테이션 이외에 @Scope("prototype")
어노테이션을 부여해 Scope를 Prototype으로 지정했습니다.
@Component
public class Single {
@Autowired
Proto proto;
public Proto getProto() {
return proto;
}
}
Single Class입니다. Proto 객체를 주입받고, 그 객체를 리턴해주는 getProto()
메소드를 가지고 있습니다.
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
Single single;
@Autowired
Proto proto;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(proto);
System.out.println(single.getProto());
}
}
테스트를 위한 Runner Class 입니다. Single
에서 주입 받도록한 Proto
외 또 하나의 Proto
객체를 주입받는 것을 볼 수 있습니다. AppRunner Class에서 주입받은 proto
와 Single Class 에서 주입받은 proto
를 출력 해보도록하겠습니다.
결과는? 네, 서로 다른 객체가 생성되어 출력된것을 볼 수 있습니다. 이처럼 Prototype은 IoC컨테이너에 의해 Bean을 주입을 받을 때 마다 새로운 객체가 생성됩니다.
xxxxxxxxxx
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
Proto proto;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(proto);
System.out.println(proto);
System.out.println(proto);
}
}
여기서 주의할 점은 같은 곳에있는 객체를 계속 호출한다고해서 계속해서 객체가 새로 생겨난다는 의미가 아닌것 입니다.
위의 결과는 당연히 같은 객체가 찍히겠죠?
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext ctx;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(ctx.getBean("proto"));
System.out.println(ctx.getBean("proto"));
System.out.println(ctx.getBean("proto"));
}
}
위의 결과는 어떨까요?
네, 모두 다른 객체가 출력되는 것을 볼 수 있습니다. 바로 IoC 컨테이너로 부터 Bean을 가져올때마다 새로운 객체가 생성되기 때문이죠.
2.2.1 Singleton Scope안의 Prototype Scope
Prototype Scope
을 사용하실때에는 Singleton Scope
안에 주입되는 Prototype Scope
을 주의하여 사용해야 합니다. 무슨말이냐구요? 예제를 보도록하죠.
@Component
@Scope("prototype")
public class Proto {}
우선 Proto Class는 매번 새로운 객체를 주입 받기 위해 prototype의 Scope
를 가지도록 했습니다.
@Component
public class Single {
@Autowired
Proto proto;
public Proto getProto() {
return proto;
}
}
Single Class의 경우에는 아무런 Scope을 지정하지 않았으므로 Singleton Scope
을 기본으로 가지게 됩니다. 또한, Proto 객체를 주입받고, 그것을 리턴하는 메소드를 만들었군요. 문제는 여기서 발생합니다. 우선 한번 실행 해보도록 하겠습니다.
x
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext ctx;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println(ctx.getBean(Single.class).getProto());
System.out.println(ctx.getBean(Single.class).getProto());
System.out.println(ctx.getBean(Single.class).getProto());
}
}
== 결과 ==
com.keesun.spring.Proto@58c1da09
com.keesun.spring.Proto@58c1da09
com.keesun.spring.Proto@58c1da09
어?, proto의 경우에는 IoC 컨테이너에 의해 Bean을 주입을 받을때마다 새로운 객체가 생성된다고 하지 않았냐구요?
잘 생각해보아야 합니다. single
에 IoC 컨테이너가 Bean을 주입해줄 때 single
의 필드에 존재하는 Proto
에 또 주입이 되고 있는 형태입니다. 이때 proto는 Prototype
이지만 single은 Singleton
입니다. 즉 Single안에 존재하는 Proto는 새로 Bean이 생성되는 일이 없다는 것이죠.
다시 한번 말씀드리자면, Proto는 IoC Container로 부터 해당 빈을 요청할때마다 새로운 빈을 반환하는 scope입니다. 하지만, Single내에 한번 주입이 되고 난 이후에는, IoC Container로 부터 빈을 받아오는것이 아니므로 새로 생성되지 않습니다.
해결방법
1. Proxymode 사용
@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class Proto {}
간단히 말씀드리면, proto의 @Scope
어노테이션 안에 proxyMode를 추가하면 됩니다.
xxxxxxxxxx
@Component
public class Single {
@Autowired
Proto proto;
이렇게 된다면 위의 proto안에 Proto를 감싸고 있는 Proxy객체가 주입되게 됩니다. Proto를 감싸는 Proxy는 자동으로 Proto를 상속받아 생성되므로 Proto타입의 참조변수 안에 주입이 가능해지게 됩니다.
결국에는, Single안의 Proto의 경우는 Proxy에 의해 새로운 객체들이 생성되어 출력되는 것을 볼 수 있습니다. 이게 어떻게 가능할까요?
바로 Single안의 Proto의 참조를 하는 경우 Proxy를 통해 참조하도록 하여 새로운 객체를 돌려주도록 하는 것입니다. 왜 Proxy로 감싸야할까요? 반대로 한번 생각해보면 간단합니다.
Single안에 있는 Proto를 직접 참조하게 된다면 중간에 Spring이 개입하여 새로운 객체를 반환하도록 할 수 있는 개입의 여지가 아예 없기 때문입니다.
2.3 이밖에 Scope..
'FrameWork > Spring' 카테고리의 다른 글
Spring - IoC 컨테이너의 기능 - 3 (MessageSource 란?) (0) | 2019.04.13 |
---|---|
Spring - IoC 컨테이너의 기능 - 2 (Environment 란? , 분석 (Property, ConfigurableEnvironment, MutablePropertyResource)) (0) | 2019.04.12 |
Spring - @Autowired 분석! (2) | 2019.04.10 |
Spring - @ComponentScan 어노테이션이란? (2) | 2019.04.10 |
Spring - @(Annotation) 사용시 알 수 없는 에러 (0) | 2019.04.04 |