study/Spring

[Spring] @Bean vs @Component

eo_neunal 2022. 4. 17. 23:44

0. 들어가며

Spring Container에서 관리하는 자바 객체를 Bean이라고 한다. Bean을 등록하는 방법은 아래 세 가지가 있다.

  1. XML
  2. Annotation
  3. Java Config

2, 3번 방법을 사용할 때 @Component와 @Bean 어노테이션 모두 객체를 Bean으로 등록하는 역할을 수행하는데 두 어노테이션의 차이가 무엇인지 궁금해서 찾아보았다.

1. @Bean

@Bean 어노테이션은 Spring Container에 의해 관리될 Bean을 생성함을 나타낸다.

@Bean 어노테이션을 이해하기 위해 가장 먼저 봐야할 것은 @Target 어노테이션이다. @Target 어노테이션은 해당 어노테이션이 사용될 수 있는 타입을 지정한다. @Bean의 타겟은 메소드와 어노테이션이다. 아직 어노테이션 타겟에 대한 내용은 정리되지 않아 이번 글에서는 메소드에 대해서만 이야기하겠다.

 

@Bean은 개발자가 직접 제어가 불가능한 객체(ex. 외부 라이브러리)들을 Bean으로 등록할 때 사용된다. 외부 라이브러리 같은 경우 개발자는 외부 라이브러리를 얼마든지 사용할 수 있지만 이를 Bean으로 등록하기 위해 클래스를 직접 수정(클래스에 직접 @Component를 사용)할 수 없기 때문에 해당 클래스의 인스턴스를 반환하는 메소드를 만들어 반환된 객체를 Bean으로 등록하는 것이다.

 

그렇다면 개발자가 직접 생성한 클래스에 @Bean을 선언할 수 있을까? 대답은 ‘사용할 수 없다'이다. 위에서 봤듯이 @Bean은 메소드에서만 선언이 가능하기 때문에 개발자가 직접 생성한 클래스에는 선언할 수 없다. 하지만 이를 반환하는 메소드에는 선언이 가능하다.

@Bean 어노테이션은 기본적으로 메소드명을 Bean 이름으로 결정한다. 이는 편리하고 직관적이지만 명시적인 이름 지정이 필요한 경우 name 속성을 사용할 수 있다. 또한 name은 문자열 배열을 허용하므로 단일 Bean에 대해 여러 이름(즉, 기본 Bean 이름과 하나 이상의 별칭)을 허용한다.

 

일반적으로 @Bean 메소드는 @Configuration 클래스 내에 선언된다. @Configuration 내에서 @Bean을 사용해야 Singleton을 보장받을 수 있기 때문이다. 이 부분은 나중에 따로 정리해두면 좋을 것 같다.

 

@Bean 사용 예제

import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
public class JavaConfig {

	@Bean
	public DataSource dataSource() {
		BasicDataSource ds = new BasicDataSource();
		ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
		ds.setUrl("jdbc:mysql://localhost:3306/wsdb?serverTimezone=UTC");
		ds.setUsername("ssafy");
		ds.setPassword("ssafy");
		return ds;
	}
}

2. @Component

@Component 어노테이션은 @Bean과 마찬가지로 Spring Container에서 관리할 Bean을 등록함을 나타내지만, 메소드 레벨이 아닌 클래스 레벨에서 사용된다.

@Component 어노테이션을 들여다보면 Target이 TYPE으로 지정된 것을 알 수 있다. 이는 Class나 Interface 또는 Enum을 타겟으로 삼는다는 의미이다.

 

즉, 개발자가 직접 제어가 가능한 객체를 Bean으로 등록하고자 할 때 사용하는 어노테이션이다. @Component가 선언된 클래스들은 Spring Container가 런타임 시 Component Scan을 통해 자동으로 찾아서 Bean으로 등록한다.

@Bean을 사용해서 모든 Bean들을 수동으로 등록한다면 클래스가 많아질수록 생산력을 저하시키는데, 이를 Component Scan이 보완해준다.

 

@Component의 하위 어노테이션으로 @Controller, @Service, @Repository가 있다. 이는 @Component를 세분화해 표기함으로써 가독성을 높이고, 프로젝트를 구조화할 수 있도록 해준다.

재미있는 사실은 Context Scan을 할 때 @Controller, @Service, @Repository는 스캔되지 않는다. 이들은 @Component가 아니기 때문이다. 각 어노테이션들은 @Component가 선언되어 있기 때문에 해당 어노테이션이 선언된 클래스들은 @Component를 선언한 것처럼 Bean으로 등록된다.

@Configuration을 살펴보면 @Component가 선언되어 있음을 알 수 있다. 따라서 @Configuration 또한 자동으로 Bean으로 등록이 되고, 그래서 @Bean이 있는 메소드를 통해 Bean을 등록할 수 있는 것이다.

3. 정리

@Bean

  • 수동으로 Spring Container에 Bean을 등록하는 방법
  • 개발자가 직접 제어 불가능한 외부 라이브러리를 Bean으로 등록할 때 사용
  • 메소드 레벨에서 선언
  • 1개 이상의 @Bean을 제공하는 클래스의 경우 @Configuration을 선언해주어야 Singleton이 보장됨

@Component

  • 자동으로 Spring Container에 Bean을 등록하는 방법
  • 개발자가 직접 제어 가능한 클래스를 Bean으로 등록할 때 사용
  • 클래스 레벨에서 선언
  • 대체로 @Component를 이용해 자동 등록 방식을 사용하는 것이 좋음
  • @Component 하위 어노테이션으로 @Configuration, @Controller, @Service, @Repository 등이 있음

Reference

Spring Framework 5.3.18 API

@Bean vs @Component