JAVA/JAVA | Spring 학습기록

[Spring] @Configuration vs @Component

kth990303 2022. 11. 24. 14:00
반응형

스프링은 IoC 컨테이너를 통해 적재적소에 빈 객체를 반환해준다.

@Configuration, @Component 어노테이션을 붙여주면 해당 클래스는 스프링 관리 대상이 된다는 점을 보면 크게 차이는 없어보일 수 있다.

어떠한 상황에 @Configuration을 쓰고 어떠한 상황에 @Component를 쓰는 것이 좋을까?


1. @Configuration은 @Component를 포함하고 있다 

둘의 차이점을 비교하기 전에, @Configuration이 @Component를 포함하고 있다는 것을 확인하자.

@Configuration 어노테이션 내부

그렇기 때문에 @Configuration 어노테이션이 붙여진 클래스도 @Component 어노테이션이 붙여진 클래스처럼 스프링 컨테이너 관리 대상이다.

 

 

참고로 @Component는 아래와 같이 소개돼있다.

Indicates that an annotated class is a "component". Such classes are considered as candidates for auto-detection when using annotation-based configuration and classpath scanning.

Since: 2.5
See Also:
Repository, Service, Controller, org.springframework.context.annotation.ClassPathBeanDefinitionScanner 

 

 

ClassPathBeanDefinitionScanner는 아래와 같이 소개돼있다.

A bean definition scanner that detects bean candidates on the classpath, registering corresponding bean definitions with a given registry (BeanFactory or ApplicationContext).

 

 

@Component가 붙은 클래스들을 찾아 BeanDefinition으로 변환 후 스프링 컨테이너 관리 대상으로 등록되는 것이다.

@Configuration는 @Component를 포함하므로 @Configuration으로 등록된 클래스도 마찬가지로 스프링 컨테이너 관리 대상이 된다.


2. @Configuration은 스프링 빈으로 등록할 객체를 싱글톤으로 반환해준다

스프링 컨테이너는 싱글톤 레지스트리로써, 싱글톤 객체를 관리하는 싱글톤 컨테이너 역할을 한다.

웹 요청은 굉장히 많은 요청으로 이루어지기 때문에 싱글톤이 아닌 프로토타입으로 스프링 빈을 반환하게 된다면, 매 요청마다 새 객체를 생성하여 힙 메모리가 터질 수 있다.

 

@Configuration을 사용하면 빈을 반환할 때 싱글톤으로 반환할 수 있다.

 

아래와 같은 코드를 보자.

@Configuration
public class MemberConfig1 {

    @Bean
    public MemberServiceConfig hello() {
        return new MemberServiceConfig();
    }
}
@Component
public class MemberConfig2 {

    @Bean
    public MemberServiceConfig hello() {
        return new MemberServiceConfig();
    }
}

MemberConfig1에는 @Configuration을, MemberConfig2에는 @Component를 붙여주었다.

@Component 클래스와 달리 @Configuration이 붙은 클래스는 CGLIB 바이트 조작 라이브러리를 통해 CGLIB Proxy 객체가 생성된 것을 확인할 수 있다. 바로 이 CGLIB Proxy 객체가 스프링 빈을 반환할 때 싱글톤이 보장되게 해주는 것이다.

 

 

@Configuration 어노테이션을 타고 들어가면 boolean proxyBeanMethods() default true; 메서드가 있다.

이 메서드의 설명에 아래와 같이 되어있다.

Specify whether @Bean methods should get proxied in order to enforce bean lifecycle behavior,
e.g. to return shared singleton bean instances even in case of direct @Bean method calls in user code. This feature requires method interception, implemented through a runtime-generated CGLIB subclass which comes with limitations such as the configuration class and its methods not being allowed to declare final.

The default is true, allowing for 'inter-bean references' via direct method calls within the configuration class as well as for external calls to this configuration's @Bean methods, e.g. from another configuration class.
If this is not needed since each of this particular configuration's @Bean methods is self-contained and designed as a plain factory method for container use, switch this flag to false in order to avoid CGLIB subclass processing. Turning off bean method interception effectively processes @Bean methods individually like when declared on non-@Configuration classes, a.k.a. "@Bean Lite Mode" (see @Bean's javadoc). It is therefore behaviorally equivalent to removing the @Configuration stereotype.

 

요약하자면 @Configuration을 사용하면 @Bean 메서드들 역시 CGLIB 바이트코드 조작 라이브러리를 통해 싱글톤으로 반환하게 해준다는 것이다. 해당 옵션을 사용하지 않도록 설정할 수 있지만, 이는 @Configuration 성격을 죽이는 것과 다름없다. 라고 되어있다.

 

 

CGLIB 내부 바이트코드를 뜯어보는 방법을 찾기 힘들어 직접 뜯어보진 못했지만, 예상 코드는 아래와 같다고 한다.

출처: 김영한님의 스프링 핵심원리 기본편

 

new MemberServiceConfig()를 반환하는 메서드를 @Bean으로 등록했다고 하더라도,

@Configuration이 아닌 @Component를 사용한다면 Java의 new 키워드때문에 매번 힙 인스턴스로 생성되어 메모리에 부하가 가게 된다. 외부 라이브러리 또는 설정 정보 클래스를 반환하는 클래스일 경우 @Configuration를 꼭 붙여주도록 하자.

 

+) 22.11.28. 추가

헷갈릴까봐 요약본을 추가한다.

우테코 크루 `알파`와의 대화

요약 내용이므로 참고하자.


3. @Configuration 사용 예제

대표적인 예로 queryDSL을 사용할 때 JPAQueryFactory를 스프링 컨테이너 관리 대상으로 등록하는 코드에서 볼 수 있다.

MessageRepository 코드 일부

queryDSL을 사용하기 위해 new JPAQueryFactory를 해주고 EntityManager를 DI해주는 모습이다.

위 코드만 봐서는 new 키워드가 한번만 쓰이므로 크게 문제없을 것 같지만, 메시지 도메인 뿐만 아니라 회원 도메인, 모임 도메인, 롤링페이퍼 도메인 등등. 다양한 도메인의 repository에서 new JPAQueryFactory() 코드를 작성해주어야 한다. 정작 JPAQueryFactory가 도메인마다 다 다른 JPAQueryFactory일 필요도 없는데 말이다.

 

하지만 만약 위와 같이 별도의 Config 클래스를 생성하여 @Configuration으로 등록해주고 init 메서드에 @Bean으로 설정해줬다면?

JPAQueryFactory는 스프링 컨테이너 관리 대상이자, CGLIB 바이트코드 조작을 통해 JPAQueryFactory는 싱글톤으로 반환될 것이다.


@Controller, @Service, @Repository, @Component들은 우리 비즈니스 로직에 직접적인 영향을 주는, 우리가 제어할 클래스들에 붙여주고, @Configuration를 통한 수동 빈 등록은 설정 정보 또는 외부 라이브러리를 사용할 때 이용하면 좋아보인다.

 

 

해당 내용에 틀린 부분이 있거나 질문이 있다면 댓글로 남겨주세요.

감사합니다 :)

반응형