땡글이LAB

[Spring] Spring Bean(스프링 빈) 본문

Framework & Library/Spring & SpringBoot

[Spring] Spring Bean(스프링 빈)

땡글이B 2022. 1. 22. 21:13

[Spring Bean]

 Spring IoC Contatiner 에 의해 관리되는 객체를 Bean 이라고 한다. 일반적으로 Java에서 new 연산자를 통해 객체를 얻어낸 것은 Spring Bean이 당연히 아니다. Bean을 우리가 사용하기 위해 가져오려면 ApplicationContext 의 getBean() 메서드를 통해 가져온다. 그럼 어떻게 등록해야 하는 걸까??

 

[Spring Bean 수동 등록]

 어플리케이션의 설정 정보를 담당하고 각 클래스 별 중추 역할을 해줌으로써 SOLID 원칙을 위반하지 않게 해주는 설정 클래스 AppConfig.class 가 있다고 가정해보자.

 해당 AppConfig.class 에 @Configuration 어노테이션을 붙여주고, AppConfig 클래스 안에서 @Bean 어노테이션을 붙여줌으로써 빈으로 등록할 수 있다. 

 만약 @Configuration 을 붙여주지 않고 @Bean 어노테이션을 붙여주게 되면 어떻게 될까?? 원래 Spring IoC 컨테이너의 가장 큰 장점은 Bean으로 등록된 객체들을 싱글톤으로 관리해줌으로써 무분별한 객체 생성을 막아주고 메모리 관리에도 용이하게 해준다는 점이다. (물론 @Scope 어노테이션을 통해서 싱글톤 형태의 스코프를 제외한 프로토타입, 웹 관련 스코프로 관리할 수 있기도 하다.)

 하지만, @Configuration 어노테이션이 없다면 @Bean 어노테이션이 붙은 객체는 스프링 컨테이너에 Bean으로 등록이 되겠지만, 싱글톤을 보장하지 않는다! 

 

이유는 뭘까?

원래 @Configuration 이 붙은 설정 정보 클래스는 스프링이 CGLIB 라는 바이트코드 조작 라이브러리를 사용해서 AppConfig 클래스를 상속받는 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록해주는 것이다. 그리고 CGLIB 라는 바이트코드 조작 라이브러리를 사용해서 새로 만들어진 클래스가 바로 싱글톤을 보장해주는 것이다.

 @Configuration : 설정 파일을 만들기 위한 어노테이션이자 Bean을 등록해주기 위한 어노테이션이다.

 

코드로 확인해보자.

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        System.out.println("call AppConfig.memberService");
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public MemoryMemberRepository memberRepository() {
        System.out.println("call AppConfig.memberRepository");
        return new MemoryMemberRepository();
    }

    @Bean
    public OrderService orderService() {
        System.out.println("call AppConfig.orderService");
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    @Bean
    public DiscountPolicy discountPolicy() {
        //return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }
}
    @Test
    void springBean() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
    }

Configuration을 붙인 경우와 안붙인 경우 어떤 결과가 나오는지 확인해보자.

스프링 컨테이너는 ApplicationContext 인자로 들어온 클래스를 스프링 빈으로 등록하고, 

 

@Configuration 붙어 있는 AppConfig로 테스트한 결과
@Configuration 붙어 있지 않는 AppConfig로 테스트한 결과

 

 @Configuration이 없으니 memberRepository가 3번 호출되면서 싱글톤이 깨지는 것을 확인할 수 있다. 항상 설정 정보들을 다루는 클래스에는 @Configuration 어노테이션을 붙여서 스프링 컨테이너의 장점을 다 활용하면서 SOLID 원칙까지 지킨 유지보수가 쉬운 탄탄한 코드를 작성하자.

 

 

[Spring Bean 자동 등록]

 Bean 으로 등록하는 데에는 다른 방법들이 많이 있다. 위에서 살펴본 코드는 수동으로 개발자가 직접 모든 코드를 작성해야하지만, 컴포넌트 스캔을 통해 자동으로 등록이 가능하기도 하다. 

 간단히 개념만 말하자면, 설정 정보를 다루는 클래스 AppConfig가 있다고 가정하면, @Configuration@ComponentScan 어노테이션을 이용해서 설정정보 클래스를 두고, 위에서 본 AppConfig 안의 코드를 다 생략하고, Bean으로 등록해야할 클래스에 @Component 어노테이션을 이용해서 빈으로 등록해주는 방법이다. 자세한 내용은 컴포넌트 스캔을 따로 다루면서 얘기하겠다.

 

 

 

Reference

http://melonicedlatte.com/2021/07/11/232800.html

 

Comments