Tiny Star

구/스프링

스프링 DAY1

하얀지 2020. 10. 3. 16:00
스프링 컨테이너의 종류

스프링에서는 BeanFactory와 이를 상속한 ApplicationContext 두 가지 유형의 컨테이너를 제공한다.

 

BeanFactory는 스프링 설정 파일에 등록된 <bean> 객체를 생성하고 관리하는 가장 기본적인 컨테이너 기능만 제공한다. 그리고 컨테니어가 구동될 때 <bean> 객체를 생성하는 것이 아니라, 클라이언트의 요청(Lookup)에 의해서만 <bean> 객체가 생성되는 지연 로딩(Lazy Loading) 방식을 사용한다. 따라서 스프링 프로젝트에서 BeanFactory를 사용할 일은 전혀 없다.

 

반면에 ApplicationContext는 BeanFactory가 제공하는 <bean> 객체 관리 기능 외에도 트랜잭션 관리나 메시지 기반의 다국어 처리 등 다양한 기능을 지원한다. 또한, 컨테이너가 구동되는 시점에 <bean> 등록된 클래스들을 객체 생성하는 즉시 로딩(pre-loading)방식으로 동작한다. 그리고 웹 애플리케이션 개발도 지원하므로 대부분 스프링 프로젝트는 ApplicationContext 유형의 컨테이너를 이용한다. 

ApplicationContext의 구현 클래스는 매우 다양하다. 실제로 가장 많이 사용하는 두 개의 클래스만 알고 있으면 된다.

 

구현 클래스

기능

GenericXmlApplicationContext

파일 시스템이나 클래스 경로에 있는 XML 설정 파일을 로딩하여 구동하는 컨테이너이다.

XmlWebApplicationContext

웹 기반의 스프링 애플리케이션을 개발할 때 사용하는 컨테이너이다.

 

 


 

 

스프링 XML 설정
<bean> 엘리먼트 속성
  • init-method

스프링 컨테이너도 서블릿과 같이 스프링 설정 파일에 등록된 클래스를 객체 생성할 때 디폴트 생성자를 호출한다. 따라서 객체를 생성한 후에 멤버 변수 초기화 작업이 필요하다면 , Servlet의 init() 같은 메소드가 필요하다. 이를 위해 스프링에서는 <bean> 엘리먼트에 init-method 속성을 지원한다.

<bean id="tv" class="polymorphism.SamsungTV" init-method="initMethod" />
package polymorphism;

public class SamsungTV implements TV {
    public void initMethod() {
    	System.out.println("객체 초기화 작업 처리.");
    }
    public void powerOn() {
    	System.out.println("SamsungTV 전원을 켠다.");
    }
    ...
}

  • destroy-method

스프링 컨테이너가 객체를 삭제하기 직전에 호출될 임의의 메소드를 지정할 수 있다.

init-method 속성으로 지정한 initMethod() 메소드는 컨테이너가 구동되어 SamsungTV 객체가 생성된 직후에 호출된다. 그리고 컨테이너가 종료되기 직전에 컨테이너는 자신이 관리하는 모든 객체를 삭제하는데, 이때 destroy-method 속성으로 지정한 destroyMethod() 메소드는 SamsungTV 객체가 삭제되기 직전이 호출된다.

<bean id="tv" class="polymorphism.SamsungTV" destroy-method="destroyMethod" />
package polymorphism;

public class SamsungTV implements TV {
    public void initMethod() {
    	System.out.println("객체 초기화 작업 처리.");
    }
    public void destroyMethod() {
    	System.out.println("객체 삭제 전에 처리할 로직 처리.");
    }
    public void powerOn() {
    	System.out.println("SamsungTV 전원을 켠다.");
    }
    ...
}

  • lazy-init

ApplicationContext를 이용하여 컨테이너를 구동하면 컨테이너가 구동되는 시점에 설정 파일에 등록된 <bean>들을 생성하는 즉시 로딩방식으로 동작한다. 그런데 어떤 <bean>은 자주 사용되지도 않으면서 메모리를 많이 차지하여 시스템에 부담을 주는 경우도 있다.

따라서 <bean>이 사용되는 시점에 객체를 생성하도록 init-lazy 속성을 제공한다. <bean>을 등록할 때, lazy-init="ture"로 설정하면 스프링 컨테이너는 해당 <bean>을 미리 생성하지 않고 클라이언트가 요청하는 시점에 생성한다. 결국, 메모리 관리를 더 효율적으로 할 수 있게 된다.

<bean id="tv" class="polymorphism.SamsungTV" lazy-init="ture" />

  • scope

프로그램을 개발하다보면 개발자도 모르는 사이에 수많은 객체가 생성된다. 그런데 이 중에는 하나만 색성돼도 상관없는 객체들이 있다. 그러면 해당 클래스로부터 하나의 객체만 생성하여 유지하려면 어떻게 해야할까? 우선 가장 쉬운 방법은 다음처럼 객체를 생성하고 주소를 복사하여 재사용하는것이다.

