@RequiredArgsConstructor 생성자 주입 - @RequiredArgsConstructor saengseongja ju-ib

Autowired는 그 이름처럼 상횡에 맞게 IoC 컨테이너 안에 존재하는 Bean을 자동으로 주입해준다.

보통은 다음과 같이 사용한다.

@Service
public class FoodService {

    @Autowired
    private FoodDelivery foodDelivery;

}

하지만 요즘의 경향을 보면 스프링팀에서 Autowired의 사용을 권고하지 않는다.

아래와 같이 직접 생성자를 주입하여 사용하거나

@Service
public class FoodService {

    private FoodDelivery foodDelivery;

    public FoodService(FoodDelivery foodDelivery) {
        this.foodDelivery = foodDelivery;
    }
}

lombok에서 제공하는 @RequiredArgsConstructor를 사용하면 된다.

lombok을 프로젝트에 추가해주어야하며 변수를 final로 선언해야한다.

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class FoodService {

    private final FoodDelivery foodDelivery;

}

생성자 주입을 사용하는 경우 아래와 같은 장점이 있다.

① 객체의 변이를 방지한다(상수 final 사용).

② 순환 참조를 방지할 수 있다.

③ 테스트 코드를 작성할 때 용이하다.

④ 코드 악취를 제거할 수 있다.

참고자료

https://galid1.tistory.com/512

취업전 공부할땐 @Autowired를 통해 의존성을 주입했는데,

public class AB {
	@Autowired
	private A a;
}

회사에선 @RequiredArgsConstructor을 이용해 의존성을 주입하더라고요...

@RequiredArgsConstructor
public class AB {
	private final A a;
}
오늘 그 차이점에 대해 기술하고자 합니다.

우선 스프링 프레임 워크에서 의존성을 주입하는 방법엔 3가지가 있습니다.

  • 생성자 주입
  • 필드 주입
  • 수정자 주입

스프링팀에선 생성자 주입이 아니라면 아래의 문구를 보여줍니다.

"스프링 팀에서는 생성자 주입(constructor injection) 방식을 추천하고 있습니다."

Spring Team recommends:
"Always use constructor based dependency injection in your beans.
Always use assertions for mandatory dependencies".

그 이유는 3가지가 있습니다.

  1.  순환 참조 방지
  2.  final 선언이 가능 (런타임에 객체 불변성을 보장한다.)
  3.  테스트 코드 작성 용이 (단순히 원하는 객체를 생성한 후, 생성자에 넣어주면 된다.)

순환 참조 방지

객체의 의존성을 추가하다 보면 순환 참조 문제가 발생할 수 있습니다. A가 B를 참조하고, B가 A를 참조하는데 이러면 서버가 죽어버립니다.

그 이유는 필드 주입과 수정자 주입은 먼저 빈을 생성한 후, 주입하려는 빈을 찾아 주입합니다.

따라서 객체 생성 시점에 빈을 주입하기 때문에 서로 참조하는 객체가 생성되지 않은 상태에서 그 빈을 참조하기 때문에 오류가 발생합니다.

그리고 생성자 주입은 먼저 생성자의 인자에 사용되는 빈을 찾거나 빈 팩토리에서 만듭니다.

즉, 먼저 빈을 생성하지 않고 주입하려는 빈을 먼저 찾습니다.

의존성 주입할때 @Autowired라는 어노테이션을 대게 많이 사용한다.

의존성이란?

class HiWorld {
   private SayHi sayHi;

   public HelloWorld() {
      this.sayHi = new SayHi();
   }

   public startHelloWorld() {
      this.sayHi.hello();
   }
}

HiWorld 클래스에서 hello함수가 호출되기 위해서는 SayHi 클래스가 필요하다.

HiWorld 클래스는 SayHi 클래스의 의존성을 가진다고 말한다.

@Autowired 예시

@Service
public class UseService {

   @Autowired
   private UseDao dao;
   
   @Autowired
   private UseResourceService resource;

@Autowired를 활용한 의존성 주입을 필드 주입이라고 한다.

하지만 이 방식은 인텔리제이나, 스프링 팀에서 권장하지 않는다.

@RequiredArgsConstructor 권장

@Service
@RequiredArgsConstructor
public class UseService {

