IoC 컨테이너 따라만들기3

pepe·2025년 12월 5일

@Configuration과 @Bean
2편에서 했던 컴포넌트 스캔의 경우

  1. 패키지를 뒤져서 @Component가 붙은 클래스를 찾는다.
  2. BeanDefinition에 등록한다.
  3. reflection을 통해 생성자 호출 후 bean에 등록한다.

라는 과정을 거쳤는데, @Bean은 다른 메커니즘을 갖는다.

  1. @Configuration을 가진 클래스를 인스턴스화 한다.
  2. @Bean 메서드를 찾는다.
  3. 해당 메서드를 reflection을 통해 실제로 호출한다.
  4. 리턴된 객체를 Bean으로 등록한다.

2편에서 했던 @Component방식은 클래스 자체를 bean으로 만드는 반면 @Bean은 메서드가 만들어주는 객체를 bean으로 만드는 것이다. 즉, 개발자가 작성한 메서드가 빈 생성의 주체가 된다.
빈의 생성을 개발자가 직접 제어한다는 것이다.

따라서 구현은 다음과 같이 해야한다.

  1. @Configuration을 가진 클래스(AppConfig)를 먼저 찾는다.
  2. 찾은 클래스 인스턴스를 만든다.
  3. @Bean메서드를 찾는다.
  4. BeanDefinition에 생성자 기반과 메서드 기반을 저장해둔다. @Configuration은 객체만 생성하면 되는 반면 @Bean은 어떤 객체의 어떤 메서드를 호출해서 만들어야 할 지를 알아야 하기 때문이다.
  5. genBean에서 분기하여 생성과정을 나눠야 한다.
/**
 * bean 설계도
 * @Component:  생성자 기반
 * @Bean:       메서드 기반
 */
@Getter
public class BeanDefinition {
    private final Class<?> beanClass;
    // @Bean용
    private final Object factoryInstance;   //@configuration 달린 클래스
    private final Method factoryMethod;     //그 안에@Bean달린 메서드

    public BeanDefinition(Class<?> beanClass) {
        this(beanClass, null, null);
    }

    // @Bean용
    public BeanDefinition(Class<?> beanClass, Object factoryInstance, Method factoryMethod) {
        this.beanClass = beanClass;
        this.factoryInstance = factoryInstance;
        this.factoryMethod = factoryMethod;
    }

    public boolean isFactoryMethod() {
        return factoryMethod != null;
    }
}

구현과정에서 약간의 문제가 있었다. 현재 구현과정에서는 의존성을 이름 기준으로 해결했는데, 그렇게 하면 다음과 같은 코드를 만났을 때 문제가 생긴다.

@Configuration
public class TestJacksonConfig {
    @Bean
    public JavaTimeModule testBaseJavaTimeModule() {
        return new JavaTimeModule();
    }

    @Bean
    public ObjectMapper testBaseObjectMapper(JavaTimeModule testBaseJavaTimeModule) {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(testBaseJavaTimeModule);
        return objectMapper;
    }
}

메서드 이름을 기준으로 했을 경우 위의 코드에서

  1. testBaseJavaTimeModule bean이 만들어진다.
  2. testBaseObjectMapper bean을 만들 때 JavaTimeModule타입이 필요한데, BeanDefinitionMap에는 다음과 같이 들어있다.
    key("testBaseJavaTimeModule") : value(JavaTimeModule객체)
  3. javaTimeModule이 필요한데 없기때문에 bean을 찾지 못하고 예외가 터진다.

처음에는 순서가 문제인 줄 알았는데 testBaseJavaTimeModule에서 testBaseObjectMapper 순으로 돈다고 해도, bean을 javaTimeModule으로 찾기 때문에 못 찾는건 똑같다.

따라서 의존성 해결을 이름 기준에서 타입 기준으로 바꿔야 한다.

profile
pepe

0개의 댓글