본문 바로가기

웹 프로그래밍

Spring - 핵심 개념(IoC, DI)

@markdown

# Spring 핵심 개념(IoC, DI)

<br/>

## Spring IoC(Inversion of Control) 제어역행

____

- 개발자가 직접 객체간 의존관계를 연결하던 제어권을, 서블릿, EJB를 관리하는 `container`에게 넘긴 것

- 객체의 생성부터 생명주기의 관리까지 모든 객체에 대한 제어권이 바뀐 것(개발자->컨테이너)을 의미한다.

- 다른 곳에서 생성된 클래스를 가져와서 사용하는 방법(결합도를 낮춤)

- 컨테이너에서 관리되고 있는 객체를 개발자가 필요할 때 코드 주입(의존 주입)을 통해 사용하는 것


## Spring Container

____

- `Spring Framework`에서 `Container` 기능을 제공해주는 클래스를 의미

- `Container` : Bean 클래스를 관리하는 주체

- `Bean` : `Spring`에서 관리되는 객체를 나타냄

- 객체를 재사용하는 방식으로, 기본적으로 `Singleton` 패턴으로 객체가 생성된다.

- `Spring xml` 파일 : 어떤 클래스의 `Setter`로 주입할지, `Constructor`로 주입할지 등의 설정 정보가 들어있는 파일

- 종류로 `Bean Factory`, `Application Context`가 있는데 `Application Context`를 가장 많이 사용함


### Bean

- `Spring Framework`에 의해 `LifeCycle`이 관리되는 클래스

- `XML`에 <bean /> 태그를 이용하여 등록

- `id` : 여러 개의 Bean 클래스를 식별하기 위한 이름 설정

- `class` : 사용하려는 Bean 클래스의 패키지명을 포함한 클래스명


### BeanFactory

- `Bean`의 LifeCycle(생성과 소멸)을 관리

- `Spring XML` 파일로 설정 정보를 저장하여, `Spring`이 여러가지 `Container Service`를 사용하는데 참조한다.

- `Resource` 클래스

- `ClassPathResource` : Xml 설정파일을 로딩하는 단순한 BeanFactory

- `FileSystemResource` : 지정된 파일 시스템으로부터 Xml 설정파일 로딩

- `BeanFactory Container` : `src/main/resources` 경로에 `beanFactory.xml` 파일 생성


### beanFactory.xml 코드

<pre><code class="xml" style="font-size:14px"><?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">

  <!-- class 파일 등록 : 패키지이름.클래스파일 등록 -->

  <bean id="hello" class="basic.Hello"/> 

</beans>

</code></pre>

- `src/main/java` 경로에 `hello.java`, `helloMain01.java` 파일 생성


### hello.java 코드

<pre><code class="java" style="font-size:14px">public class Hello {

private String msg;

public Hello() {

System.out.println("Bean 생성");

msg = "hello";

}

public void printMsg(){

System.out.println("msg : " + msg);

}

}

</code></pre>

<br/>

### helloMain01.java 코드

<pre><code class="java" style="font-size:14px">import org.springframework.beans.factory.BeanFactory;

import org.springframework.beans.factory.xml.XmlBeanFactory;

import org.springframework.core.io.ClassPathResource;

import org.springframework.core.io.Resource;

//개발자가 자기 코드에 객체를 생성하고 관리하는 방식

public class helloMain01 {

public static void main(String[] args) {

//class path는 src/main/resources 경로

Resource resource = new ClassPathResource("beanContainer.xml");

BeanFactory factory = new XmlBeanFactory(resource);

Hello obj1 = (Hello) factory.getBean("hello");

Hello obj2 = (Hello) factory.getBean("hello");

obj1.printMsg();

System.out.println("obj1 : " + obj1);

System.out.println("obj2 : " + obj2);

//obj1과 obj2가 객체주소가 같다.

}

}

실행결과

msg : hello

obj1 : basic.Hello@29444d75

obj2 : basic.Hello@29444d75

</code></pre>


## BeanFactory vs ApplicationContext

____

