Dependency Injection(의존관계 주입)이란?

 

이 글은 IoC , Dependency 의 개념을 알고있다는 전제하에 작성된 글입니다.

원활한 이해를 위해서 아래글을 읽어주세요

https://masiljangajji-coding.tistory.com/51

 

IoC(Inversion Of Control)란

IoC/DI(Inversion Of Control/Dependency Injection)란 IoC(Inversion Of Control)란? IoC는 제어의 역전을 뜻합니다. 제어의 역전.. 제어가 역전된다.. 이게 어떤 의미일까요? 기존의 프로그램은 구현 객체가 프로그램

masiljangajji-coding.tistory.com

https://masiljangajji-coding.tistory.com/52

 

Dependency(의존관계)란?

Dependency(의존관계)란? 의존관계는 코드에서 두 모듈간의 연결을 의존관계라 합니다. 객체지향언어에서 두 클래스 간의 관계를 말하기도 합니다. 의존관계의 종류는 크게 4가지가 존재합니다. Dep

masiljangajji-coding.tistory.com

 

Spring Bean과 Context

Spring Bean

Spring Bean은 Spring Container에 등록된 객체를 의미합니다.

Java Beans 와는 다른것으로 Spring FrameWork에서 중요하게 관리하는 객체로 이해하면 됩니다.

 

Java Beans 의 조건

  1. public default (no argument) constructor
  2. getter/setter
  3. implement java.io.Serializable

Container에 Bean이 등록되기 위한 조건

  1. name(식별할 이름)
  2. class(타입)
  3. object(객체)

Java Beans와 다른 별개의 개념입니다.

 

Container/Context

Container는 Interface로 일종의 개념에 해당합니다.

프로그램의 구성 요소들을 담고 관리하는 환경을 나타내며 DI Container , 서블릿 컨테이너 , 웹 컨테이너 등등이 존재합니다.

 

Context는 Container라는 Interface의 구현체입니다.

(Container = DI Container = IoC Container)

 

스프링 프레임워크에서는 빈(Bean) 설정과 관련된 정보를 나타내는 Metadata에 따라

어떤 클래스의 ApplicationContext를 이용할지 정해집니다.

 

다음의 Git Repo를 참고하시길 바랍니다.

https://github.com/masiljangajji/DI_Exapmle

 

GitHub - masiljangajji/DI_Exapmle

Contribute to masiljangajji/DI_Exapmle development by creating an account on GitHub.

github.com

 

XML 을 이용한 DI

xml 파일을 이용해 의존관계를 주입하는 방식입니다.

Context에 등록을 돕는 메타데이터 형식이 xml형식이며 ClassPathXmlApplicationContext 클래스를 사용합니다.

<bean id="english" class="com.springframework.core.practice.language.domain.English"/>

id는 Bean에 등록될 이름을 , class는 클래스의 경로를 나타냅니다

 

이 코드는 다음과 같습니다.

  1. english라는 이름으로
  2. com.springframework.core.practice.language.domain.English 경로에있는
  3. 클래스파일을 reflection으로 읽어 객체를 생성하고 컨테이너에 등록해
public class XmlMain {


    public static void main(String[] args) {

        // 컨텍스트 등록을 돕는 메타데이터로 xml 선택시 ClassPathXmlApplicationContext 사용
        try (ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                // resources/beans.xml 파일을 읽어서 컨테이너에 객체 등록하겠다.
                "beans.xml")) {

            English english = context.getBean("english", English.class);
            Korean korean = context.getBean("korean", Korean.class);
            english.hello();
            korean.hello();
        }

    }

}

korean은 컨테이너에 등록이 안됐기 때문에 다음과 같은 에러가 발생 No bean named 'korean' available

(English와 같은 방식으로 xml에 추가하면 에러가 해결됩니다.)

 

생성자 주입

public class LanguageSpeakService {

    private final Language language;

    public LanguageSpeakService(Language language) {
        this.language = language;
    }

    public void speak() {
        System.out.println("Speak Service");
        language.hello();
    }

}

 

LanguageSpeakService 클래스는 Language에 대한 의존관계를 갖습니다.

 

Field로 갖고있는 타입이 Interface기 때문에 실질적인 동작은 Language의 SubType인

