김영한님의 Spring 핵심원리 강의를 듣고 정리하는 글입니다.
1. 컴포넌트 스캔(Component Scan)
지금까지 스프링 빈에 등록하고 의존관계를 주입할 때 @Bean이나 XML 파일을 통해서 설정 정보에 등록하여 스프링 빈에 등록하고, 의존관계를 설정했다. 복잡도가 커지면 이 방법 또한 매우 귀찮은 작업이다.
@CompoentScan을 통해 @Component로 설정된 클래스는 스프링 빈에 등록하고 @Autowired를 통해 의존관계를 자동으로 주입하는 방법에 대해 알아보자.
2. 컴포넌트 스캔 사용 방법
AutoAppConfig.java(컴포넌트 탐색 역할 담당)
@Configuration
@ComponentScan
public class AutoAppConfig {
}
- AutoAppConfig는 컴포넌트를 탐색하는 클래스로 설정을 담당한다.
@ComponentScan
스캔 대상을 지정하기 위한 옵션을 제공한다.basePakages: “패키지 경로”
⇒ 탐색할 시작 위치를 지정하여, 시작 위치에서 하위 패키지로 컴포넌트를 스캔한다.basePackageClasses: 클래스타입
⇒ 해당 클래스의 패키지 경로를 탐색 위치로 지정한다.includeFilters
⇒ 컴포넌트 스캔 대상을 추가로 지정한다.excludeFilters
⇒ 컴포넌트 스캔에서 제외할 대상을 지정한다.
MemberServiceImpl.java, MemoryMemberRepository.java(컴포넌트 스캔 대상)
@Component
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
...
}
@Component
public class MemoryMemberRepository implements MemberRepository{
...
}
@Component
가 붙은 클래스는 컴포넌트 스캔의 대상이 된다.@Component
가 붙은 클래스는 스프링 빈에 등록되어, 의존 관계를 주입 받을 수 있게 된다.
AutoAppConfig는 별도의 의존관계를 설정하지 않았다. 그럼 어떻게 의존관계를 설정하게 될까?@Autowired
를 통해 의존관계를 자동으로 설정하게 된다.@Autowired
는 내부적으로 같은 타입의 빈을 찾아 주입하게 된다.(getBean()을 생각하면 된다.)
3. Spring Boot에서 빈 등록, 의존관계를 주입하는 과정
CoreApplication.java
- 스프링 부트를 실행시키는 메인 메서드이다.
- 스프링 부트에서는 별도의 설정 파일 없이 컴포넌트 스캔하여 빈에 등록하고 의존 관계를 설정할 수 있을까?
@SpringBootApplication`내부를 살펴보자.
@SpringBootApplication
public class CoreApplication {
public static void main(String[] args) {
SpringApplication.run(CoreApplication.class, args);
}
}
@SpringBootApplication
- @SpringBootApplication 을 자세히 보면 @ComponentScan 이 있는 것을 확인할 수 있다.
- 또한 스프링 부트 프로젝트를 진행하면서 @Component를 쓴 적이 없는데 어떻게 빈에 등록되고 의존 관계를 주입 받을 수 있었던 것일까?
- 바로 @Service와 @Controller, @Repository, @Configuration은 @Component를 가지고 있다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)}
)
public @interface SpringBootApplication {...}
@Controller, @Service, @Repository, @Configuration
- 참고로 어노테이션은 상속관계를 가질 수 없다. 특정 어노테이션을 들고 있는 것을 인식할 수 있는 것은 자바에서 지원하는 것이 아닌, 스프링에서 지원하는 기능이다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {...}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {...}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {...}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {...}
4. 중복 등록과 충돌
컴포넌트 스캔에서 같은 빈 이름을 등록하면 어떻게 되는지 정리해보자.
// 자동 빈 등록
@Component
public class MemoryMemberRepository implements MemberRepository {...}
// 수동 빈 등록
public class AutoAppConfig {
@Bean(name = "memoryMemberRepository")
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
위 코드를 보면 같은 이름의 빈이 등록된다.
- 자동 빈 등록 vs 자동 빈 등록의 경우 ConflictingBeanDefinitionException 예외 발생한다.
- 수동 빈 등록 vs 자동 빈 등록의 경우에는 수동 빈 등록이 우선권을 가져 자동 빈을 오버라이딩 해버린다.
자동 빈 vs 수동 빈 경우 제대로 실행되어야 정상이다. 하지만 런타임 시점에 Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true 오류가 발생했다.
오류가 발생한 원인은 최근 스프링 부트에서 수동 빈 등록 vs 자동 빈 등록에서 충돌이 발생할 경우에 오류가 발생하도록 변경하였다.
yml이나 xml에서 설정을 통해 오류 발생 여부를 바꿔줄 수 있다. 가능한 기본값으로 두자.
5. 마무리
컴포넌트 스캔
@ComponentScan
은 탐색 대상이 되는@Component
가 붙은 모든 클래스를 스프링 빈으로 등록한다.- 빈 이름의 default는 맨 앞글자만 소문자로 사용한다. MemberServiceImpl → memberServiceImpl
- 빈 이름을 직접 지정할 수 있다.
@Component("memberService2")
의존관계 자동 주입
@Autowired
를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.- 같은 타입의 빈을 찾아서 주입하게 된다.
getBean(MemberRepository.class)
로 생각하자. - 참고로 의존 관계를 자동으로 주입하는 방법은 여러 방법이 있다. 나중에 자세하게 정리하도록 하자.
'Programming > Spring' 카테고리의 다른 글
[Spring Boot] Spring Boot + Redis CRUD 예제 (0) | 2023.09.24 |
---|---|
[Spring Boot] Spring Integration MQTT 예제 (0) | 2023.09.24 |
[Spring] Bean 생명주기 콜백 (0) | 2023.09.16 |
[Spring] 의존관계 자동으로 주입하는 방법 (0) | 2023.09.07 |
[Spring] DI, DL, IoC와 ApplicationContext (0) | 2023.09.04 |