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

94. 어노테이션을 이용한 객체 관리 (3)

by Jint 2022. 8. 15.

5. ApplicationContext 변경

@Component 어노테이션이 붙은 클래스에 대해서도 객체를 생성하도록 ApplicationContext 클래스를 변경한다. spms.context.ApplicationContext 클래스를 다음과 같이 변경한다.

package spms.context;

import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Hashtable;
import java.util.Properties;
import java.util.Set;

import javax.naming.Context;
import javax.naming.InitialContext;

import org.reflections.Reflections;

import spms.annotation.Component;

//프로퍼티 파일을 이용한 객체 준비
public class ApplicationContext {
	//객체 저장할 보관소
	Hashtable<String,Object> objTable = new Hashtable<String,Object>();

	//객체 꺼낼 메서드(getter)
	public Object getBean(String key) {
		return objTable.get(key);
	}

	//ApplicationContext 클래스의 생성자
	public ApplicationContext(String propertiesPath) throws Exception {
		//'이름=값'형태로 된 파일 다루는 Properties 클래스
		Properties props = new Properties();
		//load() : FileReader를 통해 읽어드린 프로퍼티 내용 키-값 형태로 내부 맵에 보관
		props.load(new FileReader(propertiesPath));
		prepareObjects(props);
		prepareAnnotationObjects();
		injectDependency();
	}

	//프로퍼티 파일의 내용 로딩 후 객체 준비 메서드
	private void prepareObjects(Properties props) throws Exception {
		//JNDI 객체 찾을 때 사용할 InitialContext 준비
		Context ctx = new InitialContext();
		String key = null;
		String value = null;
    
		for(Object item : props.keySet()) {
			key = (String)item;
			value = props.getProperty(key);
			if(key.startsWith("jndi.")) {//keySet() : key목록 가져옴
				//lookup() : JNDI 인터페이스 통해 톰캣 서버에 등록된 객체 찾음
				objTable.put(key, ctx.lookup(value));
			}else {
				//Class.forName() 호출하여 클래스 로딩하고, newInstance() 사용하여 인스턴스 생성
				objTable.put(key, Class.forName(value).newInstance());
			}
		}
	}
	
	//어노테이션이 붙은 클래스를 찾아 객체 준비 메서드
	private void prepareAnnotationObjects() throws Exception {
		//Reflections 클래스 : 원하는 클래스를 찾아주는 도구
		Reflections reflector = new Reflections("");
		//@Component 어노테이션 선언된 클래스 찾아 클래스 목록 반환
		Set<Class<?>> list = reflector.getTypesAnnotatedWith(Component.class);
		String key = null;
		for(Class<?> clazz : list) {
			//어노테이션에 정의된 메서드 호출하여 속성값 꺼냄
			key = clazz.getAnnotation(Component.class).value();
			//어노테이션을 통해 알아낸 객체 이름(key)으로 인스턴스 저장
			objTable.put(key, clazz.newInstance());
		}
	}
    
    ...
}

이전 소스와 달라진 점은 prepareAnnotationObjects() 메서드를 추가한 부분이다. 프로퍼티 파일에 등록된 대로 객체를 준비하는 것은 예전처럼 그대로 진행한다. 여기에다가 어노테이션이 붙은 클래스를 찾아서 객체를 준비하는 것이 추가되었다. prepareAnnotationObjects() 메서드가 바로 그 일을 수행한다.

prepareObjects(props);
prepareAnnotationObjects();

 

- prepareAnnotationObjects() 메서드

이 메서드는 자바 classpath를 뒤져서 @Component 어노테이션이 붙은 클래스를 찾는다. 그리고 그 객체를 생성하여 객체 테이블에 담는 일을 한다. 이 작업을 위해 'Reflections'라는 오픈소스 라이브러리를 활용하였다.

 

