새로운 dao나 controller생성시마다 properties 파일에 계속 추가 하지 않고 쉽게 관리할 수 있는 방법이 바로 Annotation을 이용하는 것이다.
@Component annotation
: controller의 객체를 만들면서 dispatcher컨트롤러에 주소를 전달
applicaton-context.properties
: 객체를 생성하는 것은 annotation으로 만들 것이기 때문에 주석처리
annotation 클래스 추가
객체 이름을 저장하는 변수 value
package spms.annotation;
import java.lang.annotation.*;
// 클래스 파일에 이 정보가 기록되고 실행시에도 유지된다.
// 즉, 실행시에 클래스에 기록된 annotation 값을 참조할 수 있다
//runtime = 실행시 component의 정보가 기록된다
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
String value() default "";
}
각 Controller마다 annotation 추가
Reflections("") : classpath 하위를 모두 찾는다
classpath : src파일 하위에 있는 모든 것
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;
/* application-context.properties 파일을 읽어서
* 파일에 등록된 객체를 생성하는 역할
* */
public class ApplicationContext {
// url : 대응 객체
Hashtable<String, Object> objTable =
new Hashtable<String, Object>();
public Object getBean(String key) {
return objTable.get(key);
}
public ApplicationContext(String propertiesPath)
throws Exception{
// 프로퍼티 파일 목록 읽어오기
Properties props = new Properties();
props.load(new FileReader(propertiesPath));
prepareObjects(props); // 객체 생성
prepareAnnotationObjects(); // 애노테이션 포함 클래스 객체 생성
injectDependency(); // memberDao 주입
}
private void prepareObjects(Properties props) throws Exception{
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.")) {
// tomcat의 DataSource객체를 찾아서 저장
objTable.put(key, ctx.lookup(value));
}else {
// 나머지 클래스들은 직접 객체를 생성한다
objTable.put(key, Class.forName(value).newInstance());
}
}
}
private void prepareAnnotationObjects() throws Exception{
/* Reflections(패키지) : 해당 패키지 하위를 모두 찾는다
* Reflections("spms") : spms 하위를 모두 찾는다
* Reflections("spms.controls") : spms.controls 하위를 모두 찾는다
* Reflections("") : classpath 하위를 모두 찾는다
* */
Reflections reflector = new Reflections("");
// @Component 애노테이션을 가진 type만 추출함
// component가 등록된 클래스를 가져온다
Set<Class<?>> list =
reflector.getTypesAnnotatedWith(Component.class);
String key = null;
for(Class<?> clazz : list) {
// ex) value = "/auth/login.do"
key = clazz.getAnnotation(Component.class).value();
// key, key의 클래스 객체를 등록
objTable.put(key, clazz.newInstance());
}
}
private void injectDependency() throws Exception{
for(String key : objTable.keySet()) {
if(!key.startsWith("jndi.")) {
callSetter(objTable.get(key));
}
}
}
private void callSetter(Object obj) throws Exception{
Object dependency = null;
for(Method m : obj.getClass().getMethods()) {
// 메서드 중에 Setter를 찾아라
/* Dao객체는 set으로 시작하는 메서드가 없으므로
주입 대상이 되지 않는다 */
/* pageController객체만 Setter가 있다 */
if(m.getName().startsWith("set")) {
/* 현재 objTable로부터 첫번째 매개변수에 해당하는
* 클래스 객체를 찾아라 => 즉 Dao객체를 찾아라
* */
/* dependency == MemberDao객체 */
dependency = findObjectByType(m.getParameterTypes()[0]);
if(dependency != null) {
m.invoke(obj, dependency);
}
}
}
}
private Object findObjectByType(Class<?> type) {
for(Object obj : objTable.values()) {
// type으로 obj가 생성된 것이라면
if(type.isInstance(obj))
return obj;
}
return null;
}
}
ContextLoaderListener
...
public class ContextLoaderListener implements ServletContextListener {
//외부에서 바로 가져다 쓸 수 있도록 static (편의성)
static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
try {
System.out.println("contextDestroyed");
// 우리가 DataSource 객체를 해제하지 않아도
// tomcat 서버가 알아서 해제한다
}catch(Exception e) {
e.printStackTrace();
}
}
@Override
public void contextInitialized(ServletContextEvent sce) {
try {
System.out.println("contextInitialized");
ServletContext sc = sce.getServletContext();
String propertiesPath = sc.getRealPath(
sc.getInitParameter("contextConfigLocation"));
applicationContext = new ApplicationContext(propertiesPath);
}catch(Exception e) {
e.printStackTrace();
}
}
이제 properties가 없어도 annotation을 이용하여 DI(memberDao 주입) 가능.