[Spring] IoC & DI

IoC (Inversion of Control) 

제어의 역전이라는 뜻으로 기존의 자바에서는 제어권이 개발자에게 있었지만, 스프링에서는 제어권이 스프링에게 있다는 뜻이다.

 

제어권이 스프링에게 있다는 것이 무슨 뜻인가?

사용자가 직접 new를 사용해서 인스턴스를 생성하는 것이 아니라 스프링이 알아서 인스턴스를 생성해준다는 뜻이다.

 

어떻게 스프링이 인스턴스를 생성할 수 있지?

스프링에는 IoC Container라는 것이 있다.

IoC Container를 이용해서 Bean을 관리하고 DI를 수행할 수 있다.

 

IoC Container (= Spring container)란?

Spring container는 ApplicationContext 인터페이스의 구현체이다.

ApplicationContext는 최상위 인터페이스인 BeanFactory와 다른 인터페이스들을 다중 상속 받는다.

 

BeanFactory :스프링 빈을 관리하고 조회하는 역할 담당.

MessageSource : 다국어 메시지 처리 기능을 제공한다.
EnvironmentCapable: 프로파일 기능과 프로퍼티 기능을 제공한다.

           프로파일 기능: 다양한 배포 시나리오 또는 런타임 조건에 맞게 동작 및 구성을 사용자 지정하는 기능.

           프로퍼티 기능: Environment 객체를 통해 application.yml에서 작성한 속성값을 가져와서 사용할 수 있다.  

ApplicationEventPublisher : 이벤트 기반의 프로그래밍을 할 때 필요한 기능을 제공 (ex. 인터셉터)
ResourceLoader : 파일, 경로, 클래스 경로 등의 리소스를 읽어오는 기능을 제공.


DI(Dependency Injection)

"A가 B를 의존한다"라는 말은 "A는 B가 있어야만 사용할 수 있다"는 말이다.

DI는 의존성을 내부에서 생성하는 것이 아닌 외부에서 추가해주는 것이다.

DI를 사용하면 아래와 같은 장점들이 있다.

  • 느슨한 결합: 의존성 주입을 인터페이스 기반으로 설계하면 코드가 유연해진다.
  • 변경에 유연해짐: 결합도가 낮은 객체끼리는 쉽게 변경할 수 있다.
  • 테스트에 용이: DI를 이용한 객체는 자신이 의존하고 있는 인터페이스가 어떤 클래스로 구현되어 있는지 몰라도 된다. 따라서 테스트하기 더 쉬워진다. 

 

기존의 의존성 생성 방식

public class Controller{
	private Service service;

	public Controller(){
    	Service service = new Service();
    }
}

 

DI 방식

public class Controller{
	private Service service;

	public Controller(Service service){
    	this.service = Service;
    }
}

 

DI가 가능한 이유는?

DI는 Bean으로 등록되어 스프링컨테이너에서 관리하는 객체로만 가능하다.

Bean은 스프링컨테이너에서 관리하는 인스턴스이며, 스프링 실행 당시에 컴포넌트 스캔을 통해 싱글톤으로 등록된다.

싱글톤으로 저장되어 있기 때문에 Bean 인스턴스를 가져올 수 있으며, IoC 컨테이너의 다양한 기능을 이용해서 DI가 가능하다.

 

 

3가지 DI 방법

1. 필드 주입

@Autowired
private Repository repository;
  • 사용법이 간단하다.
  • 생성자 주입에 비해 안정성이 떨어진다.

 

2. 생성자 주입

@Service
public class Service {
    private Repository repository;

    public Service(Repository repository) {
        this.repository = repository;
    }
}

스프링에서 가장 권장하는 방식이다.

  • 의존성이 변경되는 경우가 거의 없기 때문에 변경이 불가능하게 함으로써 안전하다.
  • 순수 자바 코드로 작성하기 때문에 테스트 시 특정 DI 프레임워크에 의존하지 않아 순수 자바 코드 테스트가 편하다.
  • 다른 방법들과 달리 생성자 호출 시점에 주입되기 때문에 final를 사용할 수 있고
  • lombok의 @RequiredArgsConstructor를 사용하여 코드를 간결하게 작성할 수 있다.
  • A가 B를 의존하고 B가 A를 의존하는 상황에 객체 생성 시점에 에러를 발생시키기 때문에 순환 참조 에러를 방지할 수 있다. 

 

3. setter 주입

@Autowired
public void setDependency(SomeDependency dependency) {
    this.dependency = dependency;
}
  • 의존성을 변경할 일이 있을 때 사용한다.

 

Bean 등록 방법

컴포넌트 스캔

@Component
public class MyBean(){

}
  • @Component, @Repository, @Service, @Configuration 등의 @Component를 상속 받는 어노테이션들을 스캔해서 빈을 등록하는 방법으로 편리하다.

 

configure에 직접 등록

@Configuration
public class SpringConfig {

    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }
    
    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
}
  • 나중에 빈이 변경될 경우 config class에서 변경하면되기 때문에 편리하다.
  • 등록된 빈들을 한눈에 볼 수 있다.

'개발 > Spring' 카테고리의 다른 글

[Spring] Static 객체와 Bean의 차이  (2) 2023.06.28
[Spring] Filter & Interceptor & AOP  (0) 2023.05.29
빌드와 컴파일의 차이  (0) 2023.04.25
[JPA] Query Method & JPQL  (0) 2023.04.23
[Spring] JWT  (0) 2023.04.19