- 지금까지는 자바코드의 @Bean을 통해서 구성 정보에 직접 등록할 스프링 빈을 나열했다.
- 이렇게 등록해야 할 스프링 빈이 수백개가 되면 일일이 등록하기도 귀찮아지고, 구성 정보도 커지게 된다.
- 그래서 스프링은 구성 정보가 없어도 자동으로 스프링 빈을 등록하는 컴포넌트 스캔 기능을 제공한다.
- 또한, 의존관계도 자동으로 주입하는 @Autowired 기능도 제공한다.
@Configuration
@ComponentScan(excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class))
public class AutoAppConfig {
}
- 이제 @Bean 대상이었던 클래스들이 컴포넌트 스캔의 대상이 되도록 @Component를 붙여주면 된다.
- 이전 AppConfig에서는 @Bean으로 직접 구성 정보를 작성했고, 의존관계도 직접 작성했다. 이제는 의존관계 주입도 @Autowired를 통해 자동으로 주입한다.
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
...
}
동작 원리
스프링 빈 등록 → 의존관계 설정
- @ComponentScan은 @Component가 붙은 모든 클래스를 스프링 빈으로 등록한다.
- 이때, 스프링 빈의 기본 이름은 클래스명에서 소문자만 바뀐 것으로 사용한다.
- 생성자에 @Autowired를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.
- 이때, 기본 조회는 스프링 빈 저장소에서 타입이 같은 빈을 찾아서 주입한다.
- getBean(MemberRepository.class)와 동일한 기능을 한다.
탐색 위치
- 모든 자바 클래스를 전부 컴포넌트 스캔하면 오래 걸리기 때문에 꼭 필요한 위치부터 탐색하도록 시작 위치를 지정할 수 있다.
- @ComponentScan(basePackages = "hello.core") 와 같이 사용하면 된다.
- 주로 패키지 위치를 지정하기 보다는, 구성 정보 클래스의 위치를 프로젝트 최상단에 두면 된다. → 여기에선 com.hello 바로 밑에
- 또한, 스프링부트의 대표 시작 정보인 @SpringBootApplication도 프로젝트 최상단에 두는 것이 관례이다.
컴포넌트 스캔 대상
- @Component 외에도 다음 어노테이션들도 컴포넌트 스캔 대상에 포함되며, 스프링은 부가 기능도 수행한다.
- @Controller: 스프링 MVC 컨트롤러로 인식
- @Repository: 스프링 데이터 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환
- @Configuration: 스프링 설정 정보로 인식하고, 스프링 빈이 싱글톤을 유지하도록 추가 처리
- @Service: 개발자들이 핵심 비즈니스 로직 위치를 파악하고 비즈니스 계층을 인식하는데 도움
의존관계 주입
- 의존관계 주입은 크게 생성자 주입, setter 주입, 필드 주입, 일반 메서드 주입으로 4가지 방법이 있다.
- 과거에는 setter 주입, 필드 주입을 많이 사용했지만 최근에는 스프링을 포함한 DI 프레임워크 대부분이 생성자 주입을 권한다.
//setter 주입
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
// 생성자 주입
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
생성자 주입
- 생성자 호출시점에 딱 1번만 호출되는 것이 보장되며, 불변/필수 의존관계에 사용된다.
- 대부분의 의존관계는 애플리케이션 종료시점까지 변경할 일이 없다. (또한 변하면 안된다)
- setter 주입을 사용하면, setXxx 메서드를 public으로 열어두어야 하므로 위험하다.
- 필드에 final 키워드를 사용할 수 있다. (생성자 주입 외 나머지 주입 방식은 모두 생성자 이후에 호출되므로 필드에 final을 사용할 수 없음)
- 항상 생성자 주입을 선택하고, 가끔 옵션이 필요하면 setter 주입을 선택해라.
- 생성자가 딱 1개만 있으면 @Autowired를 생략할 수 있다.
- 여기에 Lombok 라이브러리의 @RequiredArgsConstructor를 사용하면 생성자 코드를 아예 생략할 수 있다.
@Autowired
- @Autowired는 타입으로 조회하기 때문에, ac.getBean(DiscountPolicy.class)와 유사하게 동작한다.
- 그러므로 선택된 빈이 2개 이상일 때 문제가 발생한다. → NoUniqueBeanDefinitionException 발생
- 조회 대상 빈이 2개 이상일 때 해결 방법은 다음 3개가 있다.
- @Autowired 필드명 매칭
- @Qualifier → @Qualifier끼리 매칭 → 빈 이름 매칭
- @Primary 사용
자동 기능 vs 수동 빈 등록
- 어떤 경우에 컴포넌트 스캔과 자동 주입을 사용하고, 어떤 경우에 설정 정보를 통해 수동으로 빈을 등록해야 할까?
- 스프링 빈을 하나 등록할 때 @Component만 넣어주면 끝나는 일을 @Configuration 설정 정보에 가서 @Bean을 적고, 객체를 생성하고, 주입 대상을 일일이 적어주는 과정은 상당히 번거롭다.
- 애플리케이션은 크게 업무 로직과 기술 지원 로직으로 나눌 수 있다.
- 업무 로직 빈: 웹을 지원하는 컨트롤러, 핵심 비즈니스 로직이 있는 서비스, 데이터 계층 로직을 처리하는 리포지토리 등이 대표적이다. 보통 비즈니스 요구사항을 개발할 때 추가/변경된다.
→ 숫자도 매우 많고, 어느정도 유사한 패턴이 있기 때문에 자동 기능을 사용하는 것이 좋다. - 기술 지원 빈: 기술적인 문제나 공통 관심사(AOP)를 처리할 때 주로 사용된다. 데이터베이스 연결이나, 공통 로그 처리처럼 업무 로직을 지원하기 위한 하부/공통 기술이다.
→ 가급적 수동 빈 등록을 사용해서 명확하게 드러내는 것이 좋다. - 애플리케이션에 광범위하게 영향을 미치는 기술 지원 객체는 수동 빈으로 등록해서 설정 정보에 바로 나타나게 하는 것이 유지보수하기에 좋다.
'Spring > 개념' 카테고리의 다른 글
Entity Manager (0) | 2024.01.09 |
---|---|
웹 애플리케이션 이해 (0) | 2023.12.26 |
빈 생명주기 콜백 / 빈 스코프 / 웹 스코프 (0) | 2023.12.25 |
객체지향 설계와 스프링 (0) | 2023.05.09 |