# Table of Contents

Spring IoC Container에 객체를 등록하는 방법은 두 가지다.

  • XML 파일을 통한 빈 등록
  • Annotation을 통한 빈 등록

이번 포스트에서는 XML 파일을 통한 객체 등록 방법에 대해 정리한다.

# XML 파일을 통한 빈 등록

Phone.java이라는 클래스가 있다고 가정하자.

package com.yologger.app;

public class Phone {

    private String name;
    private String manufacturer;

    // 기본 생성자
    public Phone() {
        
    }
}

이제 빈을 정의할 ApplicationContext.xml 파일을 생성하자. Phone.java클래스는 다음과 같이 등록할 수 있다.








 
 

 
 



// applicationContext.xml

<?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="myPhone" class="com.yologger.app.Phone">
    </bean>

    <bean id="yourPhone" class="com.yologger.app.Phone">
    </bean>

</beans>

Phone.java 클래스에 다음과 같이 생성자가 있다면









 
 
 
 


package com.yologger.app;

public class Phone {

    private String name;
    private String manufacturer;
	
    // 생성자
    public Phone(String name, String manufacturer) {
        this.name = name;
        this.manufacturer = manufacturer;
    }
}

<constructor-arg> 태그로 멤버변수를 설정할 수 있다.

// ApplicationContext.xml

<?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="myPhone" class="com.yologger.app.Phone">
        <constructor-arg value="Galaxy S22"/>
        <constructor-arg value="Samsung"/>
    </bean>

    <bean id="yourPhone" class="com.yologger.app.Phone">
        <constructor-arg value="iPhone 13"/>
        <constructor-arg value="Apple"/>
    </bean>
	
</beans>

Phone.java 클래스에 다음과 같이 Setter 메소드가 있다면

package com.yologger.app;

public class Phone {

    private String name;
    private String manufacturer;

    // Default Constructor
    public Phone() {
        
    }

    // Setter
    public void setName(String name) {
        this.name = name;
    }

    // Setter
    public void setManufacturer(String manufacturer) {
        this.manufacturer = manufacturer;
    }
}

<property> 태그로 멤버변수를 설정할 수 있다.








 
 
 
 

 
 
 
 



// applicationContext.xml

<?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="myPhone" class="com.yologger.app.Phone">
        <property name="name" value="iPhone 11" />
        <property name="manufacturer" value="Apple" />
    </bean>

    <bean id="yourPhone" class="com.yologger.app.Phone">
        <property name="name" value="Galaxy S22" />
        <property name="manufacturer" value="Samsung" />
    </bean>

</beans>

만약 빈이 배열을 포함한다면

class Person {
    String name;
    String[] hobbies;
}

다음과 같이 정의할 수 있다.

// applicationContext.xml
<bean id="me" class=“com.company.app.Person”>
    <property name=“name”>
        <value>Paul</value>
    </property>
    <property name=“hobbies”>
        <list>
            <value>soccer</value>
            <value>basketball</value> 
            <value>swimming</value>
        </list>
    </property>
</bean>		

빈이 참조 타입을 포함한다면

class Person {
    String name;
    Car car;
}

다음과 같이 등록할 수 있다.

// applicationContext.xml
<bean id="myCar" class=“com.company.app.Car”>
    ... 
</bean>

<bean id="me" class=“com.company.app.Person”>
    <property name=“name”>
        <value>Paul</value>
    </property>
    <property name=“car”>
        <ref bean name=“myCar”>
    </property>
</bean>		

# c namespace, p namespace

c namespacep namespace를 사용하면 빈의 속성값을 더 쉽게 설정할 수 있다. <beans> 태그에 다음 네임스페이스를 등록하자.

# c namespace

c namespace를 사용하면 생성자를 통한 속성값 설정을 더 쉽게 할 수 있다. c namespace를 사용하려면 객체에 생성자를 정의해야한다.

class Family {

    String father;
    String mother;
    String brother;
    String sister;

    // 생성자 정의
    public Family(String father, String mother, String brother, String sister){
        this.father = father;
        this.mother = mother;
        this.brother = brother;
        this.sister = sister;
    }
}

그리고 XML 파일에 다음 네임스페이스를 추가한다.




 




<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:c="http://www.springframework.org/schema/c"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>	

이제 다음과 같이 c namespace를 사용할 수 있다.







 



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:c="http://www.springframework.org/schema/c"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id=“samsungFamily” class=“com.company.app.Family” c:father="이건희" c:mother="홍라희" c:sister="이서현" c:brother="이재용" />

</beans>	

# p namespace

p namespace를 사용하면 Setter를 통한 속성값 설정을 더 쉽게 할 수 있다. p namespace를 사용하려면 기본생성자와 setter를 정의해야한다.

class Family {

    String father;
    String mother;
    String brother;
    String sister;

    public Family() {

    }

    public setFather(String name){
        this.father = name;
    }

    public setMother(String name){
        this.mother = name;
    }

    public setBrother(String name){
        this.brother = name;
    }

    public setSister(String name){
        this.sister = name;
    }
}

그리고 XML 파일에 다음 네임스페이스를 추가한다.




 




<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:p = "http://www.springframework.org/schema/p"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>	

이제 다음과 같이 p namespace를 사용할 수 있다.







 



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:p = "http://www.springframework.org/schema/p"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id=“samsungFamily” class=“com.company.app.Family” p:father="이건희" p:mother="홍라희" p:sister="이서현" p:brother="이건희" />

</beans>	

# Component Scan

지금까지 XML 파일을 사용하여 빈을 등록했다.








 
 



// applicationContext.xml

<?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="mailManager" class="com.yologger.app.MailManager">
    </bean>

