앞의 글에서 스프링 Bean이 무엇인지 살펴보았다. 간단히 말하면 스프링이 의존관계 주입을 위해 생성하고 관리하는 객체이다. 그러면 일반적인 의존성 주입과 스프링 빈을 통한 의존성 주입에는 어떠한 차이가 있을까?
이에 대한 답은 스프링은 Bean을 싱글톤으로 관리한다는 것이다. 기존에는 아래와 같이 문제점이 있었다.
그러나 위와 같은 싱글톤 방식은 하나의 커다란 단점을 가지는데 이는 해당 인스턴스는 값을 소유하지 않는 stateless 상태로 만들어져야한다는 것이다.
A라는 요청에서 값이 변하면 그 이후의 요청에서는 모두 변한 값을 사용하게 되므로 인스턴스 내부는 값을 지니지 않게 만들어지거나 변수가 불변인 final로 구성되어야 한다.
→ 즉, 읽기 전용 인스턴스를 만들어야 한다.
스프링에서 의존성을 주입하기 위한 여러가지 방법이 있는데 여기서는 그중에서 일반적으로 사용되는 방법에 대해서만 알아보자.
우리는 일반적으로 컴포넌트 스캔이라고 하는 방식을 사용하여 스프링 빈을 등록한다. 이때 사용하는 애노테이션은 @Service, @Controller, @Repository, @Component 등이 있다.
위의 @Service, @Controller, @Repository 애노테이션은 모두 내부에 @Component를 가지고 있는데 실질적으로 이 애노테이션이 스프링에 의하여 빈으로 등록되게하는 표식이다.
@Component
public class CacheMemoryRepository implements MemberRepository {
//...
}
다음은 이전에 보았던 방식으로 수동 @Bean 애노테이션과 의존성을 주입한 빈을 생성하여 메서드 내부에서 인스턴스를 리턴하는 방식이다. (이때 스프링 빈의 이름은 인스턴스를 반환한 메서드의 이름이다)
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new CacheMemberRepository();
}
}
스프링에서 일반적으로 @Configure나 XML 없이 의존성을 주입받는데 일반적으로는 크게 2가지 방식이 있다.
하나는 컴포넌트 @Autowired 애노테이션을 통해 생성자나 Setter를 통해 인자로 주입받는 방식이고, 다른 하나는 처음부터 변수에 의존성을 물리는 필드주입 방식이다.
@Component
public class OrderServiceImpl implements OrserService {
private final MemberRepository memberRepository;
@Autowired
public OrderSericeImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
//...
}
이러한 경우에는 @Qualifier나 @Primary 방식을 사용한다.
@Component
@Qualifier("mainDiscountPolicy")
public class MainDiscountPolicy implements DiscountPolicy { ... }
@Component
@Qualifier("vipDiscountPolicy")
public class VipDiscountPolicy implements DiscountPolicy { ... }
@Autowired
public OrderServiceImpl(
MemberRepository memberRepository,
@Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy
) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Component
@Primary //적용
public class RateDiscountPolicy implements DiscountPolicy { ... }
@Component
public class FixDiscountPolicy implements DisocuntPolicy { ... }