   private final UseDao dao;
  
   private final UseResourceService resource;

Lombok에서 지원하는 @RequiredArgsConstructor 어노테이션을 사용하여 의존성을 주입 하는 것을 생성자 주입이라고 한다.

스프링 팀은 생성자 주입을 사용할 것을 권장한다.

생성자 주입을 사용할 경우 아래와 같은 장점이 있다.

① 순환 참조 방지

② 테스트 코드 작성 용이

③ 코드 악취 제거

④ 객체 변이 방지 (final 가능)

@RequiredArgsConstructor 생성자 주입 - @RequiredArgsConstructor saengseongja ju-ib

[Spring] 의존관계 자동 주입(Dependency Injection, DI) - 2

생성자 주입을 선택하는 이유

이전 포스팅 [Spring] 의존관계 자동 주입 - 생성자, 수정자, 필드, 메서드 주입, 옵션 처리 (1/4) 에서 생성자 주입의 특징은 의존관계가 불변한다는 특징이 있다고 했었습니다. 과거에는 수정자, 필드 주입을 주로 사용했지만 요즘은 생성자 주입을 사용하는것이 일반적인데 이유는 다음과 같습니다.

  • 대부분의 의존관계는 애플리케이션 종료시까지 변경될 일이 없는것이 좋다.
  • 수정자 주입은 setter 메서드를 public으로 오픈해야 한다.
  • 변경이 가능하다는것은 실수를 유발할수 있고 의존관계는 불변해야 하므로 메서드를 오픈하는것은 지양하는 설계방식이다.
  • 생성자주입은 객체 생성시 한번만 호출되므로 불변하게 설계하는것이 가능하다.
  • 생성자 주입시 final키워드를 사용하므로 실수로인한 컴파일 오류를 파악할수 있다.

<정리>

생성자 주입을 선택하는 가장 큰 이유는 스프링 프레임워크에 의존하지 않고 순수 자바언어의 특징을 살리는 방법이기 때문입니다. 특별한 경우가 아니라면 항상 생성자 주입을 사용하는것이 좋습니다.

생성자 주입 편하게 사용하기 (Lombok 라이브러리)

생성자 주입은 불변하므로 안정성이 있지만 의존관계 주입이 필요한 클래스마다 생성자를 생성해야 하고 final 키워드를 붙여야 하고 @Autowired 애노테이션까지 생성자 메서드에 선언해야 합니다. 이 과정을 좀더 편하게 하는 방법에 대해 알아보겠습니다.

1. 생성자 메서드가 하나일경우 @Autowired 생략

클래스의 생성자가 하나일경우 @Autowired는 생략할수 있습니다.

OrderServiceImpl.java

@Component
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
    ...
    ...
}

2. Lombok 라이브러리를 이용한 @RequiredArgsConstructor

Lombok 라이브러리는 컴파일할때 여러가지 유용한 기능들을 제공하는 라이브러리입니다. @RequiredArgsConstructor 애노테이션은 final 키워드로 생성된 필드로 컴파일시점에 생성자 코드를 만들어 주기때문에 소스에서 일일히 생성자를 만들 필요가 없습니다. 코드상에서는 보이지 않지만 생성자를 호출해보면 존재하는것을 확인할수 있습니다.

OrderServiceImpl.java

@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    public static void main(String[] args) {
        OrderServiceImpl orderService = new OrderServiceImpl(new MemoryMemberRepository(), new RateDiscountPolicy());
    }
	...
	...
}

<정리>

최신 트렌드는 Lombok 라이브러리에서 제공하는 @RequiredArgsConstructor 기능을 사용하여 클래스에 하나의 생성자만 사용하는것이 일반적입니다.

테스트 소스(GitHub)

GitHub - rlatjd1f/spring-demo-study: spring study

spring study. Contribute to rlatjd1f/spring-demo-study development by creating an account on GitHub.

github.com

@RequiredArgsConstructor 생성자 주입 - @RequiredArgsConstructor saengseongja ju-ib

출처 : 인프런 - 우아한 형제들 기술이사 김영한의 스프링 완전 정복 (스프링 핵심원리 - 기본 편)