6.5 프로퍼티를 이용한 객체 관리
1. 학습할 내용
2. 기존 방식
public void contextInitialized(ServletContextEvent event) {
// 객체 준비
}
- 객체를 보관할 때 사용할 서블릿 컨텍스트 객체를 준비
ServletContext sc = event.getServletContext();
//톰캣 서버가 관리하는 객체를 얻기 위한 준비
InitialContext initialContext = new InitialContext();
//DAO가 사용할 DataSource 객체를 얻기
DataSource ds = (DataSource)initialContext.lookup("java:comp/env/jdbc/studydb");
//페이지 컨트롤러가 사용할 DAO 객체 준비
MySqlMemberDao memberDao = new MySqlMemberDao();
memberDao.setDataSource(ds);
//DAO 객체 생성 및 의존 객체(DataSource) 주입
MySqlMemberDao memberDao = new MySqlMemberDao();
memberDao.setDataSource(ds);
//페이지 컨트롤러 준비
sc.setAttribute("/auth/login.do", new LogInController().setMemberDao(memberDao));
//페이지 컨트롤러 객체 생성 및 의존 객체 주입
sc.setAttribute("/auth/login.do", //객체 생성
new LogInController().setMemberDao(memberDao)); //의존 객체 주입
//페이지 컨트롤러를 서블릿 컨텍스트에 보관
sc.setAttribute("/auth/login.do", new LogInController().setMemberDao(memberDao));
sc.setAttribute("/auth/logout.do", new LogOutController());
sc.setAttribute("/member/list.do", new MemberListController().setMemberDao(memberDao));
2. 기존 방식의 문제점과 해결책
1) 문제점
- 새 페이지 컨트롤러가 추가되면, ContextLoaderListener 클래스에 해당 코드를 추가해야 함. (계속 수정해야하는 단점)
MySqlBoardDao boardDao = new MySqlBoardDao();
boardDao.setDataSource(ds);
sc.setAttribute("/board/add.do”,
new BoardAddController().setBoardDao(boardDao));
…
2) 해결책
DAO나 페이지 컨트롤러가 추가되더라도 ContextLoaderListener 클래스를 변경하는 않는 방법? “구조 변경"
- 준비해야 할 객체 정보를 외부 파일(프로퍼티 파일)로 분리
- 객체 정보는 key=value 매핑되는 형태로 작성.
- .properties 파일에 작성된 내용에 따라 객체를 준비할 자동화 클래스 추가
- ContextLoaderListener는 객체 생성 및 관리를 ApplicationContext에게 위임.
public void contextInitialized(ServletContextEvent event) {
try {
ServletContext sc = event.getServletContext();
String propertiesPath = sc.getRealPath(
sc.getInitParameter("contextConfigLocation"));
applicationContext = new ApplicationContext(propertiesPath);
} catch(Throwable e) {
e.printStackTrace();
}
}
- 프런트 컨트롤러도 페이지 컨트롤러를 꺼낼 때 ApplicationContext를 사용.
3. 프로퍼티 실습
1) WEB-INF 폴더에 파일 추가 - application-context.properties
#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
* 문장 끝 공백 주의 -> 에러 발생
- 컨트롤러를 추가할 때 ContextLoaderListener 파일을 수정하지 않고도 application-context.properties에 추가하면 됨
- 키 값과 콘트롤러를 매핑만 해주면 됨
2) ApplicationContext.java
- 의존성 객체를 주입할 클래스를 하나 만들기
package spms.context;
import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Hashtable;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
public class ApplicationContext {
//application-context.properties 파일을 읽어서 ke값과 value을 담아줄 Hashtable 생성
//key : jndi.dataSource, memberDAO, /auth/login.do ....
//value : java:comp/env/jdbc/studydb, spms.dao.MemberDAO, spms.controller.LoginController...
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);
//의존성 주입 메소드
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.LoginController...
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 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메소드의 매개변수 타입으로 객체를 찾음
//dependency = 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;
}
}
3) ContextLoaderListener.java
package spms.listener;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import spms.context.ApplicationContext;
public class ContextLoaderListener implements ServletContextListener {
//datasource의 장점 커넥션풀을 톰캣에서 지원해주기 때문에
//개발자가 직접 connection pool 객체를 만들어 줄 필요가 없다
//BasicDataSource ds;
static ApplicationContext applicationContext;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void contextInitialized(ServletContextEvent sce) {
//웹 어플리케이션이 실행되면 자동으로 DB커넥션 생성 및 MemeberDAO객체 생성
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();
}
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
try {
System.out.println("contextDestroyed");
} catch(Exception e) {
e.printStackTrace();
}
}
}
4) web.xml
<!-- 컨텍스트 초기화 매개변수 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/application-context.properties</param-value>
</context-param>
5) DispatcherServlet.java 수정
//ServletContext sc = this.getServletContext();를 아래 코드로 변경
ApplicationContext ctx = ContextLoaderListener.getApplicationContext();
//Controller pageController = (Controller)sc.getAttribute(servletPath);를 아래 코드로 변경
Controller pageController = (Controller)ctx.getBean(servletPath);
6) 실행 화면
- 로그인, 로그아웃, 회원등록, 회원수정, 회원삭제 잘 되는지 확인
'👨💻 2. 웹개발_Back end > 2-4 JSP & Servlet' 카테고리의 다른 글
[JSP & Servlet] 7장 미니 프로젝트 만들기 (0) | 2021.10.19 |
---|---|
[JSP & Servlet] 6장 미니 MVC 프레임워크 만들기 (6) 애노테이션을 이용한 객체 관리 (0) | 2021.10.19 |
[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 |
댓글