DI(Dependency Injection)
Spring 프레임워크는 3가지 핵심 프로그래밍 모델을 지원하고 있는데, 그 중 하나가 의존성 주입(Dependency Injection, DI) 이다. DI란 외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴으로, 인터페이스를 사이에 둬서 클래스 레벨에서는 의존관계가 고정되지 않도록 하고 런타임 시에 관계를 동적으로 주입하여 유연성을 확보하고 결합도를 낮출 수 있게 해준다.
스프링에서 의존성 주입 방법은 대표적으로 세 가지가 있다.
@Service
public class MemberService {
@Autowired
private MemberRepository memberRepository;
}
필드 주입 방식은 필드 앞에 @Autowired 어노테이션을 명시한다.
필드 주입 방법을 사용하면 주입한 것을 중간에 바꿀 수 있는 방법이 없다. (변경하는 경우가 많지 않긴 하다) 주입되는 변수를 private로 선언하기 때문에 이후 외부 접근도 불가능하다. 따라서 추천되지 않는 방법이다.
@Service
public class MemberService {
private MemberRepository memberRepository;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
setter 함수를 사용하는 방법이다. setter 함수에 @Autowried 어노테이션을 명시한다.
memberRepository는 private으로 선언하고, 이후 setter 함수가 호출되어 memberRepository가 주입된다.
단점은 setter 함수가 public 접근 권한자로 생성되어 있어야 한다는 것이다.
사실상 Repository는 한 번 주입되고 나면 바뀔 일이 없다. 오히려 중간에 바꾸게 된다면 문제가 발생할 수 있다. 바꾼다 해도 로딩 시점에 변경되는 것이지 이미 세팅되고 나면(로딩 후) 바꿀 일이 존재하지 않는다.
그러나 setter 주입 사용 시 setter 함수가 public으로 노출되어 있어야 하니 memberRepository가 변경될 수 있는 환경이 된다. 외부에서도 얼마든지 memberService.setMemberRepository()를 호출할 수 있기 때문이다.
@Service
public class MemberService {
private MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
생성자에서 주입하는 방식으로, 처음 애플리케이션이 조립되는 시점(스프링 컨테이너에 올라가는 등의 세팅이 될 때)에 주입된다. 생성자 앞에 @Autowired 어노테이션을 명시한다.
setter를 사용한 의존성 주입은 세팅 후 개발자가 접근할 수 있다고 했지만, 생성자를 사용하여 의존성 주입 시 세팅 후 주입한 것을 변경하는 게 불가능하다.
의존관계가 프로그램 실행 중에(런타임) 동적으로 변경되는 경우는 아예 없기 때문에 생성자 주입이 권장된다.
IoC(Inversion of Control)
IoC란 Inversion of Control의 약자로 말 그대로 풀면 "제어의 역전" 이라는 의미입니다.
작성한 메서드나 객체의 호출을 개발자가 결정하는 것이 아닌 외부, 즉 스프링 프레임워크에서 이루어지게 되는데 이것을 제어의 역전(IoC)라고 합니다.
이러한 객체의 호출을 스프링 프레임워크에서 결정하게 되면 객체의 생명주기(Lifecycle) 관리를 스프링 프레임워크에서 도맡아서 하기 때문에 개발자는 온전히 비즈니스 로직 작성에 집중할 수 있는 환경을 갖게 됩니다.
객체 호출에 대한 제어권이 프레임워크에 있기 때문에 DI(의존성 주입)이 가능하게 됩니다.
Bean
Spring IoC 컨테이너가 관리하는 자바 객체를 빈(Bean)이라는 용어로 부른다.
우리가 new 연산자로 어떤 객체를 생성했을 때 그 객체는 빈이 아니다.
ApplicationContext.getBean()으로 얻어질 수 있는 객체는 빈이다.
즉 Spring에서의 빈은 ApplicationContext가 알고있는 객체, 즉 ApplicationContext가 만들어서 그 안에 담고있는 객체를 의미한다.
Component Scanning
빈 설정파일에 직접 빈을 등록
@ComponentScan 어노테이션과 @Component 어노테이션을 사용해서 빈을 등록하도록 하는 방법이다.
간단히 말하면 @ComponentScan 어노테이션은 어느 지점부터 컴포넌트를 찾으라고 알려주는 역할을 하고 @Component는 실제로 찾아서 빈으로 등록할 클래스를 의미한다.
위와 같이 @Component 애노테이션을 사용하는 방법 말고도 빈 설정파일에 직접 빈으로 등록할 수 있다.
빈 설정파일은 XML과 자바 설정파일로 작성할 수 있는데 최근 추세는 자바 설정파일을 좀 더 많이 사용한다.
자바 설정파일은 자바 클래스를 생성해서 작성할 수 있으며 일반적으로 xxxxConfiguration와 같이 명명한다.
그리고 클래스에 @Configuration 애노테이션을 붙인다.
그 안에 @Bean 애노테이션을 사용해 직접 빈을 정의한다.