[Spring] DI를 하는 여러가지 방법

노을·2023년 1월 18일
0

spring

목록 보기
1/2
post-thumbnail
post-custom-banner

spring에서 DI(Dependency Injection)를 하는 방법에는 여러가지가 있다.
각 방법의 장단점을 알아보자!




⭐ 필드 주입

필드 주입은 테스트할 때가 아니면 사용하면 안된다.

@Controller
    public class Controller {
        @Autowired private Service service;
    }
}

☑️ 필드 주입의 장점

1. 코드가 간결해진다. (하지만 장점은 이것 뿐,,)


☑️ 필드 주입의 단점

1. 테스트하기 어렵다.
DB에 접근하는 Repository 객체를 참조하는 Service 클래스가 있다고 하자.

@Component
public class MemberServiceImpl implements MemberService {

    @Autowired private MemberRepository memberRepository;

}

테스트할 때 @Autowired가 동작하지 않으므로 memberRepository를 주입받을 수 없다.
결국 @SpringBootTest를 사용해야만 테스트할 수 있다는 의미이다.

그리고 Repository 객체를 사용해서 DB에 직접 접근하지 않고, Mock 객체를 만들어서 테스트하고 싶은 경우가 생길 수 있다.
하지만 필드 주입은 스프링 컨테이너에 있는 빈을 주입받기 때문에 Mock 객체로 테스트하는 것은 불가능하다.

2. 순환참조를 찾아내기 힘들다.

순환 참조 예시를 살펴보자. CourseServiceStudentService 사이에 순환 참조가 일어나는 코드이다.

@Service
public class CourseServiceImpl implements CourseService {

    @Autowired
    private StudentService studentService;

    @Override
    public void courseMethod() {
        studentService.studentMethod();
    }
}

@Service
public class StudentServiceImpl implements StudentService {

    @Autowired
    private CourseService courseService;

    @Override
    public void studentMethod() {
        courseService.courseMethod();
    }
}

이렇게 서로 계속 참조를 하면 StackOverflowError가 발생한다.
필드 주입을 받으면 객체를 로드 할 시점이 되어서야 순환 참조 에러가 발생한다. (생성자 주입을 쓰면 더 빨리 순환참조를 알아낼 수 있다. 뒤에서 설명...)




⭐ Setter 주입


☑️ Setter 주입의 장점

1. 객체를 중간에 변경하고 싶다면 변경할 수 있다.

만약에 의존성 객체를 이미 주입받은 상태일 때, 중간에 변경하고 싶다면 setter를 호출해서 객체를 다시 주입받을 수 있다.
하지만 객체가 바뀌어야 하는 상황은 거의 없으며 중간에 의도치 않게 객체가 바뀔 가능성이 있기 때문에 이 장점은 단점으로 이어질 수 있다.

2. 선택적으로 객체를 주입 받을 수 있다.

@Component
public class OrderService {
    private MemberRepository memberRepository;

    @Autowired(required = false)
    public void setMemberRepository(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}

setter는 생성자와 달리 호출하고 싶으면 하고, 안하고 싶으면 안하면 된다.
호출을 하면 의존성 객체를 주입 받을 수 있고, 호출 안하면 주입받지 않게 된다.
따라서 선택적으로 의존성 객체 주입을 받고 싶을 때 사용할 수 있다.

spring에서 setter 주입을 사용할 때 의존성 객체를 무조건 주입받지 않아도 된다면 @Autowired(required = false)로 지정해주면 된다.


☑️ Setter 주입의 단점

1. setterpublic으로 열어두기 때문에 누군가 의존성 객체를 바꿀 수도 있다. (불변 보장 X)

2. 의존성 주입 받지 않은 시점에 객체를 사용할 수 있어서 NullPointerException이 발생할 수 있다.

생성자를 이용하면 객체 생성과 동시에 주입을 받는다.
반면에 setter로 주입을 받는 경우,

  1. new로 객체 생성
  2. setter로 의존성 주입

이런 과정이 가능해진다. 이 말은 객체 생성 후 의존성 주입을 받기 전에도 그 객체를 사용할 수 있다는 의미이다.

@Component
public class Controller {
    private Service Service;

    @Autowired
    public void setService(Service service) {
        this.service = service;
    }

    public void hello(){
        service.hello();
    }
}

Service 객체를 setter로 주입 받는 Controller 클래스에서, controller 객체가 만들어지면 (service 객체를 사용하는) hello() 를 사용할 수 있게 된다.

근데 service를 주입 받지 않은 상황에서 hello()를 호출하면 NullPointerException이 발생한다.

3. (필드 주입과 같이) 순환참조를 찾아내기 힘들다.

필드 주입 처럼 객체를 로드 할 시점이 되어서야 순환 참조 에러가 발생한다.


--

⭐ 일반 메서드 주입

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

잘 사용하지 않지만 일반 메서드로 주입받을 수도 있다.
setter 주입과 비슷하지만 한 번에 여러 필드를 주입받을 수 있다.



⭐ 생성자 주입

☑️ 생성자 주입의 장점

@Component
public class OrderService {

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

1. 생성자 호출 시점에 딱 한 번만 호출되고, 그 객체는 변하지 않는다. (불변 보장)
final 키워드를 사용할 수 있다.
객체가 만들어짐 == 의존성 주입 받음 이기 때문에 NullPointerException을 방지할 수 있다.


2. 생성자에는 null 값을 허용하지 않기 때문에 필수 의존관계에 사용하면 좋다.


3. 순환참조를 방지할 수 있다.
생성자 주입을 이용하면 컴포넌트 스캔에 의해 빈이 등록될 때 생성자가 호출되어 그 시점에 의존성 객체 주입을 받게 된다.
그래서 빈 객체 등록 시점에 순환참조를 바로 찾아낼 수 있기 때문에 순환참조 되고있는 있는 지점을 스프링이 바로 알려준다.


4. 테스트하기 쉽다.
직접 객체를 주입할 수 있기 때문에 DI 프레임워크 없이도 테스트 가능하다.






결론 : 생성자 주입을 사용하자 ^0^

post-custom-banner

0개의 댓글