Search

Spring IoC & Bean

가. 스프링 빈이란

IoC 컨테이너(or DI 컨테이너): 스프링에서 객체의 생성과 객체와의 연결을 책임
→ IoC 컨테이너는 기초적인 역할을 수행하는 Bean Factory, 또는 그 이상의 역할을 수행한다는 점에서 Application Context라고도 불림
→ 일반적으로 Application Context로 불리며, 전체적인 기능을 포괄한다는 점에서 토비도 이 명칭이 적절하다고 봄
→ Bean Factory: Bean(오브젝트)의 생성, 관계설정, 사용, 제거 등 오브젝트에 대한 DI(의존성 역전)식 관리
스프링 빈: IoC 컨테이너에 의해 생성되고 관리되는 객체
→ 특정 객체가 @Component 어노테이션을 가지면 런타임 시점에서 IoC 컨테이너가 해당 객체를 스캔하여 빈으로 등록함
→ 목적에 따라 @Service, @Controller, @Repository의 구체적인 어노테이션을 가짐
스프링 빈의 장점: 객체 간의 결합을 컨테이너에서 책임지므로 각 객체 간의 결합도가 느슨하여 단위테스트 효율이 높다

나. 스프링 빈 생애주기

Spring IoC 컨테이너가 IoC 컨테이너를 생성
Spring IoC 컨테이너는 빈을 등록하는 인터페이스에 따라 생성한 IoC 컨테이너에 스프링 빈을 등록함
@ComponentScan 어노테이션이 있는 클래스가 IoC 컨테이너에 스프링 빈을 등록하는 역할 수행
→ @SpringBootApplication 어노테이션은 내부적으로 @ComponentScan 어노테이션이 붙어 있기 때문에 스프링 빈 등록하는 역할을 수행함
@SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
Java
복사
@ComponentScan 어노테이션이 있는 클래스는 런타임 시 @Component 어노테이션이 있는 클래스를 IoC 컨테이너에 스프링 빈으로 등록함
→ @Service, @Repository, @Controller는 모두 내부적으로 @Component와 함께 구현되어 있음
위의 과정에 따라 스프링 빈으로 등록된 객체는 @Autowired 어노테이션이 붙은 생성자(or setter or field)에서 싱글톤 인스턴스 방식으로 주입됨
→ 생성자에 붙은 @Autowired는 생략 가능
@Controller public class MemberController { private final MemberService memberService; @Autowired public MemberController(MemberService memberService) { this.memberService = memberService; } }
Java
복사

다. 컴포넌트 스캔과 자동 의존관계 설정

스프링에서 자동으로 의존관계 설정
→ MemberService를 사용하는 컨트롤러는 앞으로 많아질 예정
ex) SellingController에서 MemberService::findOne 사용
→ 모든 컨트롤러에 각각의 MemberService에 대한 인스턴스를 생성하는 것은 비용면에서 낭비
→ 스프링 빈에 등록된 객체는 모두 싱글톤(하나의 객체에 대해 하나의 인스턴스를 공유함)으로 관리됨
→ 생성자에 @Autowired를 명시하면(스프링 4.3부터 생략가능) 스프링 빈으로 관리되는 객체의 인스턴스가 생성자를 통해 자동 주입됨
public class MemberController { private final MemberService memberService = new MemberService(); // ... } // to @Controller public class MemberController { private final MemberService memberService; @Autowired public MemberController(MemberService memberService) { this.memberService = memberService; } }
Java
복사
자동 의존관계 설정은 Component scan이라는 수단으로 실행됨
→ 의존관계 있는 객체를 설정하려면 생성자에 Autowired 어노테이션 설정
→ 의존관계가 있는 객체에 대해 Component 어노테이션 설정
→ Component 어노테이션이 붙은 객체는 스프링 빈에 등록됨
→ Service, Controller, Repository 모두 Component 어노테이션이 포함되어 있음
→ 런타임 시점에 스프링 빈에 등록된 객체를 찾아서 의존관계 설정함
@Service public class MemberService { private final MemberRepository memberRepository; @Autowired public MemberService(MemberRepository memberRepository) { this.memberRepository = memberRepository; } }
Java
복사

라. 자바 코드로 직접 스프링 빈 등록

기존에 작성했던 Component 계열의 어노테이션을 제거하고 아래와 같이 Configuration을 작성
@Configuration public class SpringConfig { @Bean public MemberService memberService(){ return new MemberService(memberRepository()); } @Bean public MemberRepository memberRepository(){ return new MemoryMemberRepository(); } }
Java
복사

마. 의존관계 설정 권고사항

의존관계 설정 방법 중 컴포넌트 스캔 방식으로 정형화된 패턴에 사용함 ex) 서비스, 컨트롤러, 레파지토리
정형화된 패턴이 아니거나 상황에 따라 구현 클래스를 변경할 때는 스프링 빈에 직접 등록함
의존성 주입(DI)은 생성자 주입, 필드 주입, setter 주입 세 가지가 있지만 주로 생성자 주입을 권고함. 프로그램 실행 중 의존관계가 중간에 바뀌는 경우는 없음.
→ 필드 주입은 유연성이 떨어짐. 생성자 주입의 경우, 생성자에 매개변수로 전달할 수 있는 의존관계 객체가 다양함
@Controller public class MemberController { @Autowired private final MemberService memberService; }
Java
복사
→ setter 방식은 public 접근이 가능하므로 프로그램 작동 중에 의도치 않게 변경될 가능성이 있음
@Autowired public void setMemberController(MemberService memberService) { this.memberService = memberService; }
Java
복사

Reference

이일민, 토비의 스프링 3, 에이콘