</beans>

XML 파일 대신 @Component, @Controller, @RestController, @Service, @Repository 등의 어노테이션을 사용하여 빈을 등록할 수도 있다.

 



 






package com.yologger.app;

import org.springframework.stereotype.Component;

@Component
public class MailManager {
    void sendEmail() {
        // ...
    }
}

이 기능을 사용하려면 해당 어노테이션이 붙은 클래스의 경로를 컨테이너에 알려줘야하며, 이를 Component Scan이라고 한다.

프로젝트 구조가 다음과 같다고 가정해보자.

Component Scan은 XML 파일에 다음과 같이 추가한다.









 



// applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.yologger.app" />

</beans>

이제 컨테이너가 해당 패키지를 스캔하여 @Component, @Controller, @RestController, @Service, @Repository이 붙은 컴포넌트를 컨테이너에 빈으로 등록하게 된다.

# Scope

빈은 스코프(Scope)를 가진다. 기본값은 singleton이며, 빈은 컨테이너 내에서 오직 한 개만 존재한다.

<bean id="student" class="com.company.app.domain.Student" scope="singleton">
    <constructor-arg value="Paul"> </constructor-arg>
    <constructor-arg value="USA"> </constructor-arg>
</bean>
class Main{
    public static void main(String[] args){
        // s1, s2는 같은 객체를 레퍼런스 한다.
        Student s1 = ctx.getBean("student", Student.class);
        Student s2 = ctx.getBean("student", Student.class);
        System.out.println(s1 === s2);      // true
    }
}

prototype는 빈을 주입할 때 마다 새로운 인스턴스가 생성된다.

<bean id="student" class="com.company.app.domain.Student" scope="prototype">
    <constructor-arg value="Paul"> </constructor-arg>
    <constructor-arg value="USA"> </constructor-arg>
</bean>
class Main {
    public static void main(String[] args){
        // s1, s2는 다른 객체를 레퍼런스 한다.
        Student s1 = ctx.getBean("student", Student.class);
        Student s2 = ctx.getBean("student", Student.class);
        System.out.println(s1 === s2);      // false
    }
}

# 의존성 주입

빈을 컨테이너에 등록했다면 필요한 위치에서 빈을 주입받아 사용할 수 있다. 이를 의존성 주입(Dependency Inject)이라고 한다.

의존성 주입을 하려면 먼저 컨테이너를 인스턴스화 해야한다. XML 파일을 사용하는 경우 GenericXmlApplicationContext를 이용한다.

// ApplicationContext.xml

<?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="myPhone" class="com.yologger.app.Phone">
        <constructor-arg value="Galaxy S22"/>
        <constructor-arg value="Samsung"/>
    </bean>

    <bean id="yourPhone" class="com.yologger.app.Phone">
        <constructor-arg value="iPhone 13"/>
        <constructor-arg value="Apple"/>
    </bean>
	
</beans>
// Spring IoC Container 생성
String configLocation = "classpath:applicationContext.xml";
AbstractApplicationContext ctx = new GenericXmlApplicationContext(configLocation);

컨테이너를 인스턴스화 했다면 getBean() 메소드를 통해 빈을 가져올 수 있다.

Phone myPhone = ctx.getBean("myPhone", Phone.class);
Phone yourPhone = ctx.getBean("yourPhone", Phone.class);

전체 코드는 다음과 같다.

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class App {
    public static void main(String[] args) {

        // Spring IoC Container 생성
        String configLocation = "classpath:applicationContext.xml";
        AbstractApplicationContext ctx = new GenericXmlApplicationContext(configLocation);
		
        // 의존성 주입
        Phone myPhone = ctx.getBean("myPhone", Phone.class);
        Phone yourPhone = ctx.getBean("yourPhone", Phone.class);
		
        System.out.println(myPhone.getName());  // iPhone 11
        System.out.println(myPhone.getManufacturer());  // Apple
        
        System.out.println(yourPhone.getName());  // Galaxy S22
        System.out.println(yourPhone.getManufacturer());  // Samsung
    }
}

# 다형성과 의존성 주입

다형성을 활용하여 인터페이스 타입의 변수에 여러 구현체를 주입할 수 있다. 다음과 같이 Car.java 인터페이스가 있다.

package com.yologger.app;

public interface Car {
    void drive();
}

그리고 Car인터페이스를 구현한 Truck, SUV, Sedan클래스가 있다.

package com.yologger.app;

public class Truck implements Car {
    @Override
    public void drive() {
        System.out.println("drive truck");
    }
}
package com.yologger.app;

public class SUV implements Car {
    @Override
    public void drive() {
        System.out.println("drive SUV");
    }
}
package com.yologger.app;

public class Sedan implements Car {
    @Override
    public void drive() {
        System.out.println("drive sedan");
    }
}

XML 파일에 각 구현체를 빈으로 등록하자.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 :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="myCar" class="com.yologger.app.Truck" />
    <bean id="hisCar" class="com.yologger.app.SUV" />
    <bean id="herCar" class="com.yologger.app.Sedan" />
	
</beans>

이제 다음과 같이 인터페이스에 구현체를 주입할 수 있다.

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        String configLocation = "classpath:ApplicationContext.xml";
        AbstractApplicationContext ctx = new GenericXmlApplicationContext(configLocation);
		
        Car myCar = ctx.getBean("myCar", Car.class);
        myCar.drive();		// drive truck
		
        Car hisCar = ctx.getBean("hisCar", Car.class);
        hisCar.drive();		// drive SUV
		
        Car herCar = ctx.getBean("herCar", Car.class);
        herCar.drive();		// drive sedan
    }
}