@ComponentScan
Spring이 특정 패키지 내에서 @Component, @Service, @Repository, @Controller 같은 애노테이션이 붙은 클래스를 자동으로 검색하고, 이를 Bean으로 등록하는 기능이다. 개발자가 Bean을 직접 등록하지 않고도 Spring이 자동으로 관리할 객체를 찾는다.
- ComponentScan의 범위
- basePackages : 특정 패키지를 스캔할 때 사용, 배열로 여러 개를 선언
- ex) @ComponentScan(basePackages = {"com.example", "com.another"})
- basePakageClasses : 특정 클래스가 속한 패키지를 기준으로 스캔할 수 있다.
- ex) @ComponentScan(basePackageClasses = MyApp.class)
- excludeFilters : 스캔에서 제외할 클래스를 필터링할 수 있다.
- ex) @ComponentScan(excludeFilters = @ComponentScan.Filter(SomeClass.class))
- includeFilters : 특정 조건에 맞는 클래스만 스캔하여 포함할 수 있다.
- ex) @ComponentScan(includeFilters = @ComponentScan.Filter(Service.class))
- basePackages : 특정 패키지를 스캔할 때 사용, 배열로 여러 개를 선언
- ComponentScan의 동작 순서
- Spring Application이 실행되면 @ComponentScan이 지정된 패키지를 탐색한다.
- 해당 패키지에서 @Component 또는 애노테이션이 붙은 클래스를 찾는다.
- 찾은 클래스를 Spring 컨테이너에 빈으로 등록한다.
- 등록된 빈은 DI과 같은 방식으로 다른 빈과 연결 된다.
Spring Bean 자동 등록 방법
- @component를 클래스에 위에 작성하면 된다.
Spring Bean 수동 등록 방법
- @Configuration이 있는 클래스를 빈으로 등록하고 해당 클래스를 파싱해서 @Bean이 있는 메서드를 찾아 Bean을 생성한다.
참고로 수동으로 Bean을 등록할 때는 항상 @Configuration과 함께 사용해야 Bean이 싱글톤으로 관리된다.
// 인터페이스
public interface TestService {
void doSomething();
}
// 인터페이스 구현체
public class TestServiceImpl implements TestService {
@Override
public void doSomething() {
System.out.println("Test Service 메서드 호출");
}
}
// 수동으로 빈 등록
@Configuration
public class AppConfig {
// TestService 타입의 Spring Bean 등록
@Bean
public TestService testService() {
// TestServiceImpl을 Bean으로 등록
return new TestServiceImpl();
}
}
// Spring Bean으로 등록이 되었는지 확인
public class MainApp {
public static void main(String[] args) {
// Spring ApplicationContext 생성 및 설정 클래스(AppConfig) 등록
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 등록된 TestService 빈 가져오기
TestService service = context.getBean(TestService.class);
// 빈 메서드 호출
service.doSomething();
}
}
같은 타입의 Bean이 충돌할 때
1. @Qualifier
@Qualifier는 명시적으로 어떤 bean을 사용할지 지정할 수 있도록 도와준다.
- 특정 Bean을 지정해야 할 때
- Bean 이름을 기반으로 명확하게 구분해야 할 때
@Component
@Qualifier("firstService")
public class MyServiceImplV1 implements MyService { ... }
@Component
@Qualifier("secondService")
public class MyServiceImplV2 implements MyService { ... }
@Component
public class ConflictApp {
private final MyService myService;
@Autowired
public ConflictApp(@Qualifier("firstService") MyService myService) {
this.myService = myService;
}
}
2. @Primary
@Primary는 우선 순위를 부여하여 충돌 시 기본으로 선택되도록 설정한다.
- 주로 사용해야 할 Bean이 정해져 있을 때
- 디폴트 Bean을 설정해야 할 때
참고) @Primary와 @Qualifier가 동시에 사용될 경우 @Qualifier 우선순위가 더 높다
@Component
@Primary
public class MyServiceImplV2 implements MyService { ... }
@Component
public class ConflictApp {
private final MyService myService;
@Autowired
public ConflictApp(MyService myService) {
// MyServiceImplV2가 기본으로 주입됨
this.myService = myService;
}
}
3. Custom Annotation
사용자 정의 애노테이션을 만들어 특정 Bean을 구분하는 방법
- 복잡한 프로젝트에서 Bean을 그룹으로 관리하고 싶을 때
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface FirstService {
}
@Component
@FirstService
public class MyServiceImplV1 implements MyService { ... }
@Component
public class ConflictApp {
private final MyService myService;
@Autowired
public ConflictApp(@FirstService MyService myService) {
this.myService = myService;
}
}
그럼 자동 Bean 등록을 사용? 수동 Bean 등록을 사용?
1. 대부분의 경우 자동으로 Bean을 등록
- 자동 등록이 더 간편하고 현대적인 방식이다.
- 예외적으로 충돌 관리가 필요한 경우 @Qualifier나 @Primary를 활용
2. 특별한 요구사항이 있을 경우 수동으로 Bean을 등록
- 특정한 초기화 로직이나 의존성 관계가 복잡한 경우에는 수동 등록이 유리하다.
- 예를 들어, 외부 라이브러리나 커스텀 객체를 Bean으로 등록해야 한다면 Config를 사용하자
'Back-End > Spring' 카테고리의 다른 글
| 스프링 트랜잭션의 이해(feat. 전파) (0) | 2025.01.20 |
|---|---|
| Spring Valid (1) | 2024.12.08 |
| IOC / DI (0) | 2024.12.06 |
| Spring Container와 Spring Bean (0) | 2024.12.06 |
| 객체 지향 설계(SOLID 원칙) (1) | 2024.12.06 |