본문 바로가기
Spring 이해하기

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

by simplify-len 2021. 6. 12.

위 내용은 https://youtu.be/C6nsjqrCJq4 백기선님의 유튜브에서 발췌했습니다.

만약 위와 같은 에러가 발생했다라면?

***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'defaultMyService' could not be injected as a 'com.example.demo.DefaultMyService' because it is a JDK dynamic proxy that implements:
	com.example.demo.MyService


Action:

Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on @EnableAsync and/or @EnableCaching.

 

 

들어가기

의존성 주입시, 인터페이스가 있는 경우, 인터페이스 타입을 쓰라고 권장하고 있습니다. 실제로 유지보수하고 있는 서비스의 클래스 구조를 살펴보면 인터페이스&인터페이스구현체 가 짝꿍처럼 만들어집니다.

 

물론 객체지향을 추구하는 부분도 있습니다. 확장성을 위한 부분도 있습니다. 동영상에서 말하는 더 큰 문제는 따로 있습니다.

 

다시한번, 인터페이스를 쓰지 않을 때 더 큰 문제가 발생할 수 있습니다. 언제 발생할 수 있을까? Spring Boot 에서는 spring.aop.proxy-target-class 해당 속성이 기본으로는 true로 설정되어 있습니다.

 

이 설정의 의미는 무엇일까요?

Aop Proxy 를 생성할 때 클래스를 기반으로 Proxy 를 생성할 것인가?  인터페이스 기반으로 Proxy를 생성할 것인가? 를 의미합니다.

클래스 기반으로 Proxy 를 생성시 cgLib, 인터페이스 기반으로 만들 때는 jdk에 들어있는 다이나믹프록시를 사용합니다.

spring.aop.proxy-target-class=false
spring.aop.proxy-target-class=true

 

Aop 클래스가 필요하지 않는 상황에서는 문제가 없지만,Hibernate 를 사용할 상황에서는 문제가 발생합니다.

해당 속성을 False 로 하고 아래코드을 동작시킨다면, 가장 상위에 있는 에러를 발견할 수 있습니다.

import javax.transaction.Transactional;

public interface MyService {

    @Transactional
    void doSomething();
}
@SpringBootApplication
public class DemoApplication {

    @Autowired
    DefaultMyService myService;

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    ApplicationRunner applicationRunner() {
        return (args) -> {
            myService.doSomething();
        };
    }
}

 

 

DefaultMyService 가 아니라 MyService 로 지정할 경우에는 예외가 발생하지 않습니다.

 

이쯤에서 왜 안되는지 다시한번 생각해봅시다.

> MyService ---> DefaultMyService

> MyService ---> ProxyMyService

똑같은 MySerivce 를 subClass로 사용하지만, DefaultMyService 와 ProxyMySerivce 는 다른 객체입니다. 

 

만약 spring.aop.proxy-target-class 을 True 로 설정한다면 이제 아래와 같은 계층 구조를 가지게 될 것입니다.

> MyService ---> DefaultMyService ----> ProxyMyService

 

그럼 생성자 주입시, 인터페잇 또는 구현체를 넣어도 아무런 문제가 발생하지 않습니다.

댓글