[Spring] Spring Bean 등록

이연우·2025년 7월 25일

TIL

목록 보기
46/100

🧭 @ComponentScan이란?

  • Spring이 지정된 패키지를 스캔해서 @Component, @Service, @Repository, @Controller가 붙은 클래스를 자동으로 Bean으로 등록하는 기능

👨‍🍳 비유: 셰프가 자동으로 재료를 찾아 주는 저장고

  • @ComponentScan은 셰프(Spring)가 요리에 필요한 재료(Bean)식료품 저장고(패키지)에서 자동으로 찾아 주는 기능을 의미함

🔍 @ComponentScan의 동작 순서

  1. @ComponentScan이 지정된 패키지부터 탐색 시작
  2. @Component 또는 유사 어노테이션(@Service 등)이 붙은 클래스를 탐색
  3. 해당 클래스를 Spring Bean으로 등록
  4. 등록된 Bean은 DI(의존성 주입) 등에서 사용 가능

🔧 주요 속성

속성설명예시
basePackages스캔할 패키지 지정@ComponentScan(basePackages = {"com.app"})
basePackageClasses특정 클래스가 속한 패키지 기준@ComponentScan(basePackageClasses = App.class)
excludeFilters특정 클래스 제외@ComponentScan(excludeFilters = ...)
includeFilters특정 클래스만 포함@ComponentScan(includeFilters = ...)

🗒️ 예시 코드

@Component
public class MyService {
    public void serve() {
        System.out.println("서비스 실행");
    }
}

@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        MyService service = context.getBean(MyService.class);
        service.serve();
    }
}

🛠️ @Configuration, @Bean

  • 개발자가 직접 Bean을 명시적으로 등록하는 방식
  • 주로 외부 라이브러리 객체 등록, 조건에 따라 다른 구현체 사용 시 유용

1. 자동 Bean 등록(@ComponentScan, @Component)

  • @Component가 있는 클래스의 앞글자만 소문자로 변경하여 Bean 이름으로 등록
// myService 라는 이름의 Spring Bean
@Component
public class MyService {

    public void doSomething() {
        System.out.println("Spring Bean 으로 동작");
    }
    
}

@ComponentScan 을 통해 @Component로 설정된 클래스 탐색

2. 수동 Bean 등록(@Configuration, @Bean)

  • @Configuration이 있는 클래스를 Bean으로 등록하고,
    해당 클래스를 파싱해서 @Bean 이 있는 메서드를 찾아 Bean 생성
  • 이때 해당 메서드의 이름으로 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();
    }
}

❗ 주의: 반드시 @Configuration이 있어야 싱글톤 보장됨

  • 내부적으로 CGLIB Proxy를 사용해 객체 생성을 제어
  • @Bean만 있을 경우 싱글톤 보장이 안 될 수 있음

⚔️ Bean 충돌

  • 수동, 자동 두 가지가 존재하고, Bean은 각각의 이름으로 생성
  • 이때 이름이 같은 Bean이 설정되고자 한다면 충돌 발생

1️⃣ 자동 vs 자동 충돌

@Component("service")
public class V1 implements MyInterface {}

@Component("service")
public class V2 implements MyInterface {}

ConflictingBeanDefinitionException 발생
이름이 같은 Bean이 2개 이상 존재하면 충돌

2️⃣ 수동 vs 자동 충돌

@Component("conflictService") // 자동 등록
public class ConflictService implements MyService {}

@Configuration
public class ConflictConfig {
    @Bean(name = "conflictService") // 수동 등록
    public MyService conflictService() {
        return new ConflictServiceV2();
    }
}

→ 수동 등록된 Bean이 자동 등록을 오버라이딩(덮어씀)
→ 의도적일 수도 있지만, 대부분 실수 → 버그 발생 위험

⚙️ 해결 방법

✅ 설정에서 허용

spring.main.allow-bean-definition-overriding=true

🚫 기본값은 false (충돌 시 오류 발생)


🔄 자동 vs 수동 등록 비교

항목자동 등록수동 등록
방법@Component, @Service, @Repository@Bean, @Configuration
제어 수준낮음 (자동 등록)높음 (직접 제어 가능)
용도대부분의 일반 서비스나 DAO 등외부 라이브러리, 조건부 설정 등
충돌 시Spring Boot에서는 기본적으로 오류수동이 자동을 오버라이드

0개의 댓글