백앤드(Back-End)/Spring Boot

[Spring Boot] 구조 분석 (7) - @Configuration vs @Component 차이

RyanSin 2023. 5. 14. 01:09
반응형

- 개요

안녕하세요. 이번 시간에는 @Configuration과 @Component 차이의 대해 알아보겠습니다.

 

혹시 @Configuration과 @Component의 대한 개념을 놓치고 오신 분들은 아래 링크를 통해 학습하고 오시는 걸 추천드리겠습니다.

 

[Spring Boot] 구조 분석(1) - @SpringBootApplication 이란?

 

[Spring Boot] 구조 분석(1) - @SpringBootApplication 이란?

- 지난 시간 안녕하세요. 지난 시간에는 아주 간단하게 Spring Boot API 서버를 만들어 봤습니다. 처음 Spring Boot를 접하시는 분들은 아래 링크를 통해 학습하고 오시는 걸 추천드리겠습니다. [Spring Boo

any-ting.tistory.com

 

 

- @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가지 클래스를 생성하겠습니다.

 

  1. Parents
  2. Child
  3. BeanInConfiguration
  4. 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://docs.spring.io/spring-framework/docs/3.2.0.M1_to_3.2.0.M2/Spring%20Framework%203.2.0.M2/org/springframework/context/annotation/Bean.html

 

Bean

The optional name of a method to call on the bean instance upon closing the application context, for example a close() method on a JDBC DataSource implementation, or a Hibernate SessionFactory object. The method must have no arguments but may throw any exc

docs.spring.io

 

소스 코드

https://github.com/Ryan-Sin/Spring_Boot_Rest_API/tree/v5

 

GitHub - Ryan-Sin/Spring_Boot_Rest_API: Spring Boot RESTful API 프로젝트

Spring Boot RESTful API 프로젝트. Contribute to Ryan-Sin/Spring_Boot_Rest_API development by creating an account on GitHub.

github.com