Reflections 클래스는 구글 코드 사이트(https://code.google.com/archive/p/reflections/)에 등록된 오픈 소스 프로젝트이다. 현재 이 프로젝트의 소스는 GitHub 사이트(https://github.com/ronmamo/reflections)로 옮겨졌다. 이 라이브러리에서 제공하는 클래스를 사용하면, 자바에서 제공하는 리플랙션 API를 사용하는 것보다 더 쉽게 클래스를 찾거나 클래스의 정보를 추출할 수 있다.

//Reflections 클래스 : 원하는 클래스를 찾아주는 도구
Reflections reflector = new Reflections("");

Reflections 클래스는 우리가 원하는 클래스를 찾아 주는 도구이다. 생성자에 넘겨주는 매개변수 값은 클래스를 찾을 때 출발하는 패키지이다. 만약 매개변수 값이 'spms'라면 spms 패키지 및 그 하위 패키지를 모두 뒤진다. 매개변수 값이 'spms.controls'라면, spms.controls 패키지와 그 하위 패키지를 모두 뒤진다. 예제에서는 빈 문자열을 넘겼다. 즉 자바 classpath에 있는 모든 패키지를 검색하라는 뜻이다.

Reflections 클래스의 getTypesAnnotatedWith() 메서드를 사용하면 어노테이션이 붙은 클래스들을 찾을 수 있다. 이 메서드의 매개변수 값은 어노테이션의 클래스(java.lang.Class 객체)이다.

//@Component 어노테이션 선언된 클래스 찾아 클래스 목록 반환
Set<Class<?>> list = reflector.getTypesAnnotatedWith(Component.class);

@Component 어노테이션이 붙은 클래스를 찾고 싶으면 앞의 코드처럼 어노테이션 클래스를 지정하면 된다. 반환되는 값은 @Component 어노테이션이 선언된 클래스 목록이다.

for(Class<?> clazz : list) {
    ...
}

getAnnotation() 메서드를 통해 클래스로부터 어노테이션을 추출한다. 어노테이션에 정의된 메서드를 호출하여 속성값을 꺼낼 수 있다. @Component의 기본 속성값을 꺼내고 싶으면, 다음과 같이 value()를 호출한다.

//어노테이션에 정의된 메서드 호출하여 속성값 꺼냄
key = clazz.getAnnotation(Component.class).value();

이렇게 어노테이션을 통해 알아낸 객체 이름(key)으로 인스턴스를 저장한다.

//어노테이션을 통해 알아낸 객체 이름(key)으로 인스턴스 저장
objTable.put(key, clazz.newInstance());

 

6. Reflections 라이브러리 준비

이제 실행을 위해 마지막으로 Reflections 라이브러리를 준비한다. 다음 지시에 따라 라이브러리를 다운로드 받고 배치한다. 웹 브라우저에서 https://code.google.com/archive/p/reflections/에 접속하여 메인 화면에서 'Downloads' 링크를 클릭한다(그림 1).

그림 1 (Reflections 사이트 메인 화면)

다운로드 화면에서 'reflections-0.9.9-RC1-uberjar.jar' 링크를 클릭한다. 이 JAR 파일에는 Reflections 라이브러리와 의존 라이브러리들이 들어있다. 또한 'reflections-0.9.9-RC1.jar' 링크를 클릭한다. 이 JAR 파일은 Reflections 라이브러리 파일이다(그림 2).

그림 2 (Reflections 라이브러리 및 의존 라이브러리 내려받기)

reflections-0.9.9-RC1.jar 파일을 WEB-INF/lib 폴더에 복사한다. reflections-0.9.9-RC1-uberjar.jar 파일의 압축을 해제한다. 압축 해제된 파일들 중에서 JAR 파일을 WEB-INF/lib 폴더에 복사한다. 다음 그림은 WEB/lib 폴더의 내용이다.

그림 3 (WEB-INF/lib 폴더)

톰캣 서버를 재시작하여 동작을 테스트한다. 정상적으로 잘 동작한다. 이렇게 어노테이션을 이용하면 프로퍼티 파일을 이용하는 것보다 객체 관리가 더 쉽다. 또한, 소스 파일에 객체에 대한 부가 정보를 붙이기 때문에 유지 보수가 편해진다.

 

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

 

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

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

freelec.co.kr

댓글