Spring/Spring

[Spring][Core] 3-2. Container: Classpath Scanning and Managed Components

noahkim_ 2025. 4. 6. 18:12

1. XML 없이 빈 정의

클래스패스 스캔

  • Spring은 자동으로 후보 클래스를 탐지하고 이를 빈으로 등록할 수 있음
  • XML 구성이 아닌 어노테이션으로 지원함

 

2. Bean Annotation

메타 어노테이션

어노테이션 설명 용도
@Component Spring 관리 빈을 정의하는 일반적인 어노테이션
모든 컴포넌트 클래스
@Repository 영속성 계층을 담당하는 클래스에 사용
예외 자동 변환 지원
데이터 액세스 객체(DAO) 클래스
@Service 서비스 계층을 담당하는 클래스에 사용
서비스 클래스
@Controller 웹 계층(Spring MVC)을 담당하는 클래스에 사용
웹 컨트롤러 클래스

 

복합 어노테이션

어노테이션 사용된 메타 어노테이션 설명
@RestController @Controller, @ResponseBody
REST API 컨트롤러로 사용
HTTP 응답 본문에 데이터를 직접 반환하도록 처리
@SessionScope @Scope(WebApplicationContext.SCOPE_SESSION)
세션 범위에서만 유효한 빈을 생성
기본적으로 proxyMode는 TARGET_CLASS로 설정

 

3. @Bean

  • 사용자 빈 정의 어노테이션
  • @Configuration / @Component 내에서 메서드 정의를 통해 등록할 수 있음

 

예제

더보기
더보기
@Component
public class FactoryMethodComponent {

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    public void doWork() {
        // Component method implementation omitted
    }
}

 

애노테이션

어노테이션 역할 비고
@Bean 메서드가 빈 팩토리 메서드로 작동
메서드가 빈을 정의하는 역할
@Qualifier 특정 빈을 구별하는 역할
같은 타입의 빈이 있을 때 구별을 위한 이름 지정
@Scope 빈의 스코프 정의
Singleton, Prototype 등
@Lazy 지연 초기화
의존성 주입 시 lazy proxy 생성
@Value Spring Expression Language (SpEL) 사용
값 주입에 SpEL 사용 가능

 

특징

주제 설명
InjectionPoint 사용
@Bean 메서드에서 InjectionPoint를 사용하여 빈 생성에 필요한 메타데이터를 접근할 수 있음
Static @Bean 메서드
정적 @Bean 메서드는 CGLIB 프록시를 우회할 수 있음
초기화 단계에서 문제가 발생하지 않음
빈 이름 자동 생성
기본적으로 빈 이름은 클래스 이름을 소문자로 변환한 값으로 자동 생성됨

 

예제) InjectionPoint 

더보기
더보기
@Component
public class FactoryMethodComponent {

    @Bean @Scope("prototype")
    public TestBean prototypeInstance(InjectionPoint injectionPoint) {
        return new TestBean("prototypeInstance for " + injectionPoint.getMember());
    }
}

 

고급 기능

주제 설명
BeanNameGenerator @ComponentScan에서 빈 이름 생성 전략을 커스터마이즈할 수 있음.
기본적으로 AnnotationBeanNameGenerator가 사용됨
이를 커스터마이즈하려면 BeanNameGenerator를 구현해야 함.
ScopeResolver 빈의 스코프를 해결하는 전략을 커스터마이즈할 수 있음.
ScopeMetadataResolver를 구현하여 사용자 정의 스코프 해결 전략을 설정.
ScopedProxy
비싱글톤 스코프의 빈에 대한 프록시를 생성할 때 사용하는 설정.
scopedProxy 속성을 사용하여 targetClass 또는 interfaces로 설정.

 

예제) BeanNameGenerator

더보기
더보기
@Configuration
@ComponentScan(basePackages = "com.example", nameGenerator = CustomBeanNameGenerator.class)
public class AppConfig {
    // 빈 설정
}