Tv tv1 = new SamsungTV();
TV tv2 = tv1;
TV tv3 = tv2;

결과적으로 tv1, tv2, tv3는 같은 주소를 가지므로 하나의 객체를 공유하게 된다. 하지만 이렇게 프로그램을 개발하는 것은 거의 불가능에 가깝다. 결국은 자연스럽게 하나의 객체만 생성하도록 제어해야 하는데, 이때 사용하는 것이 GoF 디자인 패턴 중 하나인 '싱글톤 패턴'이다. 그러나 싱글톤 패턴을 구현하려면 일일이 클래스에 패턴 관련 클래스를 작성해야 하므로 매우 귀찮은 일이다.

결국, 클래스로부터 객체를 생성하는 쪽에서 자동으로 싱글톤 객체로 생성해주는 것이 가장 바람직하며, 스프링에서는 바로 이런 기능을 컨테이너가 제공한다.

scope 속성값은 기본이 싱글톤이다. 해당 bean이 스프링 컨테이너에 의해 단 하나만 생성되어 운용되도록 한다.

<bean id="tv" class="polymorphism.SamsungTV" scope="singleton" />
package polymorphism;

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

public class TVUser {
	public static void main(String[] args) {
		// 1. Spring 컨테이너를 구동한다.
		AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
		
		// 2. Spring 컨테이너로부터 필요한 객체를 요청(Lookup)한다.
		TV tv1 = (TV)factory.getBean("tv");
		TV tv2 = (TV)factory.getBean("tv");
		TV tv3 = (TV)factory.getBean("tv");
		
		// 3. Spring 컨테이터를 종료한다.
		factory.close();	
	}
}

SamsungTV 클래스의 scope 속성값을 singleton으로 설정하고 클라이언트에서 세 번 요청하면 SamsungTV 객체는 메모리에 하나만 생성되어 유지된다.

scope="singleton"

 

<bean> scope 속성을 "prototype"으로 지정하면 스프링 컨테이너는 해당 <bean>이 요청될 때마다 매번 새로운 객체를 생성하여 반환한다.

scope="prototype"

 


 

Setter 인젝션

생성자 인젝션은 생성자를 이용하여 의존성을 처리한다. 하지만 Setter 인젝션은 이름에서 알 수 있듯이 Setter 메소드를 호출하여 의존성 주입을 처리하는 방법이다. 두 가지 방법 모두 멤버변수를 원하는 값으로 설정하는 것을 목적으로 하고 있고, 결과가 같으므로 둘 중 어떤 방법을 쓰든 상관없다. 다만 코딩 컨벤션에 따라 한 가지로 통일해서 사용하는데 대부분은 Setter 인젝션을 사용하며, Setter 메소드가 제공되지 않는 클래스에 대해서만 생성자 인젝션을 사용한다.

package polymorphism;

public class SamsungTV implements TV {
	private Speaker speaker;
    private int price;
    
    public SamsungTV() {
    	System.out.println("SmasungTV(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;
    }
}
<?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="tv" class="polymorphism.SamsungTV">
		<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>
</beans>    

Setter 인젝션을 이용하려면 <property> 엘리먼트를 사용해야하며 name 속성 값이 호출하고자하는 메소드 이름이다. 즉, name 속성값이 "speaker"라고 설정되어 있으면 호출되는 메소드는 setSpeaker()이다.

생성자 인젝션과 마찬가지로 Setter 메소드를 호출하면서 다른 <bean> 객체를 인자로 넘기려면 ref 속성을 사용하고, 기본형 데이터를 넘기려면 value 속성을 사용한다.

 

 


 

컬렉션 객체 설정

 

컬렉션 유형 엘리먼트
java.util.List, 배열 <list>
java.util.Set <set>
java.util.Map <map>
java.util.Properties <props>

 

| List 타입 매핑

배열 객체나 List타입의 컬렉션 객체는 <list> 태그를 사용하여 설정한다.
package com.springbook.ioc.injection;

import java.util.List;

public class CollectionBean {
	private List<String> addressList;
	
	public void setAddressList(List<String> addressList) {
		this.addressList = addressList;
	}
	public List<String> getAddressList() {
		return addressList;
	}
}
<?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="collectionBean" class="com.springbook.ioc.injection.CollectionBean">
		<property name="addressList">
			<list>
				<value>서울시 강남구 역삼동</value>
				<value>서울시 성동구 행당동</value>
			</list>
		</property>
	</bean>  
    
</beans>    
package com.springbook.ioc.injection;

import java.util.List;

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

public class CollectionBeanClient {
	public static void main(String[] args) {
		AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
		CollectionBean bean = (CollectionBean) factory.getBean("collectionBean");
		
        List<String> addressList = bean.getAddressList();
		for(String address : addressList) {
			System.out.println(address.toString());
		}
		factory.close();    
	}
}        

 

List 결과

 


 

 

| Set 타입 매핑

중복 값을 허용하지 않는 집합 객체를 사용할 때는 Set 컬렉션을 사용한다.
package com.springbook.ioc.injection;

import java.util.Set;

public class CollectionBean {
	private Set<String> addressList;
	
