가. 스프링 빈이란
•
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, 에이콘