본문 바로가기

Develop/Springboot

인터페이스 빈주입을 사용하는 이유

Springboot를 사용할 때 대부분, 인터페이스를 이용한 @Service 빈주입을 한다. 하지만 왜 하는지는 모르는채 사용만 했었는데, 백기선님의 유튜브 영상을 보고 깨닫게 되었다.

영상 : https://www.youtube.com/watch?v=C6nsjqrCJq4&feature=youtu.be

서비스 코드

public interface MyService {
    void doSomething();
}
@Service
public class MyServiceImpl implements MyService {
    @Override
    public void doSomething() {
        System.out.println("hello Im Impl Service");
    }
}

이렇게 서비스가 있는경우를 생각해보자. 
Controller 에서는 Service를 두가지 방법으로 주입 받을 수 있다.

1. MyService 를 타입으로 하는 (인터페이스 타입) 빈주입
2. MyServiceImpl 을 타입으로 하는 (클래스 타입) 빈주입

application properties를 이용하여 spring.aop의 proxy-target-class를 false로 설정할 경우엔 클래스를 이용한 @Service 빈 주입을 할 수 없음을 상기하자

spring.aop.proxy-target-class=false
# spring의 기본 설정은 true이다.

 

결론부터 말하면 인터페이스 서비스로 빈 주입을 해야하는 이유는 spring proxy라고 할 수 있다.

spring proxy는 상속을 이용하여 프록시를 생성하는데, class service는 상속을 받아서 프록시를 만드는 과정에서 빈을 만들다가 에러가 난다.

  • 사용자가 service class를 final로 설정해버리거나,
  • 생성자를 private으로 생성하여 자식인 프록시가 부모 생성자를 찾지 못하는 경우

 

1. MyService 를 타입으로 하는 (인터페이스 타입) 빈주입

@Autowired
private MyService myService;

구현 상속 관계 : MyService ---> MyserviceImpl
프록시 상속 관계 : Myservice ---> ProxyMyService 

따라서 ProxyMyService 가 인터페이스인 MyService를 상속받는다.

 

2. MyServiceImpl 을 타입으로 하는 (클래스 타입) 빈주입

@Autowired
private MyServiceImpl myService;

구현 상속 관계 : MyService ---> MyserviceImpl
프록시 상속 관계 : MyServiceImpl ---> ProxyMyService 

이경우에 스프링이 생성하는 프록시가 MyServiceImpl 을 부모로하여 상속을 받는 구조인 것이다.

이런 구조를 가진 상황에서 final 혹은 private 생성자를 이용해서 프록시를 만들지 못하게 되고,
빈 생성시 에러가 난다 (sub classing 에러)