6.6절 애노테이션을 이용한 객체 관리
1. 기존 방식 -> 프로퍼티를 이용한 방식
2. 기존 방식의 문제점
- DAO 또는 페이지 컨트롤러를 추가할 때 마다 프로퍼티 파일을 변경해야 함.
3. 해결 방안
- 애노테이션을 이용하여 클래스 파일에 객체 정보를 담는다.
- DAO 객체 이름을 클래스 파일에 보관
- 페이지 컨트롤러의 서블릿 경로 정보를 클래스 파일에 보관
- DAO 객체나 페이지 컨트롤러를 추가할 때도 단지 애노테이션만 클래스에 선언해 주면 된다.
4. 실습 시나리오
1단계. 객체 이름을 보관할 애노테이션 정의
2단계. DAO 및 페이지 컨트롤러에 애노테이션 적용
3단계. 프로퍼티 파일변경
4단계. ApplicationContext 클래스 변경
- 애노테이션 정보 추출 : 페이지 컨트롤러는 클래스 파일에서 애노테이션 정보를 추출한다.
- 애노테이션 정보를 바탕으로 객체를 생성한다.
5. 참고할것
- 클래스 파일에서 애노테이션 정보를 추출할 때 Reflections 오픈 소스 라이브러리 사용
6. 애노테이션 실습 ( _30_MVC_Annotation )
1) lib 폴더에 라이브러리 추가
2) annotation 패키지 생성
3) component 클래스 생성
package spms.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
String value() default "";
}
4) 모든 컨트롤러에 아래 코드 추가 : 컴포넌트 어노테이션으로 명시한다.
import spms.annotation.Component;
@Component("/member/add.do")
- 그렇게 되면 memberDAO와 pageController를 따로 만들어줄 필요가 없게 됨
5) application-context.properties #2, #3 코드 지우기 (4번에서 컴포넌트 어노테이션으로 명시했기때문)
#1) dataSource
jndi.dataSource=java:comp/env/jdbc/studydb
#2) MemberDAO
memberDAO=spms.dao.MySqlMemberDAO
#3) pageController
/auth/login.do=spms.controller.LoginController
/auth/logout.do=spms.controller.LogoutController
/member/list.do=spms.controller.MemberListController
/member/add.do=spms.controller.MemberAddController
/member/delete.do=spms.controller.MemberDeleteController
/member/update.do=spms.controller.MemberUpdateController
지운 후..
#1) dataSource
jndi.dataSource=java:comp/env/jdbc/studydb
6) ApplicationContext.java
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 {
//application-context.properties 파일을 읽어서 key값과 value을 담아줄 Hashtable 생성
// key : jndi.dataSource, memberDAO, /auth/login.do .....
// value : java:comp/env/jdbc/studydb, spms.dao.MemberDAO, spms.controller.LoginCotroller...
Hashtable<String, Object> objTable = new Hashtable<String, Object>();
// key값에 해당하는 객체 반환하는 메소드
public Object getBean(String key) {
return objTable.get(key);
}
//생성자는 파일경로(application-context.properties)를 읽어서 프로퍼티를 생성해주는 역할
public ApplicationContext(String propertiesPath) throws Exception {
Properties props = new Properties();
props.load(new FileReader(propertiesPath));
//프로퍼티 파일을 읽어서 객체를 만들어 주는 메소드
prepareObject(props);
//어노테이션으로 선언된 객체 생성
prepareAnnotationObject();
//의존성 주입 메소드
injectDependency();
}
public void prepareObject(Properties props) throws Exception {
Context ctx = new InitialContext();
String key = null;
String value = null;
//props.keySet : jndi.dataSource, memberDAO, /auth/login.do .....
for(Object item : props.keySet()) {
key = (String)item;
//props.getProperty : java:comp/env/jdbc/studydb, spms.dao.MemberDAO, spms.controller.LoginCotroller...
value = props.getProperty(key);
if(key.startsWith("jndi.")) {
//key 값이 dataSource면 lookup
objTable.put(key, ctx.lookup(value));
} else {
//dataSource 외에 객체들은 직접 객체 생성
objTable.put(key, Class.forName(value).newInstance());
}
}
}
public void prepareAnnotationObject() throws Exception {
//리플렉션 라이브러리 : 리플렉션API보다 더 쉽게 클래스나 메소드를 찾을 수 있다.
//new Reflections("패키지명") : 해당 패키지의 하위를 모두 찾는다.
//new Reflections("spms") : spms 패키지의 하위를 모두 찾는다.
//new Reflections("spms.controller") : spms.comtroller 패키지의 하위를 모두 찾는다.
//new Reflections("") : classpath 하위를 모두 찾는다(컴파일된 클래스를 모두 검사)
Reflections reflector = new Reflections("");
//@Component 어노테이션을 가진 type 추출하도록 설정
Set<Class<?>> list = reflector.getTypesAnnotatedWith(Component.class);
String key = null;
for(Class<?> clazz : list) {
key = clazz.getAnnotation(Component.class).value();
objTable.put(key, clazz.newInstance());
}
}
public void injectDependency() throws Exception {
for(String key : objTable.keySet()) {
//dataSource가 아니면 setter메소드를 찾아서 DAO를 객체를 주입
if(!key.startsWith("jndi.")) {
//매개변수로는 key값에 해당하는 객체를 보내줌
callSetter(objTable.get(key));
}
}
}
//setter메소드를 찾아서 실행해줌
private void callSetter(Object obj) throws Exception {
Object dependency = null;
//객체에 있는 모든 메소드들을 호출해서 그 중에 setter메소드를 찾음
for(Method m : obj.getClass().getMethods()) {
if(m.getName().startsWith("set")) {
//setter메소드를 찾아서 setter 메소드의 매개변수 타입으로 객체를 찾음
//dependeny = MySqlMemberDAO
dependency = findObjectByType(m.getParameterTypes()[0]);
if(dependency != null) {
m.invoke(obj, dependency);
}
}
}
}
private Object findObjectByType(Class<?> type) {
for(Object obj : objTable.values()) {
if(type.isInstance(obj)) {
return obj;
}
}
return null;
}
}
7) 실행 화면
- 로그인, 로그아웃, 회원등록, 회원수정, 회원삭제 잘 되는지 확인
'👨💻 2. 웹개발_Back end > 2-4 JSP & Servlet' 카테고리의 다른 글
[JSP & Servlet] 7장 미니 프로젝트 만들기 (0) | 2021.10.19 |
---|---|
[JSP & Servlet] 6장 미니 MVC 프레임워크 만들기 (5) 프로퍼티를 이용한 객체관리 (0) | 2021.10.18 |
[JSP & Servlet] 6장 미니 MVC 프레임워크 만들기 (4) 리플랙션 API를 이용하여 프런트 컨트롤러 개선하기 (0) | 2021.10.18 |
[JSP & Servlet] 6장 미니 MVC 프레임워크 만들기 (3) DI를 이용한 빈 의존성 관리 (0) | 2021.10.18 |
[JSP & Servlet] 6장 미니 MVC 프레임워크 만들기 (2) 페이지 컨트롤러의 진화 (0) | 2021.10.18 |
댓글