기존에는 스프링 의존성 주입을 위해 xml 파일에 코드를 작성했었다.
궁금하다면 클릭 👇
2024.04.09 - [Web/Spring] - [Spring] 의존성 주입과 제어 역행
이번 글에서는 어노테이션을 이용하여 의존성을 자동으로 주입할 수 있는 방법을 알아보도록 하겠다.
바로 @Autowired 어노테이션을 이용하는 것이다.
1. @Autowired의 특징
- 기존 XML 파일에서 각각의 빈을 DI로 주입했던 기능을 코드에서 애너테이션으로 자동으로 수행함
- @Autowired를 사용하면 별도의 setter나 생성자 없이 속성에 빈을 주입할 수 있음
👉 XML 파일에서 빈 설정 후 주입하는 방법에 비해 훨씬 간단함
2. @Autowired 이용 위치
- 필드를 통한 의존성 자동 주입
public class MemberService {
private final MemberRepository memberRepository;
// field를 통한 의존성 자동 주입
@Autowired
private MemberRepository memberRepository;
public void register(Member member) {
memberRepository.insert(member);
}
}
위 코드는 @Autowired를 field에 적용시켜 의존성을 자동 주입받고 있다.
MemberRepository 타입의 bean 객체를 찾아서 memberRepository 필드에 할당한다.
- 장점: 다른 것보다 코드량이 적다. 가장 간단한 방식이다.
- 단점
- DI 컨테이너에게 지나치게 의존적이라서 단위테스트 시 스프링 설정을 읽고 모든 bean 설정이 되어야만 테스트를 할 수 있다.
- 생성자와 세터 메서드에 비해서 의존 관계를 한 눈에 알아보기가 매우 힘들다.
- 생성자를 통한 의존성 자동 주입
public class MemberService {
private final MemberRepository memberRepository;
// 생성자를 통한 의존성 자동 주입
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
public void register(Member member) {
memberRepository.insert(member);
}
}
이렇게 생성자에 @Autowired 어노테이션을 붙여 의존성 자동 주입을 받을 수 있다.
spring 4.3 버전부터는 오직 하나의 생성자를 가지고 있다면 @ Autowired 생략이 가능하다.
- 세터 메서드를 통한 의존성 자동 주입
public class MemberService {
private final MemberRepository memberRepository;
// setter 메서드를 통한 의존성 자동 주입
@Autowired
public void setmemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
public void register(Member member) {
memberRepository.insert(member);
}
}
위 코드처럼 세터 메서드를 통해 의존하고 있는 객체를 자동 주입받을 수도 있다.
스프링은 해당 메서드를 호출하면서 메서드 파라미터 타입(MemberRepository)에 해당하는 bean 객체를 찾아 인자로 주입한다.
필수 또는 불변 프로퍼티 DI라면 final 제어자를 이용하는 생성자 자동 주입을 사용하고,
선택적이고 가변적인 프로퍼티 DI라면 세터 메소드 자동 주입을 사용하는 것이 좋다.
👉 이런 식으로 정확하게 의존 관계 설정하는 것이 좋음
3. @Autowired의 선택값 처리
@Autowired 어노테이션은 기본적으로 @Autowired 어노테이션을 붙인 타입에 해당하는 빈이 존재하지 않으면 NoSuchBeanDefinitionException이 발생한다.
만약 값이 존재할 경우만 자동 주입을 하고 싶은 경우는 어떻게 해야 할까?
선택적으로 의존성 자동 주입을 하는 방법이 3가지가 있다.
: required=false, Optional, @Nullable이다.
- @Autowired 어노테이션의 required 속성을 false로 지정
@Autowired(required=false)
public void setDateFormatter(DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
이렇게 지정하면 매칭되는 빈이 없어도 예외가 발생하지 않으며 자동 주입을 수행하지 않는다.
즉, Bean이 존재하지 않으면 해당 메서드 자체가 실행(=호출)되지 않는다.
위 예제에서 DateTimeFormatter 타입의 빈이 존재하지 않으면 예외가 발생하지 않고 setDateFormatter() 메서드를 실행하지 않는다.
- 장점
- 간단하고 직관적인 방법이다.
- @Autowired 어노테이션과 함께 사용할 수 있다.
- 단점
- 생성자 주입에서 사용할 수 없다.
- Java8의 Optional 이용
@Autowired
public void setDateFormatter(Optional<DateTimeFormatter> formatterOpt) {
if(formatterOpt.isPresent()) {
this.dateTimeFormatter = dateTimeFormatter;
} else {
this.dateTimeFormatter = null;
}
}
자동 주입 대상 타입이 Optional인 경우, 일치하는 빈이 존재하지 않으면 값이 없는 Optional을 인자로 전달하고(예외가 발생하지 않음), 일치하는 빈이 존재하면 해당 빈을 값으로 갖는 Optional을 인자로 전달한다.
Optional을 사용하는 코드는 값의 존재 여부에 따라 알맞게 의존 객체를 사용하면 된다.
- 장점
- 명시적으로 의존성이 없을 수 있다는 의미를 전달할 수 있다.
- 메서드가 선택적 인자를 받는다는 것을 명확히 할 수 있다.
- 단점
- Java 8 이전 버전에서는 사용할 수 없다.
- @Nullable 어노테이션 사용하기
@Autowired
public void setDateFormatter(@Nullable DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
@Autowired 어노테이션을 붙인 세터 메서드에서 @Nullable 어노테이션을 의존 주입 대상 파라미터에 붙이면, 스프링 컨테이너는 세터 메서드를 호출할 때 자동주입할 빈이 존재하면 해당 빈을 인자로 전달하고, 존재하지 않으면 인자로 null을 전달한다.
- 장점
- Spring의 의존성 주입 시스템과 잘 통합된다.
- 메서드의 매개변수나 변환값에 사용할 수 있어 선택적인 의존성을 명시할 수 있다.
- 단점
- 코드가 Spring 프레임워크에 종속되게 되어 코드의 이식성을 낮출 수 있다.
정리
- 생성자를 이용한 DI 주입 방식은 반드시 Bean을 할당받아야만 동작함
👉 @Autowired(required = false)는 생성자 주입에서 사용할 수 없음 - @Nullable과 Optional은 생성자 주입에서도 사용할 수 있음
👉 생성자 주입에서 특정 필드에 null 값을 허용하고 싶은 경우 사용할 수 있음
- Java 8 이상을 사용하고 있다면 Optional을 사용
- Spring 프레임워크와의 통합을 고려한다면 @Nullable을 사용
- 간단한 선택성 의존성이 필요한 경우에는 required = false를 사용
4. @RequiredArgsConstructor
초기 스프링에서는 @Autowired를 필드에 할당하거나, Setter를 작성하는 방식을 많이 이용해 왔지만, 스프링 3 이후에는 생성자 주입 방식을 더 많이 활용하고 있다.
객체를 생성할 때 문제가 발생하는지를 미리 확인할 수 있기 때문이다.
생성자 주입 방식은 아래 두 가지 규칙으로 작성된다.
- 주입 받아야 하는 객체의 변수는 final로 작성된다.
- 생성자를 이용해서 해당 변수를 생성자의 파라미터로 지정한다.
- 역할
Lombok을 이용하여 생성자 주입을 보다 간단히 작성할 수 있는데,
그것이 바로 @RequiredArgsConstructor이다.
이것은 특별한 처리가 필요한 각 필드마다 하나의 파라미터를 갖는 생성자를 생성해준다.
초기화되지 않은 모든 final 필드와,
선언될 때 초기화되지 않은 @NonNull이 붙은 필드에 대해 생성자를 생성해준다.
새로운 필드를 추가할 때 다시 생성자를 만들어서 관리해야 하는 번거로움을 없애준다.
package org.zerock.springex.sample;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service // 도메인 기능에 부여
@ToString
@RequiredArgsConstructor
public class SampleService {
// lombok의 @RequiredArgsConstructor을 사용해서 '생성자 주입 방식'으로 객체를 주입함
// 반드시 final로 선언해야 함
// 인터페이스를 주입을 하면 자식 클래스인 SampleDAOImpl가 자동으로 주입됨
@Qualifier("normal")
private final SampleDAO sampleDAO;
// 필드 주입 방식 - 예전에 쓰던 방식
/* @Autowired
private SampleDAO sampleDAO;
*/
}
- 단점
: 상속 받은 클래스에서는 적용이 안 된다.
(인터페이스를 구현하는 경우에는 가능)
참고
https://hirlawldo.tistory.com/37
https://namocom.tistory.com/663
https://devraphy.tistory.com/486
https://dreamcoding.tistory.com/83
구멍가게 코딩단, ⌈자바 웹 개발 워크북⌋, 프리렉, p. 236-237
'Web > Spring' 카테고리의 다른 글
[Spring] Spring MVC란? (1) | 2024.05.03 |
---|---|
[Spring] JDBC 프로그래밍을 위한 API와 용어들 (0) | 2024.04.30 |
[Spring] 컴포넌트 스캔 Component Scan (0) | 2024.04.17 |
[Spring] 의존성 주입과 제어 역행 (4) | 2024.04.10 |
[Spring] 스프링 컨테이너 (2) | 2024.04.10 |