[Day 24 | Spring] Spring XML 기반 DI 정리

y♡ding·2024년 11월 14일
0

데브코스 TIL

목록 보기
152/163

1. Spring DI 기본 흐름

  • 스프링 컨테이너(ApplicationContext)는 객체를 생성하고, 의존성을 주입하며, 생명 주기를 관리합니다.
  • XML 설정 파일을 사용해 객체를 정의하고 의존성을 설정할 수 있습니다.
  • XML의 <bean> 태그를 통해 객체를 생성하고 의존성을 설정합니다.

2. 스프링 컨테이너에서 객체 생성 방식

  • singleton (기본값):
    • 컨테이너가 초기화될 때 객체를 생성하며, 동일한 객체를 계속 반환합니다.
    • 객체가 재사용되어 메모리 효율적입니다.
  • prototype:
    • getBean() 메서드를 호출할 때마다 새로운 객체를 생성합니다.
    • 상태가 있는 빈이나 동적 객체가 필요한 경우 유용합니다.
XML 예시
<bean id="singletonBean" class="org.example.model.HelloBean" scope="singleton" />
<bean id="prototypeBean" class="org.example.model.HelloBean" scope="prototype" />

3. DI 방식

스프링 DI는 다음과 같은 방식으로 의존성을 주입합니다:

a. 멤버 필드 초기화 (Setter 주입)
  • 스프링 XML에서 <property>를 사용해 멤버 필드에 값을 설정합니다.
// 클래스 정의
public class HelloBean {
    private String name;
    public void setName(String name) { this.name = name; }
    public void sayHello() { System.out.println(name + "님 안녕하세요!"); }
}
<bean id="helloBean" class="org.example.model.HelloBean">
    <property name="name" value="홍길동" />
</bean>

b. 생성자 주입
  • 객체 생성 시 필요한 값을 생성자를 통해 주입합니다.
  • 기본형 데이터 주입
// 클래스 정의
public class HelloBean {
    private String name;
    public HelloBean(String name) { this.name = name; }
    public void sayHello() { System.out.println(name + "님 안녕하세요!"); }
}
<bean id="helloBean" class="org.example.model.HelloBean">
    <constructor-arg value="홍길동" />
</bean>
  • 참조형 데이터 주입
// 클래스 정의
public class WriteAction {
    private BoardTO to;
    public WriteAction(BoardTO to) { this.to = to; }
    public void execute() { System.out.println("execute(): " + to); }
}
<bean id="boardTO" class="org.example.model2.BoardTO" />
<bean id="writeAction" class="org.example.model2.WriteAction">
    <constructor-arg ref="boardTO" />
</bean>

4. 주요 코드 설명

4.1 Singleton과 Prototype 비교
  • 코드:
  1. Prototype 스코프에서 객체 생성
HelloBean helloBean1 = (HelloBean) ctx.getBean("helloBean1");
HelloBean helloBean2 = (HelloBean) ctx.getBean("helloBean2");
System.out.println("helloBean1: " + helloBean1);
System.out.println("helloBean2: " + helloBean2);

// 이름을 바꾸는 순간에 NEW (1과 5 다른 참조값)
HelloBean helloBean5 = (HelloBean) ctx.getBean("helloBean1");
System.out.println("helloBean5: "  + helloBean5);
  • 동작:

    1. helloBean1helloBean2는 각각 helloBean1helloBean2라는 ID를 가진 프로토타입 빈입니다.
      • scope="prototype"이므로 getBean 호출 시마다 새로운 객체가 생성됩니다.
    2. 다시 helloBean1 ID로 getBean()을 호출하면, 새로운 객체가 생성됩니다.
      • 따라서 helloBean1helloBean5는 서로 다른 참조값을 가집니다.
  • 결과:

    helloBean1: org.example.model.HelloBean@xxxx
    helloBean2: org.example.model.HelloBean@yyyy
    helloBean5: org.example.model.HelloBean@zzzz
    • helloBean1helloBean5는 다른 참조값을 가집니다.
    • 동일한 ID(helloBean1)라도 호출할 때마다 새로운 객체를 반환.

2. Singleton 스코프에서 객체 생성

HelloBean helloBean3 = (HelloBean) ctx.getBean("helloBean3");
HelloBean helloBean4 = (HelloBean) ctx.getBean("helloBean4");
System.out.println("helloBean3: " + helloBean3);
System.out.println("helloBean4: " + helloBean4);

// 3과 5 같은 참조값
HelloBean helloBean5 = (HelloBean) ctx.getBean("helloBean3");
System.out.println("helloBean5: " + helloBean5);
  • 동작:

    1. helloBean3helloBean4는 각각 helloBean3helloBean4라는 ID를 가진 싱글톤 빈입니다.
      • scope="singleton"이므로 컨테이너 초기화 시 단 한 번만 객체를 생성합니다.
    2. 다시 helloBean3 ID로 getBean()을 호출하면 동일한 객체를 반환합니다.
  • 결과:

    helloBean3: org.example.model.HelloBean@aaaa
    helloBean4: org.example.model.HelloBean@bbbb
    helloBean5: org.example.model.HelloBean@aaaa
    • helloBean3helloBean5는 동일한 참조값을 가집니다.
    • helloBean4는 별도의 싱글톤 빈으로서 다른 참조값을 가집니다.

결론

  1. Prototype 스코프:

    • getBean() 호출 시마다 새로운 객체를 반환.
    • 같은 ID를 사용해도 호출할 때마다 다른 참조값.
  2. Singleton 스코프:

    • 같은 ID로 getBean()을 호출하면 항상 동일한 객체를 반환.
    • 다른 ID로 정의된 싱글톤 빈은 각각 독립된 객체.

4.2 생성자 주입
  • 코드:
<bean id="helloBean1" class="org.example.model.HelloBean">
    <constructor-arg value="홍길동" />
</bean>
<bean id="helloBean2" class="org.example.model.HelloBean">
    <constructor-arg value="이몽룡" />
</bean>
HelloBean hello1 = (HelloBean) ctx.getBean("helloBean1");
hello1.sayHello();
HelloBean hello2 = (HelloBean) ctx.getBean("helloBean2");
hello2.sayHello();
  • 결과:
    • helloBean1은 "홍길동님 안녕하세요!" 출력.
    • helloBean2는 "이몽룡님 안녕하세요!" 출력.

4.3 참조형 데이터 주입
  • 코드:
<bean id="boardTO" class="org.example.model2.BoardTO" />
<bean id="writeAction" class="org.example.model2.WriteAction">
    <constructor-arg ref="boardTO" />
</bean>
WriteAction writeAction = (WriteAction) ctx.getBean("writeAction");
writeAction.execute();
  • 결과:
    • writeActionboardTO 객체를 의존성으로 받아 사용.
    • "execute(): org.example.model2.BoardTO@xxxx" 출력.

5. 정리

  1. DI 핵심 개념

    • 객체 생성 및 의존성 주입을 스프링 컨테이너가 담당.
    • XML을 통해 객체 간 의존성을 정의.
  2. 주요 구성 요소

    • <bean>: 스프링 빈을 정의.
    • scope: 빈의 생명주기를 정의(singleton, prototype).
    • <constructor-arg>: 생성자 주입에 사용.
    • <property>: 멤버 필드 초기화에 사용.
  3. DI 활용

    • 재사용성이 높은 코드 작성 가능.
    • 객체 간 결합도를 낮춰 유지보수성과 테스트 용이성을 향상.

0개의 댓글

관련 채용 정보