스프링 주입의 다양한 방법들
개요
스프링은 스프링 컨테이너에 빈이 등록된 이후에 빈이 필요한 곳에 자동으로 주입하는 작업까지 해준다. 이 작업이 DI의 작업이고 이렇게 스프링이 모든 제어권을 가지고 가는 것을 IoC라고 하는 것이다.
스프링이 빈을 주입하는 방식은 다양하게 있다.
- 생성자 주입
- 수정자(setter) 주입
- 필드 주입
- 일반 메서드 주입
여러 방식들의 사용법과 장단점을 살펴보자.
(1) 수정자(setter) 주입
setter를 직접 만들어서 주입받는 방식이다.
public class MemberServiceImpl implements MemberService {
private MemberRepository memberRepository;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
@Autowired는 스프링에서 제공해주는 어노테이션으로 선언된 변수에 맞는 빈을 자동으로 주입해준다. 필요하다면 중간에 새로운 repository를 주입해서 사용할 수도 있을 것이다.
하지만 public으로 setter가 만들어지기 때문에 언제든 외부에서 접근할 수 있다는 문제점이 있다. 의존관계 주입은 대부분 한번 일어나면 종료시점까지 변경할 일이 거의 없는 법이다. 그런 불변성에 가까운 코드가 setter로 인해 쉽게 접근할 수 있기 때문에 상당히 위험할 수 있다.
스프링이 실행될 때 주입받아야할 빈이 등록이 안되있다면 에러를 일으킨다. 처음 생성시에 주입받는 것이 아니라 이후에 필요 시 주입받는 방식으로 하고싶다면 @Autowired(required = false)로 설정해주어야 한다.
(2) 필드 주입
public class MemberServiceImpl implements MemberService {
@Autowired
private MemberRepository memberRepository;
}
코드로만 봐도 setter를 생성하는것보다 훨씬 간결하고 외부에서 접근할 수도 없기 때문에 한 때 많이 사용하는 방식이었다.
그런데 이 방식에도 문제점이 있다. 외부에서 전혀 접근할 수 없다는 것이다. 완전하게 스프링이 자동으로 주입하는 방식이기 때문에 테스트코드를 작성할 때 임시로 만든 다른 인스턴스를 사용하고자 한다면 주입할 수 있는 방법이 없다. 한마디로 테스트코드를 작성하기에 불편하다는 것이다.
그리고 DI컨테이너가 없으면 주입을 받을 수 없다는 문제점도 있다.
@Autowired는 애플리케이션의 실제 코드와는 상관없는 테스트코드에서 사용하거나 @Configuration과 같은 스프링 설정을 목적으로 하는 곳에서만 사용해야 한다.
(3) 일반 메서드 주입
public class MemberServiceImpl implements MemberService {
private MemberRepository memberRepository;
@Autowired
public void init(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
일반 메서드에 @Autowired를 설정해서 주입받는 방식인데 거의 사용하지도 않고 사용하지 않는것이 좋다.
(4) 생성자 주입
처음 생성자가 생성될 때 필요한 빈을 받아서 주입하는 방식이다. 가장 보편적으로 사용되고 권유되고 있는 사용방법이다.
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
처음 객체를 생성할 때 주입받을 빈을 선택할 수 있기 때문에 테스트코드에서도 용이하고 final변수로 선언되있기 때문에 혹시라도 실수로 생성자에서 누락 되었을 때 컴파일에러로 잡아주는 아주 좋은 방식이다.
생성자 주입방식 외에 다른 방식들은 모두 생성자 이후에 호출되므로 final을 선언할 수가 없다.
만약 생성자가 1개밖에 없다면 @Autowired를 생략해도 된다.
스프링을 이용해서 빈을 주입받기 위해서는 주입받고자하는 대상도 스프링 컨테이너가 관리하는 스프링 빈이어야 가능하다. 스프링 빈으로 등록되지 않은 일반 클래스에서 @Autowired를 사용해도 스프링이 자동으로 빈을 주입해주지 않는다.
롬복을 이용한 주입방법
생성자를 이용해서 주입받는 방식은 모든 면에서 가장 완벽한 방법이지만 항상 생성자를 만들어야 하기 때문에 코드가 길어진다는 단점이 있다. 이러한 문제점을 해결해 줄 수 있는 방법 중 하나가 롬복을 이용하는 것이다.
롬복은 일반적으로 자주 사용하는 코드들을 어노테이션으로 해결할 수 있도록 만들어준 라이브러리이다.
@Getter, @Setter, @RequiredArgsConstructor등 정말 다양한 어노테이션들이 존재한다.
- @Getter : getter를 자동으로 생성해준다. 클래스레벨에 이 어노테이션을 선언하면 클래스 내부에 getter를 따로 생성하지 않아도 getter를 사용할 수 있다.
- @Setter: setter를 자동으로 생성해준다. 클래스레벨에 이 어노테이션을 선언하면 클래스 내부에 setter를 따로 생성하지 않아도 setter를 사용할 수 있다.
- @RequiredArgsConstructor : 위에서 공부한 생성자를 이용한 주입방식의 코드를 자동으로 생성해준다.
@RequiredArgsConstructor을 사용하는 방법은 다음과 같다.
@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
}
생성자 주입방식에서 사용했던 생성자가 없어지고 어노테이션이 추가되었다. 이렇게 어노테이션만 추가해주면 final이 붙은 변수들은 자동으로 생성자에서 입력받을 수 있도록 코드를 만들어준다.
가장 선호하는 방식인 생성자를 생성하는 방식이고 final도 그대로 사용할 수 있는데 코드도 간결해지기 때문에 이제는 이 방식을 가장 많이 선호하고 있다.
About
해당 게시글은 김영한님의 '스프링 핵심 원리 - 기본편'을 공부하고 작성한 내용입니다.
스프링 핵심 원리 - 기본편 - 인프런 | 강의
스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...
www.inflearn.com
해당 게시글의 소스코드는 https://github.com/ssung0810/spring_study/tree/main/singleton에서 볼 수 있습니다.