Programming/Spring

[Spring] DI, DL, IoC와 ApplicationContext

kmindev 2023. 9. 4. 14:47

김영한님의 Spring 핵심원리 강의를 듣고 정리하는 글입니다

 

 

1. DI, IoC 탄생배경


DI, IoC가 없었더라면, 객체지향의 원칙 중 DIP, OCP 등을 완벽하게 지킬 수 없다.
코드를 보자.

public class MemberServiceImpl implements MemberService {
    private MemberRepository memberRepository = new MemoryMemberRepository();

}
  • MemberServiceImpl은 MemoryMemberRepository와 의존관계를 가진다.
  • MemberReposiory는 인터페이스로 구현체(클래스)가 바뀔 경우 MemberServiceImpl에서도 코드를 변경해줘야 한다. ⇒ 객체지향 설계원칙(DIP, OCP)를 위반한다.
  • 스프링에서 이야기하는 제어의 역전(IoC), 의존관계 주입(DI)은 다형성을 활용해서 역할구현을 편리하게 다룰 수 있도록 지원한다.

 

2. IoC / DI / DL / 스프링 컨테이너


IoC(Inversion of Control)

  • 제어의 역전이라 한다.
  • 스프링 애플리케이션에서는 오브젝트(빈)의 생성의존관계 설정, 사용, 제거 등의 작업을 애플리케이션 코드 대신 스프링 컨테이너가 담당한다.
  • 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 IoC 라고 한다.

 

DI(Dependency Injection)

  • 의존관계 주입이라는 의미이다.
  • 의존관계를 자신이 아닌 외부(스프링 컨테이너)에서 주입하는 것이다.

 

DL(Dependency Lookup)

  • 의존관계 검색이라는 의미이다.
  • 의존관계가 있는 객체를 외부에서 주입 받는 것이 아닌, 의존관계가 필요한 객체에서 직접 검색하는 방식이다.

 

스프링 컨테이너

  • 스프링에서 IoC를 담당하는 컨테이너를 어셈블러, IoC 컨테이너라, DI 컨테이너 라고도 한다.
  • 빈을 생성하고 의존관계를 설정하는 기능을 담당하는 가장 기본적인 IoC 컨테이너는 BeanFactory라고 한다.
  • 스프링에서는 컨테이너가 단순한 DI 작업 이외에 다른 작업을 하는데, BeanFactory에 여러가지 기능을 추가한 것을 Application Context라 한다.

 

3. Application Context와 BeanFactory의 관계

 

[Spring Framwork 6.0.12 문서]

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/io/support/ResourcePatternResolver.html

 

ResourcePatternResolver (Spring Framework 6.0.12 API)

Pseudo URL prefix for all matching resources from the class path: "classpath*:". This differs from ResourceLoader's "classpath:" URL prefix in that it retrieves all matching resources for a given path — for example, to locate all "beans.xml" files in the

docs.spring.io

 

ApplicationContext와 BenFactory

"김영한님의 Spring 핵심원리 강의 참고"

BeanFactory

  • BeanFactory는 스프링 컨테이너에서 최상위 인터페이스다.
  • 스프링 빈을 관리하고 조회하는 역할을 한다.
  • getBean()과 같은 대부분의 기능은 BeanFactory에서 제공한다.

 

ApplicationContext

  • 위에서 말했듯이 BeanFactory에서 제공하는 Bean을 관리하고 조회하는 기능 외에 많은 부가기능이 존재한다.
  • 대표적으로 BeanFactory 이외에도 4가지 인터페이스를 상속 받아 부가기능을 제공한다.(다른 인터페이스도 존재)
    • MessageSource를 활용한 국제화 가능
    • EnvironmentCapable(환경변수)
    • ApplicationEventPublisher(애플리케이션 이벤트)
    • ResourceLoader(편리한 리소스 조회)

 

 

4. DI, IoC 설정과 ApplicationContext 등록하는 코드 예시

 

1.의존관계 주입이 필요한 MemberServiceImpl, MemberRepository

public class MemberServiceImpl implements MemberService {
    private final MemberRepository memberRepository;

    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}

 

 

2.IoC와 의존관계 설정
컨테이너가 의존 관계를 맺어주기 위해서는 빈(bean)으로 등록해줘야 한다.
ApplicationContext 설정 정보를 등록하는 방법은 대표적으로 두 가지 방법이 존재한다.(xml, java)

 

< Java 코드로 빈 등록 >

@Configuration
public class AppConfig {
    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
}

 

< xml 파일로 빈 등록(appConfig.xml) >

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="memberService" class="hello.core.member.MemberServiceImpl">
        <constructor-arg name="memberRepository" ref="memberRepository"/>
    </bean>

    <bean id="memberRepository" class="hello.core.member.MemoryMemberRepository"/>
<bean/>

 

 

3.Application Context 생성과 설정 파일 등록

// Java 코드로 등록한 설정 파일(AppConfig.java) ApplicationContext에 등록
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

// XML로 등록한 설장 파일(AppCong.xml) ApplicationContext에 등록
ApplicationContext ac = new GenericXmlApplicationContext("appConfig.xml");

 

  • DI, IoC 설정과 ApplicationContext 설정 정보를 등록하는 방법에 대해서 알아보았다.
  • XML 파일이나, Java코드(@Bean)의 설정 정보를 ApplicationContext에 파라미터 넘겨주면 스프링 컨테이너는 설정 정보를 사용해서 스프링 빈에 등록한다.

 

또한 각 빈 이름은 하나만 존재해야 하며, 그렇지 않을 경우에 NoUniqueBeanDefinitionException 예외가 발생한다.
기본적으로 빈 이름은 메서드 이름을 사용한다. @Bean(name ="빈이름") 이런식으로 빈이름을 직접 지정할 수 있다.

 

5. ApplicationContext에서 관리하는 Bean 조회

// ApplicationContext에 AppConfig를 설정 정보로 등록
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

@Test
@DisplayName("이름으로 조회")
void findBeanName() {
    MemberService memberService = ac.getBean("memberService", MemberService.class);
    assertThat(memberService).isInstanceOf(MemberServiceImpl.class);
}
  • AnnotationConfigApplicationContext 설정 정보를 등록하면 설정 정보에 맞게 빈을 생성하고 의존 관계를 맺어주게 된다.
  • 빈이 제대로 등록 되었는지 확인하고 싶을 경우에 getBean(”빈 이름”, 타입)을 통해 확인할 수 있다.(타입만으로 조회할 수 있다)

 

6. BeanDefinition이란?

"김영한님의 Spring 핵심원리 강의 참고"

  • 스프링 빈 설정 메타정보라고 한다.
  • 스프링에서 다양한 설정 형식(java, xml)을 지원할 수 있는 이유는 추상화가 잘 되어 있기 때문이다.
  • 하나의 Bean 당 가각 하나씩 메타 정보가 생성된다.

스프링 컨테이너는 메타 정보를 가지고 스프링 빈을 생성한다.

 

7. 마무리

  • 객체지향 설계원칙을 지키며 의존관계를 클라이언트 코드에서 설정하지 않고, 외부에서 설정하며, 애플리케이션을 빠르고 편리하게 개발하기 위해 Spring 컨테이너와 DI, IoC 개념이 나오게 되었다.
  • 스프링 컨테이너는 다양한 설정 형식을 지원하며, XML, Java 코드로 의존 관계와 빈 등록과 같은 정보를 설정하는 방법에 대해서 다뤄보았다.