Spring Bean(스프링 빈)
스프링 IoC 컨테이너(객체를 생성하고 관리하며 객체끼리의 의존관계를 연결함)가 관리하는 객체들을 Bean이라고 부른다. 스프링 컨테이너에 등록된 객체를 스프링 빈이라 하는 것이다.
본 글에서는 스프링 IoC 컨테이너를 편의상 스프링 컨테이너라고 작성한다.
이때 스프링 컨테이너가 관리하는 객체는 우리가 평상시 new 연산자로 생성하는 객체가 아닌, ApplicationContext.getBean()으로 얻어질 수 있는 객체이다. new 연산자로 생성한 객체는 사용자가 직접 생성한 객체이므로 스프링 컨테이너에서 관리되지 않는다. 스프링 컨테이너에서 관리하는 객체는 ApplicationContext가 생성한 객체이다.
스프링은 스프링 컨테이너에 스프링 빈을 등록할 때 (따로 설정하지 않는 한) 싱글턴으로 등록한다. 즉, 유일하게 하나만 등록해서 공유한다. 따라서 같은 스프링 빈이면 모두 같은 인스턴스이다.
스피링 빈을 등록하는 두 가지 방법
컴포넌트 스캔과 자동 의존관계 설정
@Controller, @Service, @Repository, @Configuration 등의 자바의 어노테이션을 사용하는 것이 컴포넌트 스캔이다.
위 어노테이션 사용 시 스프링 빈으로 자동 등록되는데(컴포넌트 스캔 대상이 된다), 해당 어노테이션에 들어가 보면 @Component를 포함하고 있다. 그래서 컴포넌트 스캔이라고 부른다. 물론 @Component 어노테이션 자체도 컴포넌트 스캔 대상이다.

@Controller 어노테이션을 따라 들어가면 보이는 코드이다. 45번째 줄에 @Component가 명시되어 있는 걸 확인할 수 있다.
아래 Service와 Repository 코드는 컴포넌트 스캔 방식을 사용한 것이다.
MemberService.java
import hello.hellospring.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
// 생략
}
@Service 어노테이션을 사용해서 스프링 빈으로 등록한다.
이때 생성자에 @Autowired 어노테이션을 사용하여 의존 관계 자동 주입이 이루어지게 한다. (생성자 주입 방법) 스프링이 관리하는 MemberRepository를 자동으로 매핑해 준다.
MemoryMemberRepository.java
import hello.hellospring.domain.Member;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@Repository
public class MemoryMemberRepository implements MemberRepository {
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L;
@Override
public Member save(Member member) {
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
return Optional.ofNullable(store.get(id));
}
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member -> member.getName().equals(name))
.findAny();
}
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
}
MemoryRepository 인터페이스를 구현한 클래스이다. 어노테이션은 인터페이스에 붙을 수 없기 때문에 구현체 클래스에 @Repository 어노테이션을 명시해 준다.
자바 코드로 직접 스프링 빈 등록하기
직접 스프링 빈을 등록하기 위해서는 위 예시 코드의 Service 부분 @Service 어노테이션과 생성자 부분 @Autowried 어노테이션을 삭제하고, Repository 부분에서는 @Repository 어노테이션을 삭제해야 한다.
@Autowired 어노테이션을 사용하여 자동 주입을 하는 것은 스프링이 관리하는 객체에서만 동작한다. 직접 스프링 빈으로 등록한 객체는 내가 직접 생성한 객체이므로 스프링이 관리하지 않는다.
SpringConfig라는 java 파일을 생성한다. SpringConfig에서는 Service와 Repository 등을 스프링 빈으로 등록할 수 있다.
Controller는 스프링에서 관리하는 것이기 때문에 직접 스프링 빈으로 등록하지 못하고 컴포넌트 스캔 방식을 사용해야 한다. 따라서 Controller에서 의존성 주입을 하는 Service는 @Autowired 어노테이션을 붙여주어야 한다.
SpringConfig 파일을 아래와 같이 작성한다.
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
스프링은 @Configuration 어노테이션이 명시된 클래스를 우선으로 읽는다. @Configuration은 Bean으로 등록하는 설정 파일임을 알려주는 어노테이션이다. 또한 해당 어노테이션이 붙은 클래스 내에서 생성된 스프링 빈 객체는 싱글톤임을 보장해 준다.
단순히 @Bean 어노테이션만 사용하여 스프링 빈을 등록할 수 있지만, @Configuration 어노테이션과 @Bean 어노테이션을 함께 사용하여야 싱글톤임을 보장해 주는 것이다.
@Bean 어노테이션이 붙은 메서드는 메서드 이름이 곧, 스프링 빈 이름이다. 위 코드에 따르면 memberService와 memberRepository라는 이름으로 스프링 빈이 등록된다.
컴포넌트 스캔 방법이 더 편리하긴 하지만 직접 스프링 빈을 등록하여 관리하는 장점도 있긴 하다.
외부 라이브러리 사용 시 @Bean으로 클래스를 등록해줘야 하는 경우 때문에 사용되기도 하고, SpringConfig 파일에서 한눈에 스프링 빈 객체가 어떤 게 등록되어 있는지 파악하기 쉽다는 장점이 있다.
또한 해당 방법을 사용함으로써 OCP 원칙을 지킬 수 있다.
Service나 Repository 같은 경우는 따로 컴포넌트 스캔 방법을 사용하여 스프링 빈을 등록해도 좋지만, AOP 같은 경우는 앞서 말한 컴포넌트 스캔 방식으로 사용할 수 있는 것과 달리 정형화된 게 아니므로 AOP임을 인지할 수 있어야 한다. 이 때문에 직접 @Bean 어노테이션을 사용해 스프링 빈에 등록하기도 한다.
[인프런] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 (김영한)
Spring Bean(스프링 빈)
스프링 IoC 컨테이너(객체를 생성하고 관리하며 객체끼리의 의존관계를 연결함)가 관리하는 객체들을 Bean이라고 부른다. 스프링 컨테이너에 등록된 객체를 스프링 빈이라 하는 것이다.
본 글에서는 스프링 IoC 컨테이너를 편의상 스프링 컨테이너라고 작성한다.
이때 스프링 컨테이너가 관리하는 객체는 우리가 평상시 new 연산자로 생성하는 객체가 아닌, ApplicationContext.getBean()으로 얻어질 수 있는 객체이다. new 연산자로 생성한 객체는 사용자가 직접 생성한 객체이므로 스프링 컨테이너에서 관리되지 않는다. 스프링 컨테이너에서 관리하는 객체는 ApplicationContext가 생성한 객체이다.
스프링은 스프링 컨테이너에 스프링 빈을 등록할 때 (따로 설정하지 않는 한) 싱글턴으로 등록한다. 즉, 유일하게 하나만 등록해서 공유한다. 따라서 같은 스프링 빈이면 모두 같은 인스턴스이다.
스피링 빈을 등록하는 두 가지 방법
컴포넌트 스캔과 자동 의존관계 설정
@Controller, @Service, @Repository, @Configuration 등의 자바의 어노테이션을 사용하는 것이 컴포넌트 스캔이다.
위 어노테이션 사용 시 스프링 빈으로 자동 등록되는데(컴포넌트 스캔 대상이 된다), 해당 어노테이션에 들어가 보면 @Component를 포함하고 있다. 그래서 컴포넌트 스캔이라고 부른다. 물론 @Component 어노테이션 자체도 컴포넌트 스캔 대상이다.

