인터페이스 빈주입을 사용하는 이유 | Why We Use Interface-Based Dependency Injection
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 에러)
When using Spring Boot, most people inject @Service beans through interfaces. But I had been using this pattern without really understanding why — until I watched a YouTube video by Baek Ki-sun and it finally clicked.
Video: https://www.youtube.com/watch?v=C6nsjqrCJq4&feature=youtu.be
Service Code
public interface MyService {
void doSomething();
}
@Service
public class MyServiceImpl implements MyService {
@Override
public void doSomething() {
System.out.println("hello Im Impl Service");
}
}
Let's consider a case where we have a service like this.
In the Controller, you can inject the Service in two ways.
1. Bean injection using MyService as the type (interface type)
2. Bean injection using MyServiceImpl as the type (class type)
Keep in mind that if you set spring.aop's proxy-target-class to false in application properties, you won't be able to do @Service bean injection using the class type.
spring.aop.proxy-target-class=false
# spring의 기본 설정은 true이다.
To cut to the chase, the reason you should inject beans using an interface service is because of spring proxy.
Spring proxy creates proxies using inheritance. With a class service, errors can occur during bean creation while trying to create a proxy by inheriting from the class.
- If the user marks the service class as final, or
- If the constructor is made private, so the child proxy can't find the parent constructor
1. Bean injection using MyService as the type (interface type)
@Autowired
private MyService myService;
Implementation inheritance: MyService ---> MyServiceImpl
Proxy inheritance: MyService ---> ProxyMyService
So ProxyMyService inherits from MyService, the interface.
2. Bean injection using MyServiceImpl as the type (class type)
@Autowired
private MyServiceImpl myService;
Implementation inheritance: MyService ---> MyServiceImpl
Proxy inheritance: MyServiceImpl ---> ProxyMyService
In this case, the proxy that Spring creates has a structure where it inherits from MyServiceImpl as its parent.
In this structure, if you use final or a private constructor, the proxy can't be created,
and you'll get an error during bean creation (a sub-classing error).
댓글
Comments