[Spring Boot] 구조 분석 (7) - @Configuration vs @Component 차이
- 개요
안녕하세요. 이번 시간에는 @Configuration과 @Component 차이의 대해 알아보겠습니다.
혹시 @Configuration과 @Component의 대한 개념을 놓치고 오신 분들은 아래 링크를 통해 학습하고 오시는 걸 추천드리겠습니다.
[Spring Boot] 구조 분석(1) - @SpringBootApplication 이란?
- @Configuration과 @Component
깊은 개념은 이전 포스팅에서 확인할 수 있기 때문에 해당 포스팅에서는 간단하고 핵심적인 내용을 설명하겠습니다.
@Configuration 구조
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
/**
* Explicitly specify the name of the Spring bean definition associated with the
* {@code @Configuration} class. If left unspecified (the common case), a bean
* name will be automatically generated.
* <p>The custom name applies only if the {@code @Configuration} class is picked
* up via component scanning or supplied directly to an
* {@link AnnotationConfigApplicationContext}. If the {@code @Configuration} class
* is registered as a traditional XML bean definition, the name/id of the bean
* element will take precedence.
* @return the explicit component name, if any (or empty String otherwise)
* @see AnnotationBeanNameGenerator
*/
@AliasFor(annotation = Component.class)
String value() default "";
/**
* Specify whether {@code @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 {@code @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 {@code final}.
* <p>The default is {@code true}, allowing for 'inter-bean references' via direct
* method calls within the configuration class as well as for external calls to
* this configuration's {@code @Bean} methods, e.g. from another configuration class.
* If this is not needed since each of this particular configuration's {@code @Bean}
* methods is self-contained and designed as a plain factory method for container use,
* switch this flag to {@code false} in order to avoid CGLIB subclass processing.
* <p>Turning off bean method interception effectively processes {@code @Bean}
* methods individually like when declared on non-{@code @Configuration} classes,
* a.k.a. "@Bean Lite Mode" (see {@link Bean @Bean's javadoc}). It is therefore
* behaviorally equivalent to removing the {@code @Configuration} stereotype.
* @since 5.2
*/
boolean proxyBeanMethods() default true;
}
@Ccomponent 구조
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
String value() default "";
}
기본적으로 예전시간에서 알아봤던 것처럼 @Configuration 어노테이션은 @Component를 참조하고 있습니다.
하지만 두 어노테이션에 차이는 boolean proxyBeanMethods() default true; 라는 메서드가에서 차이를 알 수 있습니다.
proxyBeanMethods 메서드 기능을 결론부터 말하면 컨테이너에 등록된 @Bean 간의 참조(Reference)가 강력하게 만들어집니다.
이러한 관계를 "빈 간의 참조(inter-bean references)" 라고 부르며 CGLIB Wrapper에 의해 래핑 되기 때문에 동작이 가능합니다.
반대로 @Component로 @Bean을 등록하게 되면 어떻게 될까요?.
이전시간에 지속적으로 @Bean을 선언할 때는 @Configuration 어노테이션을 선언한 클래스에 사용해야 한다고 설명했습니다.
사실 @Component 클래스에도 @Bean을 선언해서 사용할 수 있습니다. 하지만 우리가 원하는 기대를 도출할 수 없습니다.(만약... 원하는 기대가 싱글톤이 보장되지 않는 상황이라면 도출할 수 있습니다.)
@Component 클래스에 선언된 @Bean 메서드는 "lite mode" 로 처리됩니다.
lite mode의 @Bean 메서드는 Spring Container에 의해 일반 팩토리 메서드로 처리가 되며 참조를 지원하지 않습니다.
무슨 말인지... 글을 보면 이해할 수 없습니다. 실습을 통해 확인해 보겠습니다.
- 실습
실습을 위해 4가지 클래스를 생성하겠습니다.
- Parents
- Child
- BeanInConfiguration
- BeanInComponent
Parents 클래스와 Child 클래스는 빈 클래스로 생성합니다.
package com.ryan.spring_boot_rest_api.config;
public class Parents {
}
package com.ryan.spring_boot_rest_api.config;
public class Child {
}
- BeanInConfiguration 클래스
package com.ryan.spring_boot_rest_api.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Ryan
* @description @Bean Methods in @Configuration Classes
*/
@Configuration
public class BeanInConfiguration {
@Bean
public Parents getParents() {
return new Parents();
}
@Bean
public void SystemOutPrint1(){
Parents parents = getParents();
System.out.println("SystemOutPrint1 parents = " + parents);
}
@Bean
public void SystemOutPrint2(){
Parents parents = getParents();
System.out.println("SystemOutPrint2 parents = " + parents);
}
}
- BeanInComponet 클래스
package com.ryan.spring_boot_rest_api.config;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* @author Ryan
* @description @Bean Lite Mode
*/
@Component
public class BeanInComponent {
@Bean
public Child getChild(){
return new Child();
}
@Bean
public void SystemOutPrint3(){
Child child = getChild();
System.out.println("SystemOutPrint3 child = " + child);
}
@Bean
public void SystemOutPrint4(){
Child child = getChild();
System.out.println("SystemOutPrint4 child = " + child);
}
}
위 실행 결과를 확인해 보면 결과적으로 싱글톤이 보장이 되는 것과 되지 않는 것을 확인할 수 있습니다.
기본적으로 @Configuration으로 @Bean을 사용하면 빌드되는 시점에는 느립니다. 하지만 실제 운영하는 상황에서 많은 요청을 처리할 때 결국 해당 구체 클래스를 생성하지 않아도 되기 때문에 요청이 많을 때는 속도가 빠릅니다.
반대로 @Component로 @Bean을 사용하면 빌드되는 시점에는 빠릅니다. 하지만 실제 운영하는 상황에서는 요청마다 구현체 클래스를 생성하기 때문에 속도가 느릴 수 있습니다.
즉!! 각 목적에 따라 기술을 사용하는게 좋습니다.
참조 문서
소스 코드
https://github.com/Ryan-Sin/Spring_Boot_Rest_API/tree/v5