티스토리 뷰

JAVA/Spring

Spring - 빈의 범위(Bean Scope)

realizers 2021. 10. 17. 14:35
728x90
반응형

Spring Bean이란?

  • Spring에서 POJO(Plain, old java object)를 'Beans'라고 부릅니다.
  • Beans는 애플리케이션의 핵심을 이루는 객체이며, Spring IoC컨테이너에 의해 인스턴스화, 관리, 생성됩니다.
  • Beans는 설정 메타 데이터(XML 파일)에 의해 생성됩니다.
  • 애플리케이션의 객체가 지정되면, 해당 객체는 getBean() 메서드를 사용하여 가져올 수 있습니다.

Spring Bean 정의

  • 일반적으로 XML파일에 정의합니다.
  • 주요 속성으로는
    • class(필수) - 정규화된 자바 클래스 이름
    • id - bean의 고유한 식별자
    • abstract - true로 설정된 경우 빈 정의가 추상적이라는 의미
      • abstract 키워드를 사용하여 추상적으로 빈을 설정하게 된다면 class 특성을 지정하지 않는 경우 부모 클래스의 정의를 추상적으로 정의해 스프링 컨테이너가 해당 빈 인스턴스를 생성하지 않게 해야합니다.  
      • 다만 상속을 받는 자식 빈에 대해서는 class 특성을 지정해야 합니다.
      • 스프링 컨테이너가 추상 빈 정의에 해당하는 빈은 생성하지 않습니다.
    • scope - 객체의 범위(singleton, prototype)
    • constructor-arg - 생성 시 생성자에 전달할 인수
    • property - 생성시 bean setter에 전달할 인수

사용 예제

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="person" class="com.spring.person.Person">
		<constructor-arg index="0" value="1" />
		<constructor-arg index="1" value="홍길동" />
		<constructor-arg index="2" value="25" />
	</bean>
	
	<bean id="personDao" class="com.spring.person.PersonDao" />
	
	<bean id="personService" class="com.spring.person.PersonService">
		<property name="personDao" ref="personDao" />
	</bean>
	
	<bean id="anotherPerson" class="com.spring.person.Person">
		<constructor-arg index="0" value="1" />
		<constructor-arg index="1" value="홍길동" />
		<constructor-arg index="2" value="25" />
	</bean>
	
	<bean id="prototypePerson" class="com.spring.person.Person" scope="prototype">
		<constructor-arg index="0" value="2" />
		<constructor-arg index="1" value="이순신" />
		<constructor-arg index="2" value="30" />
	</bean>
</beans>

 

Person Class

package com.spring.person;

public class Person {

	private long id;
	private String name;
	private int age;
	
	public Person(long id, String name, int age) {
		this.id = id;
		this.name = name;
		this.age = age;
	}

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
}

 

PersonDao Class

package com.spring.person;

public class PersonDao {
	
	private Map<Long, Person> personMap = new HashMap<Long, Person>();
	
	public Person getPerson(long id) {
		return personMap.get(id);
	}
	
	public boolean createPerson(Person person) {
		personMap.put(person.getId(), person);
		
		return true;
	}
}

 

PersonService Class

package com.spring.person;

public class PersonService {

	private PersonDao personDao;
	
	public void setPersonDao(PersonDao dao) {
		this.personDao = dao;
	}
	
	public PersonDao getPersonDao() {
		return personDao;
	}
	
	public Person getPerson(long id) {
		return personDao.getPerson(id);
	}
	
	public boolean createPerson(Person person) {
		return personDao.createPerson(person);
	}
}

 

테스트 코드 1) 

  • 동일한 XML 파일로 person1과 person2의 객체를 만들면 스프링 컨테이너는 싱글톤 빈을 생성하게 되어 두 개의 person은 동일하게 됩니다.
public class PersonTest {

	private static ApplicationContext context;
	
	@BeforeClass
	public static void init() {
		context = new ClassPathXmlApplicationContext("classpath:META-INF/spring/applicationContext.xml");
	}
	
	@Test
	public void samePersonInstances() {
		Person person1 = (Person) context.getBean("person");
		Person person2 = (Person) context.getBean("person");
		
		assertSame(person1, person2);
	}
}

 

테스트 코드 2)

  • 위의 예제와 마찬가지로 Dao 객체 역시 동일한 것을 알 수 있습니다.
public class PersonTest {

	private static ApplicationContext context;
	
	@BeforeClass
	public static void init() {
		context = new ClassPathXmlApplicationContext("classpath:META-INF/spring/applicationContext.xml");
	}
	
	@Test
	public void sameDaoInstances() {
		PersonService personService = (PersonService) context.getBean("personService");
		PersonDao dao1 = (PersonDao) context.getBean("personDao");
		PersonDao dao2 = personService.getPersonDao();
		
		assertSame(dao1, dao2);
	}
}

 

테스트 코드 3)

  • XML 파일에서 scope의 범위를 prototype으로 설정하게 된다면 요청이 있을 떄 마다 스프링 IoC가 빈을 생성하기 때문에 싱글톤이 아니게 됩니다. 그래서 person1과 person2는 서로 다른 주소를 가지고 있습니다.
public class PersonTest {

	private static ApplicationContext context;
	
	@BeforeClass
	public static void init() {
		context = new ClassPathXmlApplicationContext("classpath:META-INF/spring/applicationContext.xml");
	}
	
	// bean의 scope가 prototype인 경우
	@Test
	public void scopeDiff() {
		Person person1 = (Person) context.getBean("prototypePerson");
		Person person2 = (Person) context.getBean("prototypePerson");
		
		assertNotSame(person1, person2);
	}
}

 

테스트 코드 4)

  • 동일한 XML을 사용하여 스프링 컨테이너를 각각 만들게 되면 스프링 컨테이너 마다 각각의 범위를 가지게 되므로 서로의 영역은 다르게 되어 자원을 공유할 수 없게됩니다.
public class PersonTest {

	private static ApplicationContext context;
	
	@BeforeClass
	public static void init() {
		context = new ClassPathXmlApplicationContext("classpath:META-INF/spring/applicationContext.xml");
	}
	
	// 동일한 메타데이터를 이용하여 스프링 컨테이너의 인스턴스를 2개를 만드는 경우
	@Test
	public void referenceDiff1() {
		ApplicationContext anotherContext = new ClassPathXmlApplicationContext("classpath:META-INF/spring/applicationContext.xml");
		Person person1 = (Person) context.getBean("person");
		Person person2 = (Person) anotherContext.getBean("person");
		
		assertNotSame(person1, person2);
	}
}

 

테스트 코드 5)

  • XML 파일에 person 객체와 anotherPerson객체를 생성하게 되면 2개의 bean이 생성되므로 역시 서로 다른 주소를 가지게 됩니다.
public class PersonTest {

	private static ApplicationContext context;
	
	@BeforeClass
	public static void init() {
		context = new ClassPathXmlApplicationContext("classpath:META-INF/spring/applicationContext.xml");
	}
    
	@Test
	public void referenceDiff2() {
		Person person1 = (Person) context.getBean("person");
		Person person2 = (Person) context.getBean("anotherPerson");
		
		assertNotSame(person1, person2);
	}
}
728x90
반응형