[Spring] 의존관계주입 방법? @Autowired란?

Yujeong·2024년 1월 21일
post-thumbnail

@Autowired

  • 스프링 프레임워크(Spring Framework)에서 제공하는 의존성 주입(DI, Dependency Injection) 기능 중 하나
  • 스프링 컨텍스트(Spring Context)에 등록된 빈(Bean) 객체들 사이에서 의존성을 자동으로 주입하기 위해 사용

@Autowired 사용하지 않을 때

public class MemberService {
    private MemberRepository memberRepository;

    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    public void join(Member member) {
        memberRepository.save(member);
    }

    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }

    public void deleteMember(Long memberId) {
        memberRepository.deleteMember(memberId);
    }

    public void updateMember(Member member) {
        memberRepository.updateMember(member);
    }
}
  1. MemberService 클래스는 MemberRepository에 의존하고 있다.
    → 유연성, 테스트 용이성 감소
  2. 다른 MemberRepository 구현체로 변경하면, MemberService의 코드를 수정해야 한다.
  3. SOLID 원칙 위배

@Autowired 사용할 때

public class MemberService {

	@Autowired
    private MemberRepository memberRepository;

    public void join(Member member) {
        memberRepository.save(member);
    }
}  
  1. @Autowired annotation을 사용하여 스프링 프레임워크는 MemberRepository에 인스턴스를 자동 생성하고 MemberService에 주입해준다.
  2. MemberRepository에 구현체를 변경하더라도 UserService의 코드를 수정할 필요가 없다.
  3. 의존성 주입을 통해 유연성과 테스트 용이성이 향상된다.
  4. 스프링은 의존성을 관리하고 주입하기에, 객체 간 결합도가 낮아지며, 코드를 더 쉽게 테스트하고 확장가능하다.

의존관계 주입 방법

의존관계 주입 방법에는 @Autowired 어노테이션으로 의존관계 주입 방법에는 3가지와 일반 메서드 주입으로 총 4가지가 있다.

1. 생성자 주입(Contructor Injection)

@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;
	}
}

특징

  • 클래스를 인스턴스화할 때, 모든 필수적인 의존성을 주입받는다.
  • 생성자 호출 시점에 딱 1번만 호출된다는 것이 보장된다.
  • 생성자가 1개만 있으면 @Autowired가 생략 가능하다.
  • 객체를 불변 상태로 유지할 수 있다.
  • 코드의 가독성이 높아진다.
  • 테스트 용이성이 높아진다.(mock 객체를 주입하여 단위테스트하기 쉽다.)

장점

  • 필수적인 의존성을 강제로 주입받기에 객체의 일관성 보장이 가능하다.
  • 의존성 변경에 유연하게 대처할 수 있다.

단점

  • 의존성이 많은 경우 생성자의 파라미터 개수가 늘어날 수 있다.
  • 객체 생성시 매번 모든 의존성을 주입해야하기 때문에 코드의 중복이 발생할 수 있다.

2. 수정자 주입(Setter Injection)

@Component
public class OrderServiceImpl implements OrderService {

	private MemberRepository memberRepository;
    private DiscountPolicy discountPolicy;
        
    @Autowired
	public void setMemberRepository(MemberRepository memberRepository) {
            this.memberRepository = memberRepository;
	}
        
	@Autowired
	public void setDiscountPolicy(DiscountPolicy discountPolicy) {
		this.discountPolicy = discountPolicy;
	}
        
}

특징

  • 스프링 컨테이너(Spring Container)가 세터 메서드(Setter Method)를 호출하여 의존성 주입을 수행한다.
  • 생성자 주입과 같이 있으면, 생성자 주입 다음으로 이루어진다.

장점

  • 선택적인 의존성에 유연하게 대처할 수 있다.

단점

  • 외부에서 의존성을 변경하기 어렵다.
  • 세터 메서드가 노출되어 객체의 일관성을 해치는 가능성이 있다.
  • 코드의 가독성이 생성자 주입에 비해 낮아질 수 있다.

3. 필드 주입(Field Injection)

@Component
public class OrderServiceImpl implements OrderService {

	@Autowired
	private MemberRepository memberRepository;

	@Autowired
	private DiscountPolicy discountPolicy;
  }

특징

  • 스프링 컨테이너가 필드에 접근하여 주입한다.
  • 주입받을 필드에 접근 제어자를 private으로 지정할 수 있으므로, 필드의 캡슐화를 유지할 수 있다.

장점

  • 코드가 간결하고 읽기 쉽다.

단점

  • 필드에 직접 접근하므로 외부에서 의존성을 변경하기 어렵다.
  • 주입할 의존성이 선택적이 아닌 경우에도 null이 될 수 있다.
  • 의존성을 명시적으로 표현하지 않기 때문에 코드의 의존성 관계 파악이 어려울 수 있다.

4. 일반 메서드 주입(Method Injection)

@Component
public class OrderServiceImpl implements OrderService {

	private MemberRepository memberRepository;
	private DiscountPolicy discountPolicy;
        
	@Autowired
	public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
		this.memberRepository = memberRepository;
		this.discountPolicy = discountPolicy;
	}
}

특징

  • @Autowired 어노테이션을 부텨 일반 메서드 init을 만들어 의존성을 주입한다.
  • 한번에 여러 필드에 의존성을 주입할 수 있다.

결론: 생성자 주입을 선택하자!

이유
1. 불변: 생성자 주입은 객체 생성할 때 1번만 호출되므로 이후에 호출되는 일이 없다. 따라서 불변하게 설계 가능하다.
2. 누락: 프레임워크없이 자바 코드를 단위 테스트하는 경우에 생성자 주입을 사용하면 주입 데이터를 누락했을 때, 컴파일 오류가 발생한다.
3. final 키워드: 생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있다. 생성자에 값이 설정되지 않는 오류를 컴파일 시점에 막아준다.


참고
2023-CS-Study
스프링 핵심 원리 - 기본편

profile
공부 기록

0개의 댓글