class CustomBeanNameGenerator implements BeanNameGenerator {
    @Override
    public String generateBeanName(BeanDefinition definition, AnnotationConfigApplicationContext context) {
        // 커스터마이즈된 빈 이름 생성 로직
        return "custom" + definition.getBeanClassName().toLowerCase();
    }
}

 

예제) ScopeResolver

더보기
더보기
@Configuration
@ComponentScan(basePackages = "com.example", scopeResolver = CustomScopeResolver.class)
public class AppConfig {
    // 빈 설정
}

class CustomScopeResolver implements ScopeMetadataResolver {
    @Override
    public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
        // 사용자 정의 스코프 해결 전략 (예: 프로토타입 빈으로 설정)
        ScopeMetadata metadata = new ScopeMetadata();
        metadata.setScopeName("prototype");
        return metadata;
    }
}

 

 

예제) ScopedProxy

더보기
더보기
@Configuration
@ComponentScan(basePackages = "com.example", scopedProxy = ScopedProxyMode.TARGET_CLASS)
public class AppConfig {
    // 빈 설정
}

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class MyPrototypeBean {
    // 프로토타입 빈 설정
}

 

3. 클래스패스 스캔

@ComponentScan

  • 특정 조건에 맞는 컴포넌트를 자동으로 스캔하여 빈을 등록할 수 있음
  • 반드시 @Configuration과 함께 사용해야 함

 

속성 설명
basePackages 스캔할 기본 패키지 경로 지정 (복수 패키지 지정 가능)
basePackageClasses 스캔할 기본 패키지 경로를 클래스 기준으로 지정
includeFilters 포함할 클래스를 필터링하는 필터 목록 (조건에 맞는 클래스만 스캔)
excludeFilters 제외할 클래스를 필터링하는 필터 목록 (조건에 맞는 클래스 제외)
useDefaultFilters 기본 필터 사용 여부 설정 (기본값 true, 기본 필터를 사용하지 않으려면 false로 설정)
lazyInit 빈 초기화를 지연시킬지 여부 (기본값 false -> 초기화 즉시)
scopeResolver 스코프를 결정할 커스텀 resolver 지정
nameGenerator 빈의 이름 생성기를 지정

 

필터
유형 예시 표현식 설명
annotation org.example.SomeAnnotation
클래스에 특정 어노테이션이 있어야 매칭
assignable org.example.SomeClass
특정 클래스나 인터페이스를 상속하거나 구현한 클래스를 매칭
aspectj org.example..*Service+
AspectJ 표현식을 사용해 매칭
regex org\.example\.Default.*
클래스 이름에 대해 정규 표현식으로 매칭
custom org.example.MyTypeFilter
사용자 정의 필터를 적용해 매칭

 

 

예제

더보기
더보기
@Configuration
@ComponentScan(basePackages = "org.example", 
               includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
               excludeFilters = @Filter(Repository.class))
public class AppConfig  {
	// ...
}

 

고려사항

자동 등록 빈
자동 등록 빈 담당 역할
AutowiredAnnotationBeanPostProcessor @Autowired 의존성 주입 담당
CommonAnnotationBeanPostProcessor @PostConstruct, @Resource 빈 등록 생명주기 후크 수행
  • 비활성화하거나 커스터마이징하려면 annotation-config=false 속성 사용

 

예제

더보기
더보기
public class CustomAutowiredProcessor extends AutowiredAnnotationBeanPostProcessor {
    @Override
    protected boolean determineRequiredStatus(MergedAnnotation<?> ann) {
        // 커스텀 로직 추가
        return super.determineRequiredStatus(ann);
    }
}

 

모듈 경로
  • 클래스패스 스캐닝 기능을 모듈 경로에서 사용할 때 module-info.java 파일에 클래스를 내보내는 설정을 해야 함

 

예제

더보기
더보기
module com.example.module {
    // 모든 모듈에 공개
    exports com.example.service;

    // 특정 모듈에만 공개
    exports com.example.repository to com.example.consumer;

    // 리플렉션 허용
    opens com.example.controller;
}

 


출처