	public void setAddressList(Set<String> addressList) {
		this.addressList = addressList;
	}
	public Set<String> getAddressList() {
		return addressList;
	}
}
<?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="collectionBean" class="com.springbook.ioc.injection.CollectionBean">
		<property name="addressList">
			<set value-type="java.lang.String">
				<value>서울시 강남구 역삼동</value>
				<value>서울시 성동구 성수동</value>
				<value>서울시 성동구 성수동</value>
			</set>
		</property>
	</bean>
    
</beans>    
package com.springbook.ioc.injection;

import java.util.Set;

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

public class CollectionBeanClient {
	public static void main(String[] args) {
		AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
		CollectionBean bean = (CollectionBean) factory.getBean("collectionBean");
        
		Set<String> addressList = bean.getAddressList();
		for(String address : addressList) {
			System.out.println(address.toString());
		}
		factory.close();    
	}
}        

"서울시 성동구 성수동"이라는 주소가 두 번 등록된 것을 확인할 수 있다. 그러나 Set 컬렉션은 같은 데이터를 중복해서 저장하지 않으므로 실제 실행해보면 "서울시 성동구 성수동"이라는 주소는 하나만 저장된다.

Set

 


 

 

| Map 타입 매핑

특정 Key로 데이터를 등록하고 사용할 때는 Map 컬렉션을 사용한다.
package com.springbook.ioc.injection;

import java.util.Map;

public class CollectionBean {
	private Map<String, String> addressList;
    
	public void setAddressList(Map<String, String> addressList) {
		this.addressList = addressList;
	}
	public Map<String, String> getAddressList() {
		return addressList;
	}
}
<?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="collectionBean" class="com.springbook.ioc.injection.CollectionBean">
		<property name="addressList">
			<map>
				<entry>
					<key><value>고길동</value></key>
					<value>서울시 강남구 역삼동</value>
				</entry>
				<entry>
					<key><value>마이콜</value></key>
					<value>서울시 강서구 화곡동</value>
				</entry>
			</map>
		</property>
	</bean>
    
</beans>    
package com.springbook.ioc.injection;

import java.util.Map;

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

public class CollectionBeanClient {
	public static void main(String[] args) {
		AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
		CollectionBean bean = (CollectionBean) factory.getBean("collectionBean");
        
		Map<String, String> addressList = bean.getAddressList();
		for( String key : addressList.keySet() ){
			System.out.println( key + ", " +  addressList.get(key) );
        }
		factory.close();  
	}
}        

Map

 


 

 

| Properties 타입 매핑

key=value 형태의 데이터를 등록하고 사용할 때는 properties 컬렉션을 사용한다.
package com.springbook.ioc.injection;

import java.util.Properties;

public class CollectionBean {
	private Properties addressList;
    
	public void setAddressList(Properties addressList) {
		this.addressList = addressList;
	}
    
	public Properties getAddressList() {
		return addressList;
	}	
}
<?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="collectionBean" class="com.springbook.ioc.injection.CollectionBean">
		<property name="addressList">
			<props>
				<prop key="고길동">서울시 강남구 역삼동</prop>
				<prop key="마이콜">서울시 강서구 화곡동</prop>
			</props>
		</property>
	</bean>
    
</beans>    
package com.springbook.ioc.injection;

import java.util.Properties;

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

public class CollectionBeanClient {
	public static void main(String[] args) {
		AbstractApplicationContext factory = new GenericXmlApplicationContext("applicationContext.xml");
		CollectionBean bean = (CollectionBean) factory.getBean("collectionBean");
        
		Properties addressList = bean.getAddressList();
		for( Object key : addressList.keySet() ){
			System.out.println( key + ", " +  addressList.getProperty((String)key) );
        }
		factory.close();
	}
}        

 

 


의존성 주입 어노테이션
어노테이션 설명
@Autowired 주로 변수 위에 설정하여 해당 타입의 객체를 찾아서 자동으로 할당한다.
org.springramework.beans.factory.annotation.Autowired
@Qualifier 특정 객체의 이름을 이용하여 의존성 주입할 때 사용한다.
org.springframework.beans.factory.annotation.Qualifier
@Inject @Autowired와 Qualifier의 기능을 결합한 어노테이션이다.
javax.inject.Inject
@Resource @Autowired와 동일한 기능을 제공한다.
javax.annotation.Resource

 

추가 어노테이션
어노테이션 위치 의미
@Service XXXServicelmpl 비즈니스 로직을 처리하는 Service 클래스
@repository XXXDAO 데이터베이스 연동을 처리하는 DAO 클래스
@Controller XXXController 사용자 요청을 제어하는 Controller 클래스

 

 

 

 

* Spring Quick Start을 발췌한 포스트입니다.

top