스프링을 공부하다보면 Bean이라는 단어를 종종 듣게된다.
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 관련 글을 통해서 이야기 해보도록 하겠다.

Bean 생성방법

Component Scanning 방법과 혹은 직접 일일히 XML 이나 자바 설정파일에 등록하는 방법이다.

Component Scanning

이를 이해하기 위해 조금 깊게 알아보도록 하자.
우리가 흔히 @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로 묶여있다고 생각을 하면 편하다.

그럼 어디서 Scanning을 하는것일까?

우리가 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으로 등록을 해준다.

Java 설정

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객체가 된것이다.

Bean 객체 불러오기

가볍게 테스트코드를 작성해서 알아보도록 하겠다.

ApplicationContext만 이용한 방법

위에 처음 예시를 들어 설명한 방법이 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를 이용해서 불러올 수 있다.

Autowired + ApplicationContext

@SpringBootTest
public class TestConrollerTest{
	@Autowired
	ApplicationContext applicationContext;

	@Test
	public void TestDI(){
		TestConroller bean = applicationCotnext.getBean(TestConroller);
		assertThat(bean).isNotNull;
	}
}

Autowired 어노테이션을 사용하여 생성자 없이도 사용할 수 있게 된다.


아래 영상을 토대로 필자가 정리한 내용이다. 한번쯤 보면 정말 좋을거같다. 진짜 좋은 영상이다.
예제로 배우는 스프링 입문 7. 빈 (bean)

profile
지금보다 내일을, 모레를 준비하자

0개의 댓글