@Controller 어노테이션을 따라 들어가면 보이는 코드이다. 45번째 줄에 @Component가 명시되어 있는 걸 확인할 수 있다.
아래 Service와 Repository 코드는 컴포넌트 스캔 방식을 사용한 것이다.
MemberService.java
import hello.hellospring.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
// 생략
}
@Service 어노테이션을 사용해서 스프링 빈으로 등록한다.
이때 생성자에 @Autowired 어노테이션을 사용하여 의존 관계 자동 주입이 이루어지게 한다. (생성자 주입 방법) 스프링이 관리하는 MemberRepository를 자동으로 매핑해 준다.
MemoryMemberRepository.java
import hello.hellospring.domain.Member;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@Repository
public class MemoryMemberRepository implements MemberRepository {
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L;
@Override
public Member save(Member member) {
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
return Optional.ofNullable(store.get(id));
}
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member -> member.getName().equals(name))
.findAny();
}
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
}
MemoryRepository 인터페이스를 구현한 클래스이다. 어노테이션은 인터페이스에 붙을 수 없기 때문에 구현체 클래스에 @Repository 어노테이션을 명시해 준다.
자바 코드로 직접 스프링 빈 등록하기
직접 스프링 빈을 등록하기 위해서는 위 예시 코드의 Service 부분 @Service 어노테이션과 생성자 부분 @Autowried 어노테이션을 삭제하고, Repository 부분에서는 @Repository 어노테이션을 삭제해야 한다.
@Autowired 어노테이션을 사용하여 자동 주입을 하는 것은 스프링이 관리하는 객체에서만 동작한다. 직접 스프링 빈으로 등록한 객체는 내가 직접 생성한 객체이므로 스프링이 관리하지 않는다.
SpringConfig라는 java 파일을 생성한다. SpringConfig에서는 Service와 Repository 등을 스프링 빈으로 등록할 수 있다.
Controller는 스프링에서 관리하는 것이기 때문에 직접 스프링 빈으로 등록하지 못하고 컴포넌트 스캔 방식을 사용해야 한다. 따라서 Controller에서 의존성 주입을 하는 Service는 @Autowired 어노테이션을 붙여주어야 한다.
SpringConfig 파일을 아래와 같이 작성한다.
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
스프링은 @Configuration 어노테이션이 명시된 클래스를 우선으로 읽는다. @Configuration은 Bean으로 등록하는 설정 파일임을 알려주는 어노테이션이다. 또한 해당 어노테이션이 붙은 클래스 내에서 생성된 스프링 빈 객체는 싱글톤임을 보장해 준다.
단순히 @Bean 어노테이션만 사용하여 스프링 빈을 등록할 수 있지만, @Configuration 어노테이션과 @Bean 어노테이션을 함께 사용하여야 싱글톤임을 보장해 주는 것이다.
@Bean 어노테이션이 붙은 메서드는 메서드 이름이 곧, 스프링 빈 이름이다. 위 코드에 따르면 memberService와 memberRepository라는 이름으로 스프링 빈이 등록된다.
컴포넌트 스캔 방법이 더 편리하긴 하지만 직접 스프링 빈을 등록하여 관리하는 장점도 있긴 하다.
외부 라이브러리 사용 시 @Bean으로 클래스를 등록해줘야 하는 경우 때문에 사용되기도 하고, SpringConfig 파일에서 한눈에 스프링 빈 객체가 어떤 게 등록되어 있는지 파악하기 쉽다는 장점이 있다.
또한 해당 방법을 사용함으로써 OCP 원칙을 지킬 수 있다.
Service나 Repository 같은 경우는 따로 컴포넌트 스캔 방법을 사용하여 스프링 빈을 등록해도 좋지만, AOP 같은 경우는 앞서 말한 컴포넌트 스캔 방식으로 사용할 수 있는 것과 달리 정형화된 게 아니므로 AOP임을 인지할 수 있어야 한다. 이 때문에 직접 @Bean 어노테이션을 사용해 스프링 빈에 등록하기도 한다.
[인프런] 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 (김영한)