프로젝트 초기 설정 중 BeanNameGenerator를 구현한 로직을 살펴보겠습니다.
Spring 프레임워크는 DI(Dependency Injection) 컨테이너에서 Bean 이름을 생성할 때 기본적으로 AnnotationBeanNameGenerator를 사용합니다. 하지만 특정 조건에 따라 커스텀한 방식으로 Bean 이름을 생성하고 싶다면 BeanNameGenerator 인터페이스를 구현하여 사용할 수 있습니다.
이 글에서는 Spring의 BeanNameGenerator를 구현한 SetBeanNameGenerator를 소개하며, 이를 통해 Bean 이름 생성 과정을 제어하는 방법을 알아보겠습니다.
1. SetBeanNameGenerator의 동작 개요
SetBeanNameGenerator 클래스는 다음과 같은 방식으로 동작합니다.
- 특정 어노테이션을 가진 Bean에 대해 패키지 이름을 포함한 전체 클래스 이름을 Bean 이름으로 사용합니다.
- 해당하지 않는 경우에는 Spring이 제공하는 기본적인 Bean 이름 생성 방식(AnnotationBeanNameGenerator)을 따릅니다.
2. 주요 구현 코드
public class SetBeanNameGenerator implements BeanNameGenerator {
private final AnnotationBeanNameGenerator defaulGenerator = new AnnotationBeanNameGenerator();
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
// definition이 컨트롤러일 경우 패키지 이름을 포함한 Bean 이름을, 아닐 경우 Spring 기본 형식을 따름
final String result;
if (isController(definition)) {
result = generatorFullBeanName((AnnotatedBeanDefinition) definition);
} else {
result = this.defaulGenerator.generateBeanName(definition, registry);
}
return result;
}
private boolean isController(BeanDefinition definition) {
if (definition instanceof AnnotatedBeanDefinition) {
final Set<String> annotationTypes = getAnnotationTypes(definition);
for (final String annotationType : annotationTypes) {
if (annotationType.equals(RestController.class.getName())) return true;
if (annotationType.equals(Service.class.getName())) return true;
if (annotationType.equals(Repository.class.getName())) return true;
if (annotationType.equals(Mapper.class.getName())) return true;
if (annotationType.equals(Component.class.getName())) return true;
}
}
return false;
}
private Set<String> getAnnotationTypes(BeanDefinition definition) {
final AnnotatedBeanDefinition annotatedDef = (AnnotatedBeanDefinition) definition;
final AnnotationMetadata metadata = annotatedDef.getMetadata();
return metadata.getAnnotationTypes();
}
private String generatorFullBeanName(AnnotatedBeanDefinition definition) {
return definition.getMetadata().getClassName();
}
}
3. 코드 설명
3.1 generateBeanName 메서드
BeanNameGenerator 인터페이스의 핵심 메서드입니다.
- 컨트롤러로 판단되는 경우(isController 메서드):
패키지 이름을 포함한 전체 클래스 이름(generatorFullBeanName)을 Bean 이름으로 반환합니다. - 컨트롤러가 아닌 경우:
Spring의 기본 Bean 이름 생성 방식을 그대로 사용합니다.
3.2 isController 메서드
BeanDefinition에 특정 어노테이션이 포함되어 있는지 확인하여 Bean이 컨트롤러(또는 주요 컴포넌트)인지 판단합니다.
- 체크하는 어노테이션:
- @RestController
- @Service
- @Repository
- @Mapper (MyBatis 사용 시)
- @Component
- AnnotatedBeanDefinition 타입의 BeanDefinition만 처리하며, 어노테이션 정보를 메타데이터로 추출하여 비교합니다.
3.3 getAnnotationTypes 메서드
BeanDefinition에서 모든 어노테이션 정보를 가져옵니다. 이 정보를 기반으로 특정 어노테이션이 포함되어 있는지 검사합니다.
3.4 generatorFullBeanName 메서드
BeanDefinition의 메타데이터에서 클래스 이름을 추출하여 Bean 이름으로 반환합니다.
여기서 반환되는 클래스 이름은 패키지 이름을 포함한 전체 경로입니다.
4. 코드 동작 방식 요약
- Bean 등록 시 generateBeanName 메서드가 호출됩니다.
- Bean이 컨트롤러 또는 주요 컴포넌트로 판단되면 패키지 이름이 포함된 전체 클래스 이름을 Bean 이름으로 사용합니다.
- 그렇지 않은 경우, Spring의 기본 방식으로 Bean 이름을 생성합니다.
5. 활용 예시
SetBeanNameGenerator는 Spring 애플리케이션에서 컨트롤러나 주요 컴포넌트의 이름을 명확히 구분하고 싶을 때 유용합니다.
예를 들어, 패키지 이름이 포함된 Bean 이름을 통해 의존성 주입 시 클래스 경로를 명시적으로 확인할 수 있습니다.
@ComponentScan(nameGenerator = MarketBeanNameGenerator.class)
@SpringBootApplication
public class MarketApplication {
public static void main(String[] args) {
SpringApplication.run(MarketApplication.class, args);
}
}
위와 같이 @ComponentScan에 SetBeanNameGenerator를 설정하면, 해당 로직이 모든 Bean 이름 생성에 적용됩니다.
BeanNameGenerator는 빈 이름 충돌 문제를 해결하고, 컨트롤러 빈의 이름 체계를 명확히 하기 위해 설계된 커스텀 구현체입니다. Spring 애플리케이션의 구조가 복잡해질수록 이러한 커스텀 로직이 큰 도움이 될 수 있습니다.
'프로젝트 > 마이그레이션' 카테고리의 다른 글
Jackson 기반 JSON 유틸리티와 커스텀 ObjectMapper 적용하기 (0) | 2025.01.09 |
---|