ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Boot - DI(Dependency Injection)의 세 가지 방법
    Framework & Library/Spring Boot 2023. 2. 5. 18:28

    DI(Dependency Injection) 세 가지 방법

    Field Injection(필드 주입)
    @Controller
    public class SampleController {
        
        @Autowired
        private SampleService sampleService;
    }

    필드 주입 방식은 의존성 주입을 받을 변수 선언부에 위와 같이 @Autowired 애너테이션을 선언함으로써, 사용이 가능하다.

    ㆍ 코드가 간결하고 편하지만, 의존관계를 정확하게 파악하기 힘들다.

    ㆍ 필드 주입 시 final 키워드를 선언하지 못하기 때문에, 객체가 변할 수 있다는 단점이 존재한다.

    ㆍ 주입이 동시에 일어나는 경우 순환참조 에러를 발생시킨다.

     

    Setter Injection(수정자 주입)
    @Controller
    public class SampleController {
        private SampleService sampleService;
        
        @Autowired
        public setSampleService(SampleService sampleService) {
            this.sampleService = sampleService;
        }
    }

    ㆍ 수정자 주입 방식은 setter() 메서드 또는 사용자가 정의한 메서드를 통해 의존관계를 주입시키는 방식이다.

    ㆍ 수정자 주입 방식의 경우 객체를 변경할 필요성이 있을 때만 사용된다.

     

    Constructor Injection(생성자 주입)
    @Component
    public class SampleController {
        private SampleService sampleService;
        
        @Autowired
        public SampleController(SampleService sampleService) {
            this.sampleService = sampleService;
        }
    }

    ㆍ 생성자 주입 방식의 경우 생성자의 @Autowired 애너테이션을 붙여 의존성을 주입받을 수 있다.

    ㆍ Spring 4.3 이후로는 클래스 내 생성자가 하나이고, 그 생성자로 주입받을 객체가 Bean으로 등록되어 있다면 @Autowired 애너테이션은 생략이 가능하다.

    ㆍ 생성자 주입 방식은 인스턴스 생성 시 1회 호출되는 것이 보장되기 때문에, 주입받은 객체가 변하지 않거나 반드시 객체 주입이 필요한 경우에 사용된다.


    생성자 주입 방식을 권장하는 이유

    1. 객체 불변성 확보

    특정 객체의 생성자는 객체 생성 시 최초 1회만 호출된다. 그렇기 때문에, 주입받은 객체가 불변 객체여야 하거나 객체의 주입이 반드시 필요한 경우에 사용된다.

     

    @Component
    public class SampleController {
        private SampleService sampleService;
        
        @Autowired
        public SampleController(SampleService sampleService) {
            this.sampleService = sampleService;
        }
    }

    위 코드에서 Controller가 사용하는 Service를 변경하는 코드는 Controller의 생성자뿐이다. 즉, 생성자로 한번 의존관계를 주입하면 생성자는 최초 1회 이후 다시 생성될 일이 없기 때문에 불변객체를 보장한다. 또한, Controller가 생성되는 시점에 무조건 Service 객체가 생성되어 주입된다.

     

    2. 테스트 용이

    필드 주입 방식으로 의존관계를 맺을 경우, 순수 자바 코드로 단위 테스트를 실행하는 것이 불가능하다. 단위 테스트를  수행할 때, 각각의 레이어는 단독적으로 실행되기 때문에 의존관계 주입이 null 상태여서 NullPointerException이 발생한다. 반면, 생성자 주입 방식을 사용할 경우 우 각각의 레이어를 단독으로 실행해도 의존 관계 주입이 가능하기 때문에, 단위 테스트 수행이 가능하다.

     

    3. 순환참조 에러 방지
    @Service
    public class ServiceA {
        
        @Autowired
        private ServiceB serviceB;
        
        public void callB() {
            serviceB.action();   // A 클래스가 B의 메서드를 호출
        }
    
    }
    @Service
    public class ServiceB {
        
        @Autowired
        private ServiceB serviceA;
        
        public void callA() {
            serviceA.action();   // B 클래스가 A의 메서드를 호출
        }
    
    }

    순환참조란 위와 같이 A 객체는 B 객체를 참조하고, B 객체는 A 객체를 서로 동시에 참조하고 있을 때 발생한다.

     

    애플리케이션을 실행하고, A 또는 B 객체에서 test() 메서드를 호출하면 서로의 메서드를 계속해서 호출하는 상황이 발생한다. 이러한 경우 StackOverflow를 발생시켜 시스템이 다운된다. 이때 컴파일 시에는 아무런 에러가 없다가 메서드 호출 시에 발생한다는 것이 문제가 된다.

     

    필드 주입 방식과 수정자 주입 방식의 경우 프로그램 실행 중에 Runtime Error가 발생하지만, 생성자 주입 방식의 경우 프로그램 실행 시점에 Complietime Error가 발생한다. 즉, Compiletime Error 발생 시 프로그램 실행 자체가 되지 않기 때문에, 개발자 입장에서는 실제 서비스가 운영되기 전 순환 참조 문제 해결이 가능하다.

     

    이러한 이유들 때문에, 여러 DI 방식들 중 생성자 주입 방식을 권장하고 있다.


    출처

    https://cheershennah.tistory.com/227

     

    728x90

    댓글

Designed by Tistory.