이번에는 이전 글과 관련이 있는 @Component
, @ComponentScan
, @Autowired
를 정리하려하고 한다.
스프링 특징으로 IoC,DI가 있다.
그말은 IoC를 잘 활용하는 코드를 작성해야한다는 말이라고 생각하는데 그것을 @Component
가 도와주는 것이다.
IoC는 프레임워크가 개발자 대신 객체의 제어권을 갖는 것인데, 개발자는 스프링에 관리를 부탁할 클래스에 컴포넌트 어노테이션으로 명시해준다.
이렇게 명시한 클래스들을 Bean으로 만들어서 Spring Container에 넣어서 보관하여 관리해준다.
- 그럼 진짜 컨테이너에서 관리하고 있는지 확인방법이 있을까?
내가new 클래스명()
을 호출하지 않아도 클래스가 객체화 되는지 확인하는 방식으로
테스트를 해보기로 했다.
생성자에 출력문을 넣어서 스프링 애플리케이션을 실행했을 떄 출력되는지 확인해보자
import org.springframework.stereotype.Component;
@Component
public class House {
private String houseName;
House() {
System.out.println("House created");
}
}
이렇게 만들어서 실행했는데 처음에 생성자가 출력문이 실행되지 않았다.
빠르게 이유만 말하자면 @SpringBootApplication
을 가진 실행 패키지
상위에 클래스를 생성하여 실행되지 않는 문제였다.
동일 위치, 하위 경로에 클래스를 새로 만들어서 컴포넌트 어노테이션을 붙였을 때는
정상적으로 실행되는 것을 확인할 수 있었다.
그렇다면 분명 @SpringBootApplication
이 어노테이션이 컴포넌트와 연관이 있을거라는
의문이 들기 시작했다. 이 의문은 아래 더 정리하면서 천천히 풀어가보자
이름을 보자마자 너무 직관적인 네이밍 클린코드!!!
보자마자 이 어노테이션은 컴포넌트 어노테이션을 찾아주는 역할을 할 것이라는 생각을 했고
역시나 기대한 역할을 수행하는 어노테이션이었다.
나는 위에서 컴포넌트 에너테이션은 클래스위에 명시했지만 컴포넌트 스캔 어노테이션은
그 어디에도 작성하지 않았는데 아주 잘 컴포넌트를 찾아서 빈으로 만들어 관리해줬다.
@SpringBootApplication
요 녀석과 관련이 있지 않을까 싶어 해당 어노테이션을
찾아보면 @ComponentScan
을 포함하고 있다는 것을 알 수 있었다.
이렇게 컴포넌트 스캔이 실행되면서 IoC할 수 있도록 빈으로 만들어 스프링 컨테이너에 보관한다.
여기서 아래와 같은 의문이 생긴다.
B 클래스 생성자에서 A를 파라미터로 받아서 사용한다면 어떻게 처리가 될까?
직접 개발자가 new B(new A())
를 해주는 것이 아닌데 어떻게 하지?
하지만 이것이 가능해야 진정한 IoC가 아닐까 싶다!
먼저 답을 말하면 스프링은 잘 주입해서 제공한다.
너무 궁금해서 아래와 같은 상황을 실행했다.
@Component
public class Person {
private String name;
private House house;
Person(House house) {
this.house = house;
System.out.println(house.getHouseName() + "집을 샀습니다.");
}
}
이렇게 Person 클래스를 생성하고 실행하면
스프링은 house.getHouseName()
을 잘 실행하고 집을 샀다고 출력되는 걸 확인할 수 있다.
- 스프링은 신인가?
그냥 알아서 척척 주입이 필요하면 넣어주고@Component
와는 다르게 스프링에게 DI 해줘 라고 안알려줘도 되는건가?
아쉽게도 아니다
그럼 어떻게 주입을 결정하나요?
스프링에는 DI를 위한 @Autowired
어노테이션이 존재한다.
필요한 의존 객체의 “타입”에 해당하는 빈을 찾아 주입한다. 는 기능이 있다.
먼저 위에서 스프링 빈으로 만드는 과정에서 생성자에서 필요한 DI는 어떻게 처리되었는지를 말하면
스프링에서는 의존성을 주입하는 생성자가 하나면 해당 생성자에 자동으로 @Autowired
어노테이션이
착한 사람들에게만 보이게 있다.
따라서 Person 생성자에 필요한 House 객체를 주입하여 정상적으로 작동할 수 있었다.
의존성 주입이 없는 경우 사전 순서로 먼저 스프링 빈이 되는 것을 여러 이름의 클래스를 만들어서
테스트해보면서 알 수 있었다. 믿거나 말거나~
해당 어노테이션은 3가지 방법으로 사용할 수 있다.
왜 3가지냐 ~! 클래스를 구성하는 3요소가 무엇일까요?
필드, 생성자, 메소드
이 3 요소에 모두 @Autowired
사용 가능하다.
지금 글을 작성하는 입장에서는 나는 거의 모든 @Autowired
를
생성자에 붙여서 사용할 것이다.
일단 필드에 주입을 한다면 의도와는 다르게 Setter나 생성자에 의해서
덮어씌어지는 경우가 발생할 것이고, Setter를 사용해서 클래스가 의존하고 있는
객체를 동적으로 바꾸는 일이 생기는 상황이 현재는 그려지지 않기 때문이다.
스프링으로 많은 프로젝트를 경험해보면서 생성자 이외에 @Autowired
를 붙여서
사용해야 하는 상황이 생긴다면 추가 경험담을 작성하러 오겠습니다.