English,Korean 중 하나를 받아 동작할 것입니다.

 

따라서 기존과 같은 방법으로 DI시켜주게 된다면

LanguageSpeakService languageSpeakService =
                    context.getBean("languageSpeakService", LanguageSpeakService.class);

    <bean id="languageSpeakService" class="com.springframework.core.practice.language.service.LanguageSpeakService"/>

스프링은 Service가 필요한 Language가 English , Korean 중 무엇인지 알 수 없음으로 에러가 발생하게 됩니다.

 

이런 경우 Constructor Injection(생성자 주입) 방식을 사용합니다.

 

<bean id="languageSpeakService" class="com.springframework.core.practice.language.service.LanguageSpeakService">
        <constructor-arg ref="korean"/>
</bean>

 

이 코드는 다음과 같습니다.

  1. languageSpeakService라는 이름으로
  2. com.springframework.core.practice.language.service.LanguageSpeakService 경로에있는
  3. 클래스파일을 reflection으로 읽어 객체를 생성할때 korean 이름으로 등록된 Bean을 참고해
  4. 만들어진 languageSpeakService 객체를 컨테이너에 등록해
 public static void main(String[] args) {

        // 컨텍스트 등록을 돕는 메타데이터로 xml 선택시 ClassPathXmlApplicationContext 사용
        try (ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                // resources/beans.xml 파일을 읽어서 컨테이너에 객체 등록하겠다.
                "beans.xml")) {

            English english = context.getBean("english", English.class);
            Korean korean = context.getBean("korean", Korean.class);
            english.hello();
            korean.hello();


            LanguageSpeakService languageSpeakService =
                    context.getBean("languageSpeakService", LanguageSpeakService.class);

            languageSpeakService.speak();

        }

생성자 주입 사용시 코드가 문제없이 동작하게 됩니다.

 

setter 주입

public class LanguageSpeakService {

    private  Language language;

    // setter 주입을 위한 기본 생성자
    public LanguageSpeakService(){

    }
    public LanguageSpeakService(Language language) {
        this.language = language;
    }

    public void setLanguage(Language language) {
        this.language = language;
    }

    public void speak() {
        language.hello();
    }

}

<bean id="languageSpeakService" class="com.springframework.core.practice.language.service.LanguageSpeakService">
        <property name="language" ref="english"/>
</bean>

이 방식은 language 에 대한 표준 명명규칙을 따르는 set메서드를 찾아갑니다.

 

실질적으론 setLanguage 메서드를 찾아 동작하는 것이죠 따라서 name을 바꾼다면 정상동작 하지 않습니다.

<bean id="languageSpeakService" class="com.springframework.core.practice.language.service.LanguageSpeakService">
        <property name="language2" ref="english"/>
</bean>

 

표준 명명규칙을 따르지 않기 떄문에 오류가 발생 (property name 을 language로 변경하면 정상동작 합니다.)

 

 public void setLanguage2(Language language) {
        this.language = language;
    }

<bean id="languageSpeakService" class="com.springframework.core.practice.language.service.LanguageSpeakService">
        <property name="language2" ref="english"/>
</bean>

이렇게 set메서드 이름을 맞춰줄시 정상동작 합니다.
(name = setLanguage2 를 부르겠다 , 넣어줄 인자는 english 이름을 가진 빈)

 

이렇듯 setter주입은 명명규칙을 지켜줘야 하며 기본 생성자가 필요합니다.

 

Autowired Injection

autowired injection 은 다음의 방식을 사용할 수 있습니다.

  1. byType
  2. byName
<bean id="koreanChef" class="com.springframework.core.practice.domain.chef.KoreanChef"/>
<bean id="chefCookService" class="com.springframework.core.practice.service.ChefCookService" autowire="byType"/>

public class ChefCookService {
    private Chef chef;

    public ChefCookService() {
    }

    public void setChef(Chef chef) {
        this.chef = chef;
    }

    public void makeFood() {
        chef.cook();
    }

}

autowired byType설정을 사용하려면 일치하는 빈이 하나여야 합니다.

 

지금은 일치하는 빈이 KoreanChef 하나기 때문에 정상동작 합니다.

하지만 AmericanChef가 추가된다면 일치하는 빈에 중복이 생기며 에러가 발생합니다.

 

