스프링을 공부하다보면 Bean이라는 단어를 종종 듣게된다.
Bean? 콩? 막 진짜 여러 생각을 하게 되는데 이게 뭔지 매번 넘겨짚다가 이제는 제대로 공부해야겠다는 생각이 들었다.
한번 제대로 알아보도록 하자.
스프링은 스프링컨테이너를 통해 객체를 관리하는데, 스프링 컨테이너에 관리되는 객체를 Bean 이라고 한다.
이 글을 보고 이와같이 생각하는 사람이 있을것이다.
객체? 객체가 Bean 이라면 아래 코드도 Bean인가?
BeanTest bean = new BeanTest();
아니다. 이는 그냥 객체이지 Bean은 아니다.
아니 Bean이 객체라며? 그런데 이건 왜 아니야?
정확히 말하면 IoC 컨테이너가 관리하는 객체이다.
아니 이게 도대체 뭔 소리인가 싶다.
위 코드가 객체인데 Bean이 아니다. IoC 컨테이너에서 관리를 하지 않기 때문이다.
그럼 이 코드는 어떨까?
ApplicationContext applicationContext;
public void Bean(){
BeanTest bean = applicationContext.getBean(BeanTest.class);
}
여기에 선언된 bean은 Bean이 맞는가? 맞다. applicationContext에서 관리를 하기 때문이다.
applicationContext는 Bean Factory를 상속한 스프링 컨테이너의 종류인다.
스프링 컨테이너에서 관리한다, IoC 컨테이너에서 관리한다. 이게 다 뭔소리인가?
처음엔 필자도 두개가 다른거라고 생각했지만, 공부를 하다보니 이 두개가 같은거라고 할 수 있다고 한다.
왜 같은지는 IoC/DI 관련 글을 통해서 이야기 해보도록 하겠다.
Component Scanning 방법과 혹은 직접 일일히 XML 이나 자바 설정파일에 등록하는 방법이다.
이를 이해하기 위해 조금 깊게 알아보도록 하자.
우리가 흔히 @Controller 혹은 @RestController 를 사용하는데, 이들은 Component라는 어노테이션으로 묶여있다.
이게 무슨소리인가 감이 잡히지 않을것이다.
처음부터 천천히 파고들어보자.
@RestController
public void getBeans(){}
이와같은 RestController가 있다고 하자.
여기서 RestController는 어떻게 생겼을까?
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
@AliasFor(annotation = Controller.class)
String value() default "";
}
이런식으로 생겼다. 위에 여러 어노테이션들이 있는데 필자가 위에 언급했듯 Controller 는 Component로 묶여있다고 했다. 그럼 Controller안으로 들어가보도록 하겠다.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(annotation = Component.class)
String value() default "";
}
Controller는 이렇게 생겨먹은놈인데 보면 Component 어노테이션을 사용하고있다. 더 들어가보면 좋겠지만, 우선 Component Scanning을 알아보기 위함이니 우선 여기까지만 알아보고,Controller어노테이션과 RestController어노테이션은 Component로 묶여있다고 생각을 하면 편하다.
우리가 SpringBoot 애플리케이션을 제작하면 src/main/java 안에 [프로젝트명]Application.java 라는 파일이 하나 있을것이다. 이 안을 한번 봐보자.
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
이런식으로 작성이 되어있다.
SpringBootApplication이라는 어노테이션을 통해 Spring Boot의 시작점이라는것을 알려준다.
그런데 갑자기 왜 이런말을 하냐? 이 어노테이션 안에는 Component Scanning을 하는 로직이 들어있다.
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication { ... }
좀 생략된 부분이 많은데, SpringBootApplication 어노테이션이 어떻게 생겨먹은건지 보면 ComponentScan 이 있다. 그렇다, 바로 이 어노테이션 안에서 Component Scanning을 하는 기능이 들어있는것이다.
안에 들어가보면 다양한 로직들이 있는데 거기까지 알아보기에도 시간이 너무 오래걸리기에 여기까지만 알아보도록 하겠다.
ComponentScan 어노테이션은 “ Component를 찾아봐!” 라고 말하는 어노테이션이라고 할 수 있다.
SpringBootApplication 어노테이션이 붙은곳부터 모든 하위 패키지를 둘러보며 Component를 사용하는 어노테이션 즉, 필자가 앞서 말한 RestController 혹은 Controller 등이 있는데 꼭 이 두개만 있는것이 아니라, Repository, Service, Configuration 등 아주 많은것들이 있다. 그 중 대표적인것들이 필자가 지금 언급한것들이다.
이들을 전부 찾아 Bean으로 등록을 해준다.
XML 혹은 Java에서 설정을 해주는 방법인데, 필자는 Java에서 설정하는 방법을 알아보도록 하겠다.
public class TestConfig{}
위와같은 클래스가 있다고 가정을 해보자.
위 클래스에다가 Bean을 등록하는 설정 코드를 작성을 하도록 하겠다.
우선, Configuration 어노테이션을 작성하고, class안에 Bean을 등록해준다.
@Configuration
public class TestConfig{
@Bean
public TestController testController(){
return new TestController();
}
}
이와같은 형식으로 작성을 해주게 된다면 TestController는 Bean객체가 된다.
아니 그러면 RestController나 Conroller, Component 이런거 안붙혀도 Bean객체가 되는건가?
맞다. 안붙혀도 된다.
public class TestController{}
이런식으로 적어줘도 해당 TestController는 Bean객체가 된것이다.
가볍게 테스트코드를 작성해서 알아보도록 하겠다.
위에 처음 예시를 들어 설명한 방법이 ApplicationContext를 이용해서 Bean인지 아닌지 예시를 들어 설명을 했는데, 이를 이용해서 테스트코드를 작성해보도록 하겠다.
@SpringBootTest
public class TestConrollerTest{
private final ApplicationContext applicationContext;
public TestConrollerTest(ApplicationContext applicationContext){
this.applicationContext = applicationContext;
}
@Test
public void TestDI(){
TestConroller bean = applicationCotnext.getBean(TestConroller);
assertThat(bean).isNotNull;
}
}
이와같은 형태로 Bean을 불러올 수 있다.
그런데 이렇게 하면 생성자를 불러오다보니 코드가 좀 길어지는 기분이다.
이 때 Autowired를 이용해서 불러올 수 있다.
@SpringBootTest
public class TestConrollerTest{
@Autowired
ApplicationContext applicationContext;
@Test
public void TestDI(){
TestConroller bean = applicationCotnext.getBean(TestConroller);
assertThat(bean).isNotNull;
}
}
Autowired 어노테이션을 사용하여 생성자 없이도 사용할 수 있게 된다.
아래 영상을 토대로 필자가 정리한 내용이다. 한번쯤 보면 정말 좋을거같다. 진짜 좋은 영상이다.
예제로 배우는 스프링 입문 7. 빈 (bean)