- `BeanFactory`는 객체 생성 시점이 `getBean()`을 호출할 때이다.

- `ApplicationContext`는 클래스가 `new`되는 시점에 객체가 생성된다.


### 실행결과로 보는 Container 객체 생성 시점

<pre><code class="java" style="font-size:14px"><beanFactory.xml>

<bean id="hello1" class="basic.Hello"/>

<bean id="hello2" class="basic.Hello"/>

</code></pre>

<pre><code class="java" style="font-size:14px"><BeanFactory java 코드>

Resource resource = new ClassPathResource("beanContainer.xml");

BeanFactory factory = new XmlBeanFactory(resource);


Hello obj1 = (Hello) factory.getBean("hello1");

obj1.printMsg();

Hello obj2 = (Hello) factory.getBean("hello2");

obj2.printMsg();


실행결과

Bean 생성

msg : hello

Bean 생성

msg : hello

</code></pre>

<pre><code class="java" style="font-size:14px"><ApplicationContext java 코드>

ApplicationContext context = new FileSystemXmlApplicationContext("beanContainer.xml");


Hello obj1 = (Hello) context.getBean("hello1");

obj1.printMsg();

Hello obj2 = (Hello) context.getBean("hello2");

obj2.printMsg();


실행결과

Bean 생성

Bean 생성

msg : hello

msg : hello

</code></pre>


## Spring DI(의존 주입)

____

- `DI(Dependency Injection)` : 각 `class` 사이의 의존관계를 `Bean` 설정 정보를 바탕으로 `container`가 자동으로 연결해 주는 것을 말한다.

- `Spring`에서 `IoC`를 제공하는 형태 중 하나(DL, DI)

- 의존관계가 발생함으로서 결합도를 낮춰, 코드 수정을 피할 수 있다.


### DI 종류

- `Setter Injection` : 클래스 setter 메소드를 이용해 객체 의존관계 연결

- `Constructor Injection` : 클래스 생성자를 사용해 객체 의존관계 연결

- `Method Injection` : `Singleton` 패턴으로 생성된 인스턴스 객체와 싱글톤이 아닌 인스턴스 간 의존관계를 연결시킬 때

- 코드 작성 방법

- `Spring`에서 제공하는 DI 활용

- 개발자가 코드에서 객체를 생성하지 않는 방식

- `XML` 환경설정 파일 또는 `Annotation`을 사용해 객체 주입

### Setter Injection 

<pre><code class="java" style="font-size:14px">A a = new A();

B b = new B();

b.setClass(a); //DI 시점

</code></pre>

<br/>

### Constructor Injection

<pre><code class="java" style="font-size:14px">A a = new A();

B b = new B(a); //DI 시점

</code></pre>

<br/>

### XML에서의 DI Constructor 주입 방법

- `<constructor-arg>` 태그 사용

- 클래스 생성자의 파라미터 순서대로 주입 시켜준다.

- `index` 속성으로 순서를 설정할 수도 있다.(0부터 시작)

- 아래에서 나올 `Car`와 `Tire` 클래스는 의존관계로, 자동차에 바퀴를 추가하는 개념으로 사용한 것


<pre><code class="java" style="font-size:14px"><bean id="Tire" class="package.TireClass">

<bean id="car" class="package.CarClass"/>

<!-- Car Class의 생성자 매개변수 넘기기, 숫자와 클래스 -->

<constructor-arg index="0"><value>2</value></constructor-arg>

<constructor-arg index="1" value="1"></constructor-arg>

<constructor-arg ref="tire"></constructor-arg>

</bean>

</code></pre>

<br/>

### XML에서의 DI Setter 주입

- `<property>` 태그 사용

- 클래스 `setter()` 메소드를 사용해 주입 시켜준다.

- `ApplicationContext`에서는 로딩 될 때 객체가 만들어졌기 때문에 `property` 속성을 사용할 수 있다.


<pre><code class="java" style="font-size:14px"><bean id="Tire" class="package.TireClass">

<bean id="car" class="package.CarClass"/>

<property ref="tire"/>

</bean>

</code></pre>