<bean id="chefCookService" class="com.springframework.core.practice.service.ChefCookService" autowire="byName"/>

// 양식 조리 출력 
public void setAmericanChef(Chef chef) {
        this.chef = chef;
}

// 한식 조리 출력
public void setKoreanChef(Chef chef) {
        this.chef = chef;
    }

byName을 사용하게되면 set메서드의 이름을 따라갑니다.

 

XML 이용한 DI With Annotation

XML 방식으로 Bean 의존성 주입을 Annotation으로 구현할 수 있습니다.

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}

(Autowired는 생성자 , 메서드 , 파라미터 , 필드 등 대부분에 사용가능)

 

<bean id="koreanChef" class="com.springframework.core.practice.domain.chef.KoreanChef"/>
<bean id="americanChef" class="com.springframework.core.practice.domain.chef.AmericanChef"/>
<bean id="chefCookService" class="com.springframework.core.practice.service.ChefCookService"/>

public class ChefCookService {

    //// Autowired 어노테이션 기반 설정
    @Autowired
    // americanChef 이름을 가진 빈을 사용하겠다
    @Qualifier("americanChef")
    private  Chef chef;

    public ChefCookService() {
    }

    public void makeFood() {
        chef.cook();
    }
}

Annotation 을 이용하지만 XML방식을 기반으로 두고있기 떄문에

등록하고자 하는 객체들은 기존과 동일하게 전부 XML 파일안에 정의되있어야 합니다.

 

이러한 방법은 설정을 아예 분리함으로써 프레임워크와의 의존성을 최소화합니다.

하지만 만약 클래스가 5천개쯤 된다면?

 

지금은 프로그램이 간단해 xml을 읽고 설정하는게 쉽지만 프로그램이 커지는 경우 XML방식은 큰 부담이 됩니다.

 

따라서 프레임워크와의 의존성이 강화되더라도 순수 자바코드로 해결하는 Java Configuration 방식이 도입됩니다.

 

Java Configuration 을 이용한 DI

 

기존에는 XML파일을 메타데이터로 사용했지만 이제는 순수자바를 사용하기 떄문에

AnnotationConfigApplicationContext를 사용합니다.

 

Java Configuration 이용한 방법은 크게 2가지가 존재합니다.

  1. 자바코드로 Bean등록
  2. Component Scan

자바코드로 Bean등록

// 이건 설정하는 파일이야
@Configuration
// JavaConfiguration 에서 XML 설정을 사용할 수 있습니다.
@ImportResource("classpath:/beans.xml")
public class JavaConfig {

    @Bean
    public AmericanChef americanChef() {
        return new AmericanChef();
    }

    @Bean
    public KoreanChef koreanChef() {
        return new KoreanChef();
    }

    // 메서드 주입방식이라 합니다.
    @Bean
    ChefCookService chefCookService() {
        return new ChefCookService(americanChef());
    }
}

여기서 Bean에 등록될 이름은 메서드의 이름과 같습니다.

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.springframework.core.practice");

이 코드는 다음과 같습니다.

  1. com.springframework.core.practice 경로 아래에있는 모든 파일에서
  2. @Configuration , @Component , @Controller , @Service , @Repository 에 해당하는 
  3. 어노테이션이 붙은 클래스들을 자동으로 빈에 등록해.

 

Component Scan

컴포넌트 스캔은 다음의 어노테이션을 포함하는 클래스를 읽어 자동으로 빈에 등록시킵니다.

  1. @Component : 컴포넌트 스캔에서 사용
  2. @Controller : 스프링 MVC 컨트롤러에서 사용
  3. @Service : 스프링 비즈니스 로직에서 사용
  4. @Repository : 스프링 데이터 접근 계층에서 사용
  5. @Configuration : 스프링 설정 정보에서 사용

(Configuration,Controller,Service,Repository 모두 내부적으로 @Component을 상속하고있습니다.)

@Component
public class English implements Language{
    @Override
    public void hello() {
        System.out.println("Hello");
    }
}

@Component
public class Korean implements Language {
    @Override
    public void hello() {
        System.out.println("안녕하세요");
    }
}

생성자 주입

@Component
public class LanguageSpeakService {

    private  final Language language;


