[SpringBoot]Spring 의존성 주입 DI(Dependency Injection) 와 순환 참조

2020. 5. 1. 13:05개발/Spring

DI(Dependency Injection) 은 기본적으로 Bean 끼리만 가능하다.

Spring 의존성 주입 DI(Dependency Injection) 와 순환 참조

Spring 4.3 이상 부터는, 어떠한 Class에 생성자가 1개 뿐이고 해당 생성자로 주입받는 Reference 변수들이 Bean으로 등록되어 있다면 그 Bean을 자동으로 주입한다. (@Autowried 생략 가능)

No qualifying bean Error : Bean 주입에 실패했다는 에러

🤔 DI (Dependency Injection) 이란?

의존성 주입. 생성된 Bean을 객체에 넣어주는걸 의미한다. 보통 @Autowired를 통해 주입하지만 위에서 말한 특성을 이용해 생성자를 통해서도 주입이 가능하다. 만약 사용해야 하는 객체에 Bean이 제대로 주입되지 않았다면 Application은 정상적으로 실행조차 되지 않을것이다.

public class TestController{
    private TestRepository testRepository;

    //@Autowired
    public TestController(TestRepository testRepository){
        this.testRepository = testRepository;
    }

}

@Autowired 를 주석처리해도, 클래스의 생성자가 1개 이고 TestRepositoryBean에 등록이 되어 있기 때문에 TestController는 정상적으로 동작한다.

🤔 순환참조(Circular Dependency)? A가 B를 참조하고, B가 A를 참조하는것.

하지만 위의 방법처럼 생성자로 Bean을 주입받을때 주의해야 할 부분이 있다. 바로 순환참조 라는 부분인데, 만약 TestRepositorySampleRepository가 서로 필요해 각각의 클래스에서 사용해야 한다고 가정해보자. 생성자를 통해 Bean을 주입 받는다고 한다면 아래와 같은 코드가 될 것이다.

@Component
public class TestRepository{
    private SampleRepository sampleRepository;

    //@Autowired
    public TestRepository(SampleRepository sampleRepository){
        this.sampleRepository = SampleRepository
    }
}
@Component
public class ampleRepository{
    private TestRepository testRepository;

    //@Autowired
    public SampleRepository(TestRepository testRepository){
        this.testRepository = testRepository
    }
}

두 클래스 모두 다

  1. 생성자가 1개 뿐
  2. 주입받는 Reference 변수들이 Bean으로 등록

따라서 @Autowired는 생략이 가능하다.

Bean을 주입하는 과정에서 A ➡ B ➡ C 순으로 DI를 하면 상관없지만, 여기서는 A와 B가 서로 참조하는 형태로 구성되어 있으므로 어떤 객체를 먼저 Bean으로 만들어야 하는지 알 수 없게 된다.

따라서 의존성을 주입할때는 순서를 맞춰서 잘 주입해야한다.

🤫 순환 참조 해결방법

DI를 순서에 맞게 잘 하는게 좋지만, 그렇지 않을 경우에는

Field Injection, Setter Injection 을 사용해 주입하는 방법이 있다.

먼저 Filed Injection 이다.

주의사항 : final을 붙이면 안된다.

@Component
public class TestRepository{
    @Autowired private SampleRepository sampleRepository;
}
@Component
public class SampleRepository{
    @Autowired private TestRepository testRepository;
}

필드 자체에서 @Autowired를 사용해 주입하는 방법이다.

다음은 Setter Injection 이다. 특정 클래스 하나를 만들어서 하나의 클래스CircularDependencyTest에서 TestRepositorySampleRepositoryBean을 주입하는 방법이다.

@Component
public class TestRepository{
    public void doSomethingTest(){
        ...
    }
}
@Component
public class SampleRepository{
    public void doSomethingSample(){
        ...
    }
}
public class CircularDpendencyTest{
    private SampleRepository sampleRepository;
    private TestRepository testRepository;

    public CircularDpendencyTest(SampleRepository sampleRepository, TestRepository testRepository){
    this.sampleRepository = sampleRepository;
    this.testRepository = testRepository;
    }
}