Contents

[Java]해당 클래스의 서비스는 어디서 주입되나요??

Contents

회사 업무중 스프링 서비스 코드 푸시를 하였습니다.

다른 팀원들에게

인터페이스를 파라미터로 정의한것은 확인하였는데, 혹시 해당 컨트롤러에 주입은 어디에서 되나요?`

라는 질문을 들었습니다.

java

@RestController
@Slf4j
@RequiredArgsConstructor
public class YourController {
    private final YourService yourService;
}

위와같이 컨트롤러가 선언되어있었습니다.(물론 예제입니다.)

해당 클래스의 yourServiceYourServiceImpl(인터페이스 구현체)는 어떻게 주입이 되는걸까요?

아직도 스프링의 Application Context에 의한 DI(의존성 주입)가 저는 이해하기 어려운것 같습니다.

인터페이스의 구현체가 Service 어노테이션을 사용해서 Application Context에 의해 관리되는 Bean으로 등록되면,

해당 인터페이스에 자동으로 주입이됩니다 (이 부분은 정말 신기하다고 생각합니다. 제가 PHP 개발자이고, Laravel 프레임워크의 간결함과 모던함을 좋아하는데, 이렇게까지 사용해본적이 없어, 해당 프레임워크 뿐 아니라, 다른 언어나 프레임워크에서도 이와같이 주입이 되는지 확답을 쓸 수 없습니다. )


2019.06.09 추가 :

의존성 주입

의존성 주입(Dependency Injection, DI)은 프로그래밍에서 구성요소간의 의존 관계가 소스코드 내부가 아닌 외부의 설정파일 등을 통해 정의되게 하는 디자인 패턴 중의 하나이다.

DI는 그저 디자인 패턴으로 어디에서든 구현 가능합니다. 예시로 Laravel 5.8 - 자동주입과 같이 다른 프레임워크에서도 스프링처럼 DI가 구현되어 있습니다.


일반적으로 해당 클래스에 구현체를 주입하기 위해서는

java

/*
 * controller/YourController.java
 */
@RestController
@Slf4j
public class YourController {
    private final YourService yourService;
    
    YouController(YourServiceImpl yourService) {
        this.yourService = yourService;
    }
}

---

/*
 * service/YourService.java
 */

public interface YourService {

    void whatYouCall();
}


---

/*
 * service/impl/YourServiceImpl.java
 */

@Service
public class YourServiceImpl implements YourService {
    @Override
    public void whatYouCall() {
        ...
    }
}

위와 같이 구현했을것입니다.

클래스에 YourService라는 Interface를 프로퍼티로 선언한 후, 해당 클래스에 YourSericeImpl을 주입하여 주는것이죠.

일반적으로 제가 생각하는 방식 또한 위와 같으나, Spring Framework는 해당 클래스에 Spring Bean을 주입해줍니다.(????)

@Service 어노테이션을 이용하여 YourServiceImpl 클래스는 YourService의 구현체로 스프링어플리케이션의 빈에 등록이 됩니다. 더불어, 스프링의 DI에 의해 YourService에 주입될 때는 해당 인터페이스의 구현체가 주입이 되게 되죠…..

같은 인터페이스의 구현체가 여러개일 경우는 어떻게 해야할까요?

text

1. 위의 코드와 같이 구현 클래스를 주입 받는 걸로 바꾼다.
2. `@Qualifier` 어노테이션을 이용한다.
3. 스프링 빈의 네임 기반으로 해야한다.

제가 아는 방안은 위와 같은 세가지 방안입니다. (해당 부분을 가르쳐주신 @jhkim님 감사합니다)


위와 같은 개념을 이해하였다면 Lombok을 사용하여 해당 부분을 더 간소화 시킬 수 있습니다.

@RequiredArgsConstructor은 어떻게 동작할까요?

해당 어노테이션 인터페이스의 주석을 보면 이와 같습니다.

java

Generates a constructor with required arguments.
Required arguments are final fields and fields with constraints such as {@code @NonNull}.

위의 주석에 따르면 final로 선언된 필드를 매개변수로 갖는 생성자를 자동 생성하여줍니다.

이전의 실수 - AllArgsConstructor의 잘못된 사용를 통해 배울 수 있던 부분입니다.

@RequiredArgsConstructor 어노테이션과 함께 private final YourService yourService; 이와 같이 선언 할 경우, 해당 코드는 컴파일시 아래와 같이 변환됩니다.

java

@RestController
@Slf4j
public class YourController {
    private final YourService yourService;
    
    YouController(YourService yourService) {
        this.yourService = yourService;
    }    
}

위에서 본 코드와 유사해지는데요

이와 같이 Lombok 어노테이션은 생성자를 직접 만들어주기때문에 간결한 코드를 유지할 수 있습니다.

허나 위의 코드와 다른점은 YourService를 생성자의 파라미터 타입으로 주느냐, YourServiceImpl을 생성자 파라미터 타입으로 주느냐의 차이인데

위에 언급한바와 같이, 스프링 DI는 스프링 빈에 등록된 클래스를 주입합니다.

고로, YourService 타입이지만, YourServiceImpl 클래스가 해당 인터페이스를 구현한 구현체이며, 스프링빈으로 등록되어 있기 때문에, 해당 클래스가 주입이됩니다.


포스팅의 잘못된 부분이 있다면 코멘트 또는 메일로 보내주시면 부족한 부분은 보완하고, 틀린 부분은 수정하겠습니다 아직 프로그래밍에 서툴어서 제가 이해한 개념과 다를 수 있는데, 많은 도움 부탁드리겠습니다.

끝으로 긴 글을 읽어주셔서 감사합니다.