    // 생성자 주입
    @Autowired
    public LanguageSpeakService(@Qualifier("english") Language language) {
        this.language = language;
    }

    public void speak() {
        language.hello();
    }

}

생성자를 이용하기 떄문에 Field를 final로 선언 가능해 불변성을 지킬수 있습니다.

가장 권고되는 주입방식입니다.

setter 주입

@Component
public class LanguageSpeakService {
    private Language language;

    // setter 주입
    @Autowired
    public void setLanguage(@Qualifier("english") Language language) {
        this.language = language;
    }

    public void speak() {
        language.hello();
    }

}

기본생성자 필요 , final 선언 불가 

필드주입

@Component
public class LanguageSpeakService {

    // 필드주입 
    @Autowired
    @Qualifier("english")
    private Language language;


    public void speak() {
        language.hello();
    }
}

코드가 가장 간결하지만 외부에서 변경이 불가능해 테스트가 어렵습니다.

또한 setter와 마찬가지로 final 선언이 불가합니다.

 

(이러한 단점들 때문에 거의 사용하지 않음이 권고됩니다.)

Bean Vs Component

자바코드를 이용한 Bean등록은 주로 System 전체에서 공통적으로 사용되야 하는 것들을 대상으로 합니다.

 

예를들어 전체에서 공통적으로 사용되는 Thread Pool , Connection Pool의 경우 

설정파일(JavaConfig.class)에 모아서 셋팅하게되면 "아 이것들은 공통적으로 전체 System에서 사용하는 구나" 이런 이해를 돕습니다.

 

반대로 Business Model들은 각각의 것들이 고유한 성질을 갖습니다.(ChefCookService , LanguageSpeakService...)

이런경우 @Service , @Controller 같은 Component Scan 방식을 이용합니다.

 

 

도움이 되셨으면 좋겠습니다.

'Spring > Spring Core' 카테고리의 다른 글

Dependency(의존관계)란?  (1) 2024.01.04
IoC(Inversion Of Control)란  (1) 2024.01.02

Dependency(의존관계)란?

의존관계는

  • 코드에서 두 모듈간의 연결을 의존관계라 합니다.
  • 객체지향언어에서 두 클래스 간의 관계를 말하기도 합니다.

 

의존관계의 종류는 크게 4가지가 존재합니다.

  1. Dependency(의존관계)
  2. Association(연관관계)
  3. Aggregation(집합관계)
  4. Composition(합성관계)

보통 4가지를 통틀어 Dependency라고 뭉뚱그려 부르긴 하지만 각각의 차이를 인지하는 것이 중요합니다. 

 

이제부터 하나씩 알아보겠습니다.

Dependency(의존관계)

public class UserService {

    public void saveUser(UserRepository userRepository){
        System.out.println("유저저장");
        userRepository.save();
    }

}


public class UserRepository {

    public void save(){
        // do something ..
    }

}

의존관계란 클래스가 다른 클래스를 일시적으로 참조하는 형태입니다.

이 코드에서는 Service가 User를 저장할 때 UserRepository를 param으로 불러와 사용합니다.

saveUser 연산을 동작시킬때만 일시적으로 참조하게 되며 생명주기와 같은 어떤것도 일치하지 않습니다.

가장 낮은 수준의 결합도를 가집니다.

 

Association(연관관계)

public class UserService {
    private UserRepository userRepository;

    public UserRepository getUserRepository() {
        return userRepository;
    }

    public void saveUser() {
        this.userRepository = new UserRepository();
        System.out.println("유저저장");
        userRepository.save();
    }

}


public class UserRepository {

    public void save(){
        // do something ..
    }

}

UserService 객체를 생성할 때는 UserRepository가 생성돼있지 않습니다.
이 부분은 의존관계와 동일하지만 saveUser() 메서드를 호출 해 동작이 끝났음에도 Repository 객체가 남아있게 됩니다.

 

public class Main {

    public static void main(String[] args) {

        UserService userService = new UserService();

        System.out.println(userService.getUserRepository());

        userService.saveUser();

        System.out.println(userService.getUserRepository());

    }

}


이것이 의존관계와의 차이점이며 의존관계보다 높은 결합도를 가집니다.

 

Aggregation(집합관계)

