• [Spring] 의존성 주입(DI) - xml과 어노테이션 활용

    2021. 2. 8.

    by. 꼬마봄이

    스프링 공부 교재로 썼던 스프링 퀵 스타트(저자:채규태) 책의 내용을 바탕으로 공부한 내용을 함께 정리한 글이다.


    ⭐️의존성 주입(injection)⭐️

    Dependency Injection(DI)은 컨테이너가 직접 객체들 사이에 의존관계를 처리하는 것을 의미하며, 객체 사이의 의존관계를 스프링 설정 파일(xml)에 등록된 정보를 바탕으로 컨테이너가 자동으로 처리해 준다.

    (의존성(Dependency) 관계란 객체와 객체의 결합 관계이다.)

     

    의존성 주입 방식은 xml 방식과 어노테이션 방식이 있다.

     

     

     

    ▶ xml로 의존성 주입하기

     

    1️⃣ 세터 인젝션 (Setter Injection)

    Setter 메소드를 호출하여 의존성 주입을 처리한다.

     

    SamsungTV.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    package polymorphism;
     
    public class SamsungTV implements TV{
        private Speaker speaker;
        private int price;
     
        public SamsungTV() {
            System.out.println("===> SamsungTV(1) 객체 생성");
        }
        
        public void setSpeaker(Speaker speaker) {
            System.out.println("===> setSpeaker() 호출");
            this.speaker = speaker;
        }
        
        public void setPrice(int price) {
            System.out.println("===> setPrice() 호출");
            this.price = price;
        }
     
        public void powerOn() {
            System.out.println("SamsungTV---전원 켠다. (가격 : " + price + ")");
        }
     
        public void powerOff() {
            System.out.println("SamsungTV---전원 끈다.");
        }
     
        public void volumeUp() {
            speaker.volumeUp();
        }
     
        public void volumeDown() {
            speaker.volumeDown();
     
        }
    }
    cs

    Setter 메소드는 스프링 컨테이너가 자동으로 호출하고, 호출하는 시점은 <bean> 객체 생성 직후이다.

    Setter 인젝션이 동작하려면 기본생성자도 반드시 필요하다.

     

    applicationContext.xml
    1
    2
    3
    4
    5
    6
    <bean id="tv" class="polymorphism.SamsungTV" destroy-method="destroyMethod">
            <property name="speaker" ref="apple"></property>
            <property name="price" value="2700000"></property>
    </bean>
    <bean id="sony" class="polymorphism.SonySpeaker"></bean>
    <bean id="apple" class="polymorphism.AppleSpeaker"></bean>
    cs

    Setter 인젝션은 <property> 엘리먼트를 사용한다.

    name 속성 값이 호출하고자 하는 메소드 이름이다.

    (변수 이름에서 첫 글자를 대문자로 바꾸고 앞에 "set"을 붙인 것이 호출할 메소드 이름이다. ex) setSpeaker, setPrice)

     

     

     

    2️⃣ 생성자 인젝션 (Constructor Injection)

    기본 생성자가 아닌 매개변수를 가지는 다른 생성자를 호출해서 처리한다.

     

    SamsungTV.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    package polymorphism;
     
    public class SamsungTV implements TV{
        private SonySpeaker speaker;
     
    public SamsungTV() {
            System.out.println("===> SamsungTV(1) 객체 생성");
        }
     
    public SamsungTV(SonySpeaker speaker) { // 다른 생성자 호출
            System.out.println("===> SamsungTV(2) 객체 생성");
            this.speaker = speaker;
        }
     
    public void powerOn() {
            System.out.println("SamsungTV---전원 켠다.");
        }
     
        public void powerOff() {
            System.out.println("SamsungTV---전원 끈다.");
        }
     
    public void volumeUp() {
            speaker.volumeUp();
     
        }
     
        public void volumeDown() {
            speaker.volumeDown();
     
        }
    }
    cs

     

    applicationContext.xml
    1
    2
    3
    4
    5
    <bean id="tv" class="polymorphism.SamsungTV">
            <constructor-arg ref="sony"></constructor-arg>
        </bean>
        
        <bean id="sony" class="polymorphism.SonySpeaker"></bean>
    cs

     

    생성자 인젝션은 <constructor-arg> 엘리먼트를 사용한다.

    생성자 인자로 전달할 객체의 아이디를 ref 속성으로 참조한다.

    매개변수가 사용되어 기본 생성자가 아닌 다른 생성자가 호출된다.

     

    생성자 인젝션 방식은 의존성이 주입될 객체가 먼저 생성되고, 생성된 객체를 매개변수로 받아들이는 생성자가 나중에 호출된다!

     

    -Setter 인젝션 : xml 작성 순서대로 <bean> 객체 생성
    -Constructor 인젝션 : 주입되는 객체 생성 후 xml 작성 순서대로 <bean> 객체 생성

     

     

    ▶ 어노테이션으로 의존성 주입하기

    어노테이션을 사용하려면 xml에서 context 네임스페이스를 추가하고, <context:component-scan> 엘리먼트를 사용한다.

    이 설정을 추가하면 스프링 컨테이너는 클래스 패스에 있는 클래스들을 스캔하여 @Component가 설정된 클래스들을 자동으로 객체 생성한다.

    <context:component-scan> 엘리먼트의 base-package 속성을 이용해 스캔할 대상을 설정한다.

    @Component를 설정해줌으로써 스프링 컨테이너는 해당 클래스를 bean으로 생성하고 관리한다.

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // xml 설정 방식
     
    <bean id="tv" class="polymorphism.LgTV"></bean>
     
     
    // 어노테이션(Annotation) 설정 방식
     
    @Component("tv")
    public class LgTV implements TV {
        public LgTV() {
            System.out.println("===> LgTV 객체 생성");
        }
    }
    cs

     

    id나 name 속성이 설정되지 않은 경우 클래스 이름의 첫글자를 소문자로 변경해서 컨테이너가 자동으로 이름을 설정해준다.

     

    ▶ 의존성 주입 어노테이션

    1️⃣ Autowired

    : 주로 변수 위에 설정하여 해당 타입(by type)의 객체를 찾아서 자동으로 할당한다. (스프링 전용 어노테이션)

     

    어노테이션을 사용할 경우, Setter 메소드나 생성자는 필요 없다.

    그러나 의존성 주입 대상인 SonySpeaker 객체가 메모리에 없으면 에러가 발생하므로 반드시 SonySpeaker 객체가 메모리에 생성되어 있어야 한다. ( xml의 <bean>이나 @Component 중 한 가지 방법을 이용해 객체를 생성한다.)

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    package polymorphism;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
     
    @Component("tv")
    public class LgTV implements TV {
        @Autowired
        private Speaker speaker;
     
        public LgTV() {
            System.out.println("===> LgTV 객체 생성");
        
        public void powerOn() {
            System.out.println("LgTV---전원 켠다.");
        }
        
        public void powerOff() {
            System.out.println("LgTV---전원 끈다.");
        }
     
        public void volumeUp() {
            speaker.volumeUp();
        }
     
        public void volumeDown() {
            speaker.volumeDown();
        }
    }
    cs

     

    @Component → 객체 생성
    @Autowired 의존성 주입

     

     

    2️⃣ Qualifier

    : 같은 타입의 객체가 두개 이상일 때, 특정 객체의 이름을 이용하여 의존성 주입한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package polymorphism;
     
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Component;
     
    @Component("tv")
    public class LgTV implements TV {
        @Autowired
        @Qualifier("apple")
        private Speaker speaker;
     
        public LgTV() {
            System.out.println("===> LgTV 객체 생성");
        }
     
         ~ 생략 ~
    }
    cs

     

    (그 외 @inject, @ Resource이 있지만 스프링에서 지원하지 않음)

     

     

    결론
    개발을 할 때는 xml 방식과 어노테이션 방식 두 가지를 적절히 병행하여 사용하는 것이 가장 현실적인 방법이다.
    즉, 변경되지 않는 객체는 어노테이션으로 설정하여 사용하고, 변경될 가능성이 있는 객체는 xml 설정으로 사용한다.
    또한 라이브러리 형태로 제공되는 클래스는 반드시 xml 설정을 통해서만 사용할 수 있다.

     

     

     

    ▶ 추가 어노테이션

    • @Service : 비지니스 로직을 처리하는 Service 클래스
    • @Repository : 데이터 베이스 연동을 처리하는 DAO 클래스
    • @Controller : 사용자 요청을 제어하는 Controller 클래스

     

    댓글