해당 포스팅은 인프런에서 제공하는 김영한 님의 '스프링 핵심원리 기본편'을 수강한 후 정리한 글입니다. 유료 강의를 정리한 내용이기에 제공되는 예제나 몇몇 내용들은 제외하였고, 정리한 내용을 바탕으로 글 작성자인 저의 언어로 다시 작성한 글이기에 서술이 부족하거나 잘못된 내용이 있을 수 있습니다. 그렇기에 해당 글은 개념에 대한 참고 정도만 해주시고, 강의를 통해 학습하시기를 추천합니다.
스프링 빈을 등록할 때 자바 코드의 @Bean
이나 XML의 <bean>
등을 통해서 설정 정보에 직접 작성한다. 그러나 등록해야 할 스프링 빈의 수가 늘어날 수록 설정 파일에 일일히 등록하는 방법은 생산성이 떨어질 수 밖에 없으며, 설정 정보가 커지고 빈이 누락되는 문제도 발생할 수 있다. 그렇기에 스프링은 설정 정보 없이도 자동으로 스프링 빈을 등록할 수 있도록 컴포넌트 스캔(Component Scan)이라는 기능을 제공하며, 의존 관계 주입을 위해 @Autowired
기능을 제공한다.
@Configuration
@ComponentScan
public class SampleAppConfig {...}
컴포넌트 스캔 사용을 위해서는 @ComponentScan
을 설정 정보에 명시해준다. @Bean
으로 클래스 등록을 하지 않아도 되며, @SpringBootApplication
어노테이션을 따라가 보면 @ComponentScan
이 등록되어 있는 것을 확인할 수 있다.
@ComponentScan
public class SampleRepository {...}
@ComponentScan
public class SampleService {
private final SampleRepository sampleRepository;
@Autowired
public SampleService(SampleRepository sampleRepository) {
this.sampleRepository = sampleRepository;
}
}
@Component
과 @Autowired
어노테이션을 사용할 경우 Config 파일에서 @Bean
을 통해 직접 설정 정보를 작성하고 의존 관계를 명시하지 않아도 스프링 빈으로 등록해주며, 의존 관계도 자동으로 주입해준다.
@ComponentScan
@ComponentScan
은 @Component
가 붙은 모든 클래스를 스프링 빈으로 등록한다. 스프링 빈의 기본 이름은 클래스명의 앞글자만 소문자로 사용하며, @Component("sampleBean")
처럼 직접 지정할 수도 있다.
@Autowired
생성자에 @Autowired
를 지정하면 스프링 컨테이너가 해당 스프링 빈을 찾아 자동으로 의존 관계를 주입해준다. 기본 조회 전략은 getBean(SampleRepository.class)
와 같이 타입이 같은 빈을 찾아 주입하는 것이다.
모든 자바 클래스를 모두 컴포넌트 스캔할 경우 탐색에 필요한 시간이 증가하여 성능이 떨어질 수 있다. 그렇기에 꼭 필요한 위치부터 탐색하도록 @ComponentScan(basepackage = "sample.repository")
와 같이 탐색 시작 위치를 지정할 수도 있다.
또한 basepackage = {"sample.repository", "sample.service"}
와 같이 다중 설정도 가능하며, 지정하지 않을 경우 @ComponentScan
이 붙은 설정 정보 클래스의 패키지가 시작 위치가 된다.
스프링 부트가 기본적으로 제공하는 방법은 패키지 위치 지정 없이 설정 정보 클래스를 프로젝트 최상단에 두는 것이다. 위에서 언급한 @SpringBootApplication
에 @ComponentScan
이 붙어 있는 이유 또한 이러한 관례 때문이다.
컴포넌트 스캔은 @Component
을 포함하여 다음 어노테이션들을 스캔하여 빈으로 등록하며, 각 어노테이션 별로 부가 기능을 수행하기도한다.
@Component
: 컴포넌트 스캔 사용@Controller
: 스프링 MVC 컨트롤러 사용, 스프링 MVC 컨트롤러로 인식@Service
: 스프링 비지니스 로직 사용, 특별한 부가 기능은 없지만 개발자들이 비지니스 계층을 인식하는데 도움이 된다.@Repository
: 스프링 데이터 접근 계층 사용, 스프링 데이터 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환해준다.@Configuration
: 스프링 설정 정보 사용, 스프링 설정 정보로 인식하고, 스프링 빈이 싱글톤을 유지하도록 추가 처리를 한다.위 어노테이션들을 추적해보면 모두 @Component
가 포함되어 있는 것을 확인할 수 있다.
필터는 컴포넌트 스캔 대상을 추가로 지정하고나 제외할 대상을 지정하는데 사용한다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SampleComponent {...}
includeFilters
: 컴포넌트 스캔 대상을 추가로 지정한다.@Configuration
@ComponentScan(
includeFilters = @Filter(type = FilterType.ANNOTATION,
classes = MyIncludeComponent.class))
static class ComponentFilterAppConfig {}
excludeFilters
: 컴포넌트 스캔에서 제외할 대상을 지정한다.@Configuration
@ComponentScan(
excludeFilters = @Filter(type = FilterType.ANNOTATION,
classes = MyIncludeComponent.class))
static class ComponentFilterAppConfig {}
org.example.SampleAnnotation
org.example.SmapleClass
org.example..*Service+
org\.example\.Default.*
TypeFilter
라는 인터페이스를 구현해서 처리org.example.SampleTypeFilter
컴포넌트 스캔에서 같은 빈 이름을 등록할 경우
ConflictingBeanDefinitionException
예외 발생스프링이 관리하는 스프링 빈을 사용해 의존관계를 주입하는 방법은 크게 생성자 주입과 수정자 주입, 필드 주입 그리고 일반 메서드 주입으로 나뉠 수 있다.
생성자를 통해 의존 관계를 주입 받는 방법으로, 생성자 호출 시점에 딱 한 번만 호출되는 것이 보장된다. 불변, 필수 의존관계에 사용되며, 생성자가 딱 한개만 있을 경우 @Autowired
를 생략할 수 있다.(스프링 빈에만 해당)
@Component
public class Test {
private final TestBean testBean;
@Autowired // 생성자가 하나일 경우 생략 가능
public Test(TestBean testBean) {
this.testBean = testBean;
}
}
자바빈 프로퍼티 규약의 수정자 메서드 방식을사용한 setter 메서드를 통해 의존관계를 주입하는 방법으로, setter 주입이라고도 한다. 선택, 변경 가능성이 있는 의존관계에 사용된다. @Autowired
는 기본적으로 주입할 대상이 없으면 오류가 발생한다. 주입할 대상 없이 동작하게 하기 위해서는 @Autowired(required = false)
속성을 사용하면 된다.
@Component
public class Test {
private TestBean testBean;
@Autowired
public void setTest(TestBean testBean) {
this.testBean = testBean;
}
}
필드에 바로 주입하는 방법으로, 코드가 간결하다는 장점이 있지만 외부에서 변경이 불가능하기에 테스트가 어려우며, DI 프레임워크 없이는 아무 것도 할 수 없다는 단점이 존재하기 때문에 사용을 지양해야 한다. 어플리케이션의 실제 코드와 관계 없는 테스트 코드나 스프링 설정을 목적으로 하는 @Configuration
같은 곳에서만 특별한 용도로 사용한다.
@Component
public class Test {
@Autowired
private TestBean testBean;
}
일반 메서드를 통해 한 번에 여러 필드를 주입 받을 수 있지만, 일반적으로 잘 사용하지 않는다.
@Component
public class Test {
private TestBean testBean;
@Autowired
public void init(TestBean testBean) {
this.testBean = testBean;
}
}
대부분의 의존관계는 어플리케이션이 종료되기 까진 변하면 안된다. 수정자 주입을 사용할 경우 setter 메서드를 public으로 열어두어야 하며, 변경 하면 안되는 메서드를 열어두는 것은 좋은 설계가 아니다. 생서자 주입을 사용할 경우 생성 단계에서 단 한 번만 생성해 싱글턴으로 관리하게 되므로 불변하게 설계할 수 있다.
생성자 주입을 사용할 경우 주입 데이터를 누락 했을 때 컴파일 오류가 발생한다. 또한 IDE를 통해 필수로 주입되어야 하는 값을 확인할 수 있다.
생성자 주입 방식은 프레임워크에 의존하지 않고, 순수한 자바 언어의 특징을 살린 방법이다. 기본적으로 생성자 주입을 사용하고, 필수 값이 아닌 경우 수정자 주입 방식을 옵션으로 선택하면 된다.