public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public UserRepository getUserRepository() {
        return userRepository;
    }

    public void saveUser() {
        System.out.println("유저저장");
        userRepository.save();
    }

}

public class UserRepository {
    public void save() {
        // do something ..
    }

}

생성자를 통해 다른 클래스의 객체를 받아오는 경우입니다.

(Field를 Final로 선언할 수 있다는 장점이 있습니다.)

UserService 생성을 위해서 필수적으로 UserRepo가 필요합니다.

하지만 이것이 UserService와 UserRepository가 동일한 생명주기를 가진다는 건 아닙니다.

 

UserRepository가 먼저 생성되고 사용된 후 Service 생성도 가능하기 때문입니다.

public class Main {

    public static void main(String[] args) {

        UserRepository userRepository = new UserRepository();

        userRepository.save();
        userRepository.save();
        userRepository.save();

        UserService userService = new UserService(userRepository);
        System.out.println(userService.getUserRepository());
        userService.saveUser();
    }

}

 


기존 관계들과의 차이점은 메서드 호출없이 객체 생성만해도 Repository가 존재합니다.

 

Composition(합성관계)

public class UserService {
    private final UserRepository userRepository;

    public UserService() {
        this.userRepository = new UserRepository();
    }

    public UserRepository getUserRepository() {
        return userRepository;
    }

    public void saveUser() {
        System.out.println("유저저장");
        userRepository.save();
    }

}

public class UserRepository {
    public void save() {
        // do something ..
    }

}

UserService와 UserRepository의 생명주기가 완전히 일치하게 됩니다.
이런 형태를 강하게 결합한다 라고도 말합니다.

 

코드의 결합이 강해지게되면 재사용성이 크게 떨어지기 때문에 보통 Aggregation 관계를 사용합니다.

 

IoC/DI와 Dependency의 관계

 

IoC는 프로그램이 흐름의 제어권을 갖음을 말합니다.

Spring에 경우 Context에 Bean을 등록하고 관리하는 등 Spring Bean의 생명주기를 스스로 관리합니다.

그런데 Aggregation , Composition 과 같이 객체 생성을 위해서 다른 클래스를 필요로하는 경우는 어떡할까요??

 

public interface Language {
    // do something
}

public class Korean implements Language{
    // do something
}

public class English implements Language{
    // do something
}

public class LanguageService {

    private final Language language;

    public LanguageService(Language language) {
        this.language = language;
    }
}

LanguageService는 Aggregation 의존관계를 갖습니다.

따라서 자체적인 객체생성이 불가능하며 다른 타입의 객체가 필요합니다.

하지만 Type이 Language로 되어있기 떄문에 필요한 객체의 타입은 English가 될 수도 Korean이 될 수도 있습니다.

따라서 프로그램이 무엇을 의존할지 자체적으로 정할 수 없게되고

어떤 타입의 객체를 사용할 것인지를 외부에서 주입해줘야 합니다.
(Korean,English 또한 설정을 추가해 주입해줘야 함)

 

이를위한 여러가지 의존관계 주입방법이 나오게됐으며 이를 통틀어 Dependency Injection 이라 부릅니다.

 

아래는 같이읽어보면 좋은 글입니다. 

https://masiljangajji-coding.tistory.com/51

 

IoC(Inversion Of Control)란

IoC/DI(Inversion Of Control/Dependency Injection)란 IoC(Inversion Of Control)란? IoC는 제어의 역전을 뜻합니다. 제어의 역전.. 제어가 역전된다.. 이게 어떤 의미일까요? 기존의 프로그램은 구현 객체가 프로그램

masiljangajji-coding.tistory.com

 

https://masiljangajji-coding.tistory.com/53

 

Dependency Injection(의존관계 주입)이란

Dependency Injection(의존관계 주입)이란? 이 글은 IoC , Dependency 의 개념을 알고있다는 전제하에 작성된 글입니다. 원활한 이해를 위해서 아래글을 읽어주세요 https://masiljangajji-coding.tistory.com/51 IoC(Inver

masiljangajji-coding.tistory.com

도움이 되셨으면 좋겠습니다.

'Spring > Spring Core' 카테고리의 다른 글

Dependency Injection(의존관계 주입)이란  (2) 2024.01.04
IoC(Inversion Of Control)란  (1) 2024.01.02

IoC(Inversion Of Control)란?

IoC는 제어의 역전을 뜻합니다.
제어의 역전.. 제어가 역전된다.. 이게 어떤 의미일까요?

 

기존의 프로그램은 구현 객체가 프로그램의 제어 흐름을 조종합니다.
즉 사용자에 의해서 흐름이 제어되며 프로그램은 사용자가 만들어놓은 흐름에 따라 실행 될 뿐입니다.

 

IoC는 이 상태를 반전시킨 것으로 사용자에 의해 흐름이 제어되는 것이 아닌 프로그램 자체가 제어권을 갖는 것입니다.

 

Non - IoC Exapmle

public class Main {


    public static void main(String[] args) {


        MemoryMemberRepository memoryMemberRepository = new MemoryMemberRepository();
        MemberService memberService = new MemberServiceImpl(memoryMemberRepository);

        /**
         * memberService 를 이용한 코드
         */

    }

}

 

일반적인 형태의 코드입니다.

 

main문 안에서 필요한 객체들을 생성하고 사용하게 됩니다

이 경우 해당 객체들의 생명주기는 main이 시작하면서 생성되고 main이 종료되면서 삭제됩니다.

 

만약 사용자가 코드를 변경한다면

public class Main {

    public static MemberService memberService = new MemberServiceImpl(new MemoryMemberRepository());

    public static void main(String[] args) {

        /**
         * memberService 를 이용한 코드
         */

    }

}

 

객체들의 생명주기는 클래스가 로드될때 생성되고 프로그램 종료시 삭제됩니다.

또한 사용자가 언제 memberService를 호출해 사용할 지 흐름을 정의할 수 있습니다.

 

사용자가 코드를 만드는 방식에 따라서 객체의 생명주기와 동작의 흐름이 바뀌게되며

이는 사용자가 프로그램의 제어 흐름을 조종하는 것과 같습니다.

 

IoC는 이러한 흐름을 역전시켜 프로그램이 자체적으로 흐름을 제어하는 것입니다.

 

IoC Exapmle

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memoryMemberRepository());
    }

    @Bean
    public MemoryMemberRepository memoryMemberRepository() {
        return new MemoryMemberRepository();
    }

}

 

Spring FrameWork에서 자바 코드를 이용해 Bean을 등록하는 코드입니다.

 

이 코드를 통해서 MemberService , MemoryMemberRepository는 Spring Bean의 형태로 Context에 등록됩니다.

또한 Bean들의 생명주기또한 Context가 스스로 제어합니다.

 

사용자는 이 과정에 개입하지 않으며 모든 동작은 Spring이 정의해놓은 순서와 규칙을 통해 이루어집니다.

 

즉 사용자에게 제어권이있는게 아닌 프로그램 자체가 제어권을 갖고있는 것입니다.
이것을 제어권이 역전되었다 , IoC 되었다 라고 말합니다.

 

FrameWork없이 순수 자바코드로도 가능합니다.

