숑숑이의 개발일기
article thumbnail

스프링에서 어노테이션을 활용한 빈 등록 방법은 크게 2가지가 존재한다

  • @Configuration + @Bean
  • @Component (@Controller, @Service, @Repository)

 

오늘은 첫 번째 방법으로 스프링이 어떻게 빈을 등록하고 싱글톤으로 관리하는지 알아본다.

@Configuration + @Bean

 

스프링 컨테이너는 @Configuration이 붙어있는 클래스를 자동으로 빈으로 등록후 해당 클래스를 파싱해서 @Bean이 있는 메서드를 찾아 빈을 생성한다.

// 빈으로 등록할 클래스
public class MyBean {
	public void sayHello() {
    	System.out.println("hi~!");
    }
}
@Configuration
public class Config {

	@Bean
    public MyBean myBean() {
    	return new MyBean();
    }
}

이때 빈의 이름은 @Bean이 붙은 메서드 명이 된다. 그러므로 이름이 중복되지 않도록 주의한다. 또, @Bean 어노테이션은 @Configuration 어노테이션과 함께 사용해야 싱글톤을 보장받을 수 있으므로 반드시 같이 사용해야 한다.

 

 

@Bean을 사용하여 수동으로 등록해야하는 경우는 언제일까?

  1. 개발자가 직접 제어가 불가능한 외부 라이브러리 사용할 때
  2. 애플리케이션 전범위적으로 사용되는 클래스를 등록할 때
  3. 다형성을 활용하여 여러 구현체를 등록해주어야 할 때

 

Singleton을 지키는 매커니즘

그렇다면 스프링은 어떠한 원리로 Singleton을 지킬수 있는 걸까? 아래의 코드를 살펴보자.

@Configuration
public class AppConfig {
	
    @Bean
    public MemberService memberService() {
    	return new MemberServiceImpl(memberRepository());
    }
    
    @Bean
    public OrderService orderService() {
    	return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
    
    @Bean
    public MemberRepository memberRepository() {
    	return new MemoryMemberRepository();
    }
}

위의 코드에서 memberService 빈 생성시 new MemoryRepository()를 호출하고, orderService의 빈에서도 마찬가지로 new MemoryRepository()를 호출한다. new로 생성되므로 서로 다른 인스턴스로 생각될 것이다.

 

그러나 스프링에서는 클래스에 @Configuration 어노테이션이 붙어있는 경우, 바이트코드를 조작하는 라이브러리를 사용하여 싱글톤으로 관리한다. 

 

아래의 코드를 실행하면, 

@Test
	void configurationDeep() {
		ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
		
		AppConfig bean = ac.getBean(AppConfig.class);
		System.out.println("bean.getClass() = " + bean.getClass());
	}

클래스명에 CGLIB가 붙어 출력되는 것을 볼 수 있다.

 

 

CGLIB

바이트 코드를 가지고 프록시 객체를 만들어주는 라이브러리. 런타임 시 자바 클래스를 상속하고 인터페이스를 구현해 동적 프록시 객체를 만든다. 즉, proxyBeanMethods가 true인 상태에서 사용되는 config 빈은 CGLIB 라이브러리에서 생성해준 프록시 객체임을 의미한다. 

 

Configuration의 속성 중 proxyBeanMethods가 빈을 싱글톤으로 관리하는 것과 연관이 있는 속성이다. 디폴트 값으로 true를 가지고 있어 빈에 대한 프록시 객체가 생성된다.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";

    boolean proxyBeanMethods() default true;

    boolean enforceUniqueMethods() default true;
}

 

정리

스프링은 싱글톤 타입인 빈을 만들기 위해 프록시 객체를 생성한다. Configuration 클래스를 그대로 사용하는 것이 아닌, 이를 상속한 프록시 객체를 새로 만들어 관리한다. 수동등록시 반드시 @Configuration 어노테이션과 @Bean 어노테이션을 같이 사용해야 싱글톤을 보장받을 수 있으므로 같이 사용해야 한다. 

 

외부에서 구현한 클래스를 빈으로 등록시에는 @Component 어노테이션을 사용할 수 없다. 이때 @Configuration을 사용하여 메서드 내부에서 해당 클래스를 호출하여 반환하고 빈으로 등록할 수 있다.

 

https://devmoony.tistory.com/192

https://velog.io/@leesomyoung/SpringBoot-빈-등록을-위한-Configuration-Bean-Component

https://tecoble.techcourse.co.kr/post/2023-05-22-configuration

https://velog.io/@max9106/Spring-Configuration을-통한-빈-등록-시-싱글톤-관리

profile

숑숑이의 개발일기

@숑숑-

풀스택 개발자 준비중입니다