본문 바로가기
교재 실습/자바 웹 개발 워크북

134. XML 기반 빈 관리 컨테이너 (4)

by Jint 2022. 10. 19.

4. 익명 빈 선언

설정 파일에서 빈을 선언할 때 빈의 이름을 지정하지 않을 수 있다. 이런 빈을 '익명(anonymous) 빈'이라고 부른다. 어떻게 선언하고 사용하는지 살펴본다.

 

- 실습 패키지 생성

exam 패키지 아래에 test03 패키지를 생성한다. test02 패키지의 모든 파일을 test03 패키지에 복사한다.

 

- beans.xml 빈 설정 파일

exam/test03/beans.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 class="exam.test03.Score"/>
	<bean class="exam.test03.Score"/>

</beans>
<bean class="exam.test03.Score"/>

이전 실습 때와는 달리 빈을 선언할 때 id나 name 값을 지정하지 않는다. 이렇게 빈의 이름을 지정하지 않으면 컨테이너에 보관할 때 "패키지 이름 + 클래스 이름 + #인덱스"를 빈의 이름으로 사용한다. 동일한 클래스의 객체가 여러 개 생성될 경우를 대비해 빈 이름 뒤에 "#인덱스"가 자동으로 붙는 것이다. 인덱스는 0부터 시작한다. 앞의 경우 첫 번째 선언된 빈의 이름은 exam.test03.Score#0이 되고, 두 번째 선언된 빈의 이름은 exam.test03.Score#1이 된다. 익명 빈의 경우 클래스 이름은 (exam.test03.Score) 첫 번째 빈(exam.test03.Score#0)에 대한 별명이 된다.

 

- 빈 컨테이너 테스트

빈 컨테이너를 테스트할 exam.test03.Test 클래스를 다음과 같이 편집한다.

package exam.test03;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
	
	public static void main(String[] args) {
		//IoC 컨테이너 준비하여 빈 생성
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("exam/test03/beans.xml");
		
		System.out.println("[컨테이너에 보관된 객체의 이름 출력]");
		for(String name : ctx.getBeanDefinitionNames()) {
			System.out.println(name);
		}
		
		System.out.println("[exam.test03.Score#0의 별명 출력]");
		for(String alias : ctx.getAliases("exam.test03.Score#0")) {
			System.out.println(alias);
		}
		
		System.out.println("[exam.test03.Score#1의 별명 출력]");
		for(String alias : ctx.getAliases("exam.test03.Score#1")) {//별명 없음
			System.out.println(alias);
		}
		
		System.out.println("[익명 빈 꺼내기]");
		Score score1 = (Score)ctx.getBean("exam.test03.Score");
		Score score2 = (Score)ctx.getBean("exam.test03.Score#0");
		if(score1 == score2) System.out.println("score == score#0");
		
		System.out.println("[두 번째로 선언된 익명 빈과 비교]");
		Score score3 = (Score)ctx.getBean("exam.test03.Score#1");
		if(score1 != score3) System.out.println("score != score#1");
		
		System.out.println("[클래스 타입으로 빈 꺼내기]");
		Score score4 = (Score)ctx.getBean(exam.test03.Score.class);
	}
	
}
[예 1]
for(String name : ctx.getBeanDefinitionNames()) {
    System.out.println(name);
}

그림 1 ([예 1] 실행 결과)

익명 빈의 이름을 확인하기 위해 getBeanDefinitionNames()를 호출하였다. 실행 결과를 보면 두 개의 익명 빈이 보관되어 있음을 알 수 있다. 빈의 이름은 패키지 이름을 포함한 클래스 이름이고, 뒤에 인덱스가 붙는다.

[예 2]
for(String alias : ctx.getAliases("exam.test03.Score#0")) {
    System.out.println(alias);
}

for(String alias1 : ctx.getAliases("exam.test03.Score#1")) {//별명 없음
    System.out.println(alias1);
}

그림 2 ([예 2] 실행 결과)

익명 빈의 별명이 있는지 확인하기 위해 getAliases() 메서드를 호출하였다. 실행 결과를 보면 클래스 이름(exam.test03.Score)이 별명으로 등록되었음을 확인할 수 있다.

(추가로 궁금해서 exam.test03.Score#1의 별명을 출력했으나 없었다.)

[예 3]
Score score1 = (Score)ctx.getBean("exam.test03.Score");
Score score2 = (Score)ctx.getBean("exam.test03.Score#0");
if(score1 == score2) System.out.println("score == score#0");

그림 3 ([예 3] 실행 결과)

getBean() 메서드를 호출하여 익명 빈을 꺼낼 때는 빈의 이름으로 "패키지명 + 클래스명#인덱스"를 사용한다. 반환된 인스턴스를 비교해 보면 당연히 'exam.test03.Score'는 'exam.test03.Score#0'의 별명이기 때문에 같은 객체라는 것을 확인할 수 있다.

[예 4]
Score score3 = (Score)ctx.getBean("exam.test03.Score#1");
if(score1 != score3) System.out.println("score != score#1");

그림 4 ([예 4] 실행 결과)

두 번째로 선언된 익명 빈을 꺼내고 싶으면 빈의 이름에 #1을 붙인다. 실행 결과를 보면 클래스 이름으로 꺼낸 빈과 다르다는 것을 확인할 수 있다.

[예 5]
Score score4 = (Score)ctx.getBean(exam.test03.Score.class);

그림 5 ([예 5] 실행 결과)

클래스 타입으로도 객체를 꺼낼 수 있다. getBean() 메서드를 호출할 때 Score 클래스에 대한 정보를 담은 java.lang.Class 객체를 넘긴다. 즉 exam.test03.Score.class 객체를 넘기면 빈 컨테이너에서 이 클래스의 인스턴스를 찾는다. 만약 같은 타입의 객체가 여러 개 있을 경우 예외가 발생한다. 빈 컨테이너 입장에선 어떤 객체를 반환해야 할지 알 수 없기 때문이다. 실행 결과를 보면 exam.test03.Score 인스턴스가 여러 개 있다고 오류를 보고하고 있다.

 

참고도서 : https://freelec.co.kr/book/1674/

 

[열혈강의] 자바 웹 개발 워크북

[열혈강의] 자바 웹 개발 워크북

freelec.co.kr

댓글