@Target(value = {ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
    enum Method{
        POST,GET
    }
    String[] value();
    Method method() default Method.GET;

}

(사용자정의 어노테이션)

public void initialize(Set<Class<?>> c, ServletContext ctx) {

        if (Objects.isNull(c)) {
            log.info("Controller not found");
            return;
        }

        for (Class<?> controllerClass : c) {
            try {
                Constructor<?> constructor = controllerClass.getConstructor();
                Object controllerInstance = constructor.newInstance();

                RequestMapping requestMapping = controllerClass.getAnnotation(RequestMapping.class);
                if (requestMapping != null) {
                    String[] values = requestMapping.value();

                    for (String value : values) {
                        String key = getKey(requestMapping.method().name(), value);
                        beanMap.put(key, controllerInstance);
                    }
                }
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }


        ctx.setAttribute(CONTEXT_CONTROLLER_FACTORY_NAME, this);

    }

 

이 코드는 Java Servlet을 이용해 WebApplication 을 구축하는 코드의 일부입니다.

(Java Servlet은 FrameWork이 아닌 순수자바)

 

Reflection 을 통해 RequestMapping 어노테이션을 갖고있는 클래스의 정보를 읽어와 객체를 생성한 후

Map에 넣어 관리하는 코드로 프로그램이 실행될 때 동적으로 클래스들을 읽어 관리하게 됩니다.

 

 

단위 테스트를 수행하는데 사용되는 프레임워크인 JUnit또한 실행 및 관리를 위한 생명주기를 갖습니다.

@SpringBootTest
class CoreApplicationTests {

    @BeforeEach
    void beforEach() {
    }

    @Test
    void contextLoads() {
    }

    @AfterEach
    void afterEach() {
    }

}

 

사용자는 @BeforeEach , @Test , @AfterEach 어노테이션만 사용하면 프로그램이 알아서 테스트로 인식하고

실행시 JUnit이 이미 정의한 동작 순서와 생명주기에 따라 테스트하게 됩니다.

 

따라서 IoC가 됐다 = 프로그램이 자체적으로 흐름을 제어한다.

이렇게 정리할 수 있습니다.

 

프로그램의 자체적인 흐름이란??

  1. 프로그램이 컨테이너를 통해 생명주기를 관리한다.
  2. 동작의 순서가 프로그램이 정의한 흐름을 따른다.

(이런 흐름에 사용자가 억지로 개입할수는 있지만 일반적인 경우만 다루겠습니다.)

 

IoC의 이점

 

IoC/DI를 구현함으로 사용영역과 구성영역을 나눌 수 있습니다.

쉽게말하면 사용자는 객체의 실행에만 신경쓰고 , 객체들의 관리와 동작의 흐름은 프로그램이 하게 하는것입니다.

// 실행영역 
public static void main(String[] args) {

        // 사용자가 객체 사용시 new 를 이용해 찍어내지 않음 , Context에 등록된 Bean을 꺼내 사용
        // 즉 객체를 사용자가 관리하는게 아님 
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberService memberService = applicationContext.getBean("memberService", MemberService.class);

        // 따라서 객체자체의 실행에만 집중할 수 있음 
        Member member = new Member(1L, "memberA", Grade.VIP);
        memberService.join(member);
    }


// 여기는 구성영역 , 해당 객체들이 Bean에 등록되는 과정과 관리방법은 이미 Spring에 정의돼있음
@Configuration 
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memoryMemberRepository());
    }

    @Bean
    public MemoryMemberRepository memoryMemberRepository() {
        return new MemoryMemberRepository();
    }

}

실행하는 코드와 구성하는 코드를 분리하게 됨으로써 유지보수의 용이성과 가독성을 높여줍니다.

 

이런 IoC개념이 기본적으로 깔려있는 FrameWork는 사용자가 제어 해야 할 상당 부분들을 대신함으로써

개발 생산성을 크게 증가시켜줍니다.

 

Ex) 스프링 사용시 사용자는 객체의 

의존관계를 어떻게 주입할것 인지만 설정해주면 
Bean을 등록하고 관리하는 모든 과정은 스프링이 제어 함 (IoC/DI의 개념)

 

추가적으로 흔히 말하는 FrameWork Vs Library 의 가장 큰 차이도 IoC개념이 있냐 없냐의 차이가 가장 핵심적입니다.

 

아래는 추가로 읽으면 좋은 글입니다.

https://masiljangajji-coding.tistory.com/52

 

Dependency(의존관계)란?

Dependency(의존관계)란? 의존관계는 코드에서 두 모듈간의 연결을 의존관계라 합니다. 객체지향언어에서 두 클래스 간의 관계를 말하기도 합니다. 의존관계의 종류는 크게 4가지가 존재합니다. Dep

masiljangajji-coding.tistory.com

https://masiljangajji-coding.tistory.com/53

 

Dependency Injection(의존관계 주입)이란

Dependency Injection(의존관계 주입)이란? 이 글은 IoC , Dependency 의 개념을 알고있다는 전제하에 작성된 글입니다. 원활한 이해를 위해서 아래글을 읽어주세요 https://masiljangajji-coding.tistory.com/51 IoC(Inver

masiljangajji-coding.tistory.com

도움이 되셨으면 좋겠습니다.

'Spring > Spring Core' 카테고리의 다른 글

Dependency Injection(의존관계 주입)이란  (2) 2024.01.04
Dependency(의존관계)란?  (1) 2024.01.04

+ Recent posts