Spring Bean, DI, IoC 이해

growdeveloper ㅣ 2022. 5. 3. 13:56

개념

DI(Dependency Injection)

DI(Dependency Injection)란 스프링이 다른 프레임워크와 차별화되어 제공하는 의존 관계 주입기능으로, 객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입 시켜주는 방식이다.

 

DI(의존성 주입)를 통해서 모듈 간의 결합도가 낮아지고 유연성이 높아진다.

첫 번째 방법은 A객체가 B와 C의 객체를 New 생성자를 통해서 직접 생성하는 방법이고,

 

두번째 방법은 외부에서 생성 된 객체를 setter() 를 통해 사용하는 방법이다.

 

이러한 두번째 방식이 의존성 주입의 예시인데,

A 객체에서 B, C객체를 사용(의존) 할 때 A 객체 에서 직접 생성 하는 것이 아니라 외부(IOC컨테이너)에서 생성된 B,C 객체 를 조립(주입) 시켜 setter 혹은 생성자를 통해 사용하는 방식이다.

 

 

 

스프링에서는 객체를 Bean 이라고 부르며 프로젝트가 실행될때 사용자가 Bean으로 관리하는 개체들의 생성과 소멸에 관련된 작업을 자동적으로 수행해주는데 객체가 생성되는 곳을 스프링에서는 Bean 컨테이너 라고 부른다.

Ioc(Inversion of Control)

Ioc(Inversion of Control)란 '제어의 역전' 이라는 의미로, 말 그대로 메소드나 객체의 호출작업을 개발자가 결정하는 것이 아니라, 외부에서 결정되는 것을 의미한다.

 

IoC는 제어의 역전이라고 말하며, 간단히 말해 "제어의 흐름을 바꾼다" 라고 한다.

 

객체의 의존성을 역전시켜 객체 간의 결합도를 줄이고 유연한 코드를 작성할 수 있게 하여 가독성 및 코드 중복, 유지 보수를 편하게 할 수 있게 한다.

 

기존에는 다음과 순서로 객체가 만들어지고 실행되었다.

  • 객체 생성
  • 의존성 객체 생성
    • 클래스 내부에서 생성
  • 의존성 객체 메소드 호출

하지만 스프링에서는 다음과 같은 순서로 객체가 만들어지고 실행된다.

  • 객체 생성
  • 의존성 객체 주입
    • 스스로가 만드는 것이 아니라 제어권을 스프링에게 위임하여 스프링이 만들어 놓은 객체를 주입한다.
  • 의존성 객체 메소드 호출

스프링이 모든 의존성 객체를 스프링이 실행될때 다 만들어주고 필요한곳에 주입시켜줌으로써 Bean 들은 싱글턴 패턴의 특징을 가지며, 제어의 흐름을 사용자가 컨트롤 하는 것이 아니라 스프링에서 맡겨 작업을 처리하게 된다.


실습 및 이해

자바로 프로그래밍을 하면서 객체를 생성할 떄 직접 클래스에 new 연상자를 이용하여 생성했습니다. 하지만 DI는 개발자가 직접 코딩을 하여 객체를 생성하는 것이 아니라, 컨테이너가 이를 생성시켜 주는 것입니다. 그렇게 된다면 코드에서 직접적인 연관 관계가 발생하지 않아 각 클래스들의 변경이 자유로워 집니다. 이를 느슨한 결합이라고 합니다.

 

DI 를 해야 하는 이유

  • 클래스들 간 의존 관계를 최소화 할 수 있다.
  • 프로젝트 유지보수가 용이하다.
  • 기존에는 개발자가 직접 객체의 생성과 소멸을 제어했을데 DI로 인해 객체의 생성과 소멸 등 클래스간 의존관계를 스프링 컨테이너가 제어 해준다.

DI는 객체의 생성, 소멸, 의존 관계를 개발자가 직접 설정하는 것이 아니라 XML이나 어노테이션을 통해 스프링 프레임워크가 제어합니다. 기존에는 개발자가 직접 객체를 생성해줬던 반면에 스프링 프레임워크에서는 객체의 제어를 스프링이 직접 담당해주는 IoC 특징을 가집니다.

 

스프링에서는 의존하는 객체를 컨테이너 실행 시 객체를 주입하기 떄문에 DI라고 부립니다 .여기서 각 클래스 객체를 Bean(이하 빈)이라고 부르는데 이는 의존관계를 설정하는 xml 파일에서 각각의 객체를 <bean>태그로 표시하기 때문입니다.

 

스프링에서 의존 객체를 주입하는 방법은 두 가지가 있습니다.

  • setter를 이용한 방법
  • 생성자를 이용한 방법

이 2가지에 대한 예시를 보여드리겠습니다.

 

 1. setter를 이용한 방법

▶/resource/bean/person.xml 생성

속성 이름 설명
id 빈 객체의 고유이름으로, 이를 이용하여 빈에 접근하며 중복되서 안된다.
name 빈 객체의 별칭
class 생성할 클래스 이름(빈 객체를 주입할 클래스의 이름)
constuctor-arg 생성자를 이용해 값을 주입할 때 사용한다.
property setter를 이용해 값을 주입할 때 사용한다.
<?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="personService" class="com.khj.work.diT.PersonServiceImpl">
            <!-- id는 빈의 고유이름, class는 객체 주입할 클래스 이름 -->
            <property name="name">
                <value>홍길동</value>
            </property>
            <property name="blood">
                <value>A형</value>
            </property>
        </bean>

</beans>

 

▶ PersonService 생성

package com.khj.work.diT;

public interface PersonService {
    public void sayHello();
}

▶ PersonServicelmpl 생성

package com.khj.work.diT;

public class PersonServiceImpl implements PersonService{
    private String name;
    private int age;
    private String blood;

    /*
     * xml <value> 태그의 값을 setter 이용해 설정
     * setter 안해주면 에러뜸
     * 만약 여기 클래스에 blood에 관한 변수가 없어도
     * 빈즈에서 property로 blood를 생성해주면
     * 그에 대한 setter가 반드시 있어야함
     * setter 말고 생성자로도 설정 가능
     * */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * setter의 인자로 빈에서 생성한 클래스 객체를 넣어줌으로써
     * 객체를 주입했기 때문에 DI라고 할수 있다.
     * @param blood
     */
    public void setBlood(String blood) {
        this.blood = blood;
    }

    @Override
    public void sayHello() {
        System.out.println(this.name);
        System.out.println(this.blood);
        System.out.println(this.age);
    }
}

 

 

▶ main

@SpringBootApplication
public class WorkApplication {

    public static void main(String[] args) {
        SpringApplication.run(WorkApplication.class, args);
        ApplicationContext context = new GenericXmlApplicationContext("/bean/person.xml");

        System.out.println(context.getBean("personService"));

        PersonService person = (PersonService) context.getBean("personService");
        person.sayHello();

    }

}

 

▶ 결과

2. 생성자를 이용한 방법

 

▶PersonServicelmpl 수정

public class PersonServiceImpl implements PersonService{

    private String name;
    private int age;
    private String blood;

    public PersonServiceImpl(String name, int age, String blood) {
        this.name = name;
        this.age = age;
        this.blood = blood;
    }

    /*
     * xml <value> 태그의 값을 setter 이용해 설정
     * setter 안해주면 에러뜸
     * 만약 여기 클래스에 blood에 관한 변수가 없어도
     * 빈즈에서 property로 blood를 생성해주면
     * 그에 대한 setter가 반드시 있어야함
     * setter 말고 생성자로도 설정 가능
     * */
    /*
    public void setName(String name) {
        this.name = name;
    }

    /**
     * setter의 인자로 빈에서 생성한 클래스 객체를 넣어줌으로써
     * 객체를 주입했기 때문에 DI라고 할수 있다.
     * @param blood
     *
     /
    public void setBlood(String blood) {
        this.blood = blood;
    }
    */
    @Override
    public void sayHello() {
        System.out.println(this.name);
        System.out.println(this.blood);
        System.out.println(this.age);
    }
}

▶ Bean 수정

<?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="personService1" class="com.khj.work.diT.PersonServiceImpl">
            <!-- id는 빈의 고유이름, class는 객체 주입할 클래스 이름 -->
            <constructor-arg value="이순신"></constructor-arg>
            <constructor-arg value="60"></constructor-arg>
            <constructor-arg value="A"></constructor-arg>
        </bean>
        <bean id="personService2" class="com.khj.work.diT.PersonServiceImpl">
            <!-- id는 빈의 고유이름, class는 객체 주입할 클래스 이름 -->
            <constructor-arg value="손흥민"></constructor-arg>
            <constructor-arg value="28"></constructor-arg>
            <constructor-arg value="B"></constructor-arg>
        </bean>



</beans>

 

▶ 결과

 

 

※ 참조

https://kingofbackend.tistory.com/41

 

[Spring] 스프링 DI, IoC 이해하기

자동차에서 만약 엔진이 오래되서 엔진을 바꾸고 싶다면, 자동자 전체가 아닌 엔진만 뽑아서 바꿔주면 됩니다. 객체 지향 프로그래밍에서도 클래스에 대한 변경이 필요하면 다른 클래스에 영향

kingofbackend.tistory.com

https://velog.io/@gillog/Spring-DIDependency-Injection

 

[Spring] DI, IoC 정리

DI(Dependency Injection)란 스프링이 다른 프레임워크와 차별화되어 제공하는 의존 관계 주입 기능으로,객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입 시켜주는 방식이다.DI(의존성 주입)

velog.io

 

'스프링부트' 카테고리의 다른 글

Spring boot visual studio 에서 사용하기  (0) 2023.01.04