VO 자동화
객체를 만들어야하는 update, add, login, delete 컨트롤러 코드수정
DataBinding interface
package spms.bind;
public interface DataBinding {
// 자동으로 생성해야 할 VO객체 항목을 반환
public Object[] getDataBinders();
}
MemberAddController
package spms.controls;
import java.util.Map;
import spms.bind.DataBinding;
import spms.dao.MemberDao;
import spms.vo.Member;
public class MemberAddController implements Controller, DataBinding {
MemberDao memberDao = null;
public MemberAddController setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
return this;
}
@Override
public Object[] getDataBinders() {
// key값 이름, 자동으로 생성해야 할 클래스 타입
return new Object[] {
"member", spms.vo.Member.class
};
}
@Override
public String execute(Map<String, Object> model) throws Exception {
//자동화때문에 객체를 무조건 만들기때문에 객체가 있냐 없냐로 판단하면 안된다.
// 그래서 객체의 정보로 판단할 수 있도록 수정했다.
// if(model.get("member") == null) {
// return "/member/MemberForm.jsp";
// }
Member member = (Member)model.get("member");
if(member.getEmail() == null) {
return "/member/MemberForm.jsp";
}else {
memberDao.insert(member);
return "redirect:list.do";
}
}
}
LogInController
package spms.controls;
import java.util.Map;
import javax.servlet.http.HttpSession;
import spms.bind.DataBinding;
import spms.dao.MemberDao;
import spms.dao.MySqlMemberDao;
import spms.vo.Member;
public class LogInController implements Controller, DataBinding {
MemberDao memberDao = null;
public LogInController setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
return this;
}
@Override
public String execute(Map<String, Object> model) throws Exception {
Member loginInfo = (Member)model.get("loginInfo");
if(loginInfo.getEmail() == null) {
return "/auth/LogInForm.jsp";
}else {
Member member = memberDao.exist(loginInfo.getEmail(),
loginInfo.getPassword());
if(member != null) {
HttpSession session = (HttpSession)model.get("session");
session.setAttribute("member", member);
return "redirect:../member/list.do";
}else {
return "/auth/LogInFail.jsp";
}
}
}
@Override
public Object[] getDataBinders() {
return new Object[] {
"loginInfo", spms.vo.Member.class
};
}
}
MemberUpdateController
package spms.controls;
import java.util.Map;
import spms.bind.DataBinding;
import spms.dao.MemberDao;
import spms.vo.Member;
public class MemberUpdateController implements Controller, DataBinding {
MemberDao memberDao = null;
public MemberUpdateController setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
return this;
}
@Override
public String execute(Map<String, Object> model) throws Exception {
Member member = (Member)model.get("member");
if(member.getEmail() == null) {
Integer no = (Integer)model.get("no");
Member detailInfo = memberDao.selectOne(no);
model.put("member", detailInfo);
return "/member/MemberUpdateForm.jsp";
}else {
memberDao.update(member);
return "redirect:list.do";
}
}
@Override
public Object[] getDataBinders() {
//어떤 클래스가 생성될 것이냐 하는 정보를 return
return new Object[] {
"no", Integer.class,
"member", spms.vo.Member.class
};
}
}
MemberDeleteController
package spms.controls;
import java.util.Map;
import spms.bind.DataBinding;
import spms.dao.MemberDao;
public class MemberDeleteController implements Controller, DataBinding {
MemberDao memberDao = null;
public MemberDeleteController setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
return this;
}
@Override
public String execute(Map<String, Object> model) throws Exception {
Integer no = (Integer)model.get("no");
memberDao.delete(no);
return "redirect:list.do";
}
@Override
public Object[] getDataBinders() {
return new Object[] {
"no", Integer.class
};
}
}
ContextLoaderListener
package spms.listeners;
import java.sql.Connection;
import java.sql.DriverManager;
import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import spms.controls.*;
import spms.dao.MySqlMemberDao;
import spms.util.DBConnectionPool;
/* JNDI
* WAS(Web Application Server)의 리소스(자원)에 대한 고유 이름 정의
* 어플리케이션에서 서버의 리소스를 접근할 때 사용하는 명명 규칙
* 1) java:comp/env - 응용 프로그램 환경 항목
* 2) java:comp/env/jdbc - JDBC 데이터 소스
* 3) java:comp/ejb - EJB 컴포넌트
* 4) java:comp/UserTransaction - UserTransaction 객체
* 5) java:comp/env/mail - JavaMail 연결 객체
* 6) java:comp/env/url - URL 정보
* 7) java:comp/env/jms - JMS 연결 객체
* */
public class ContextLoaderListener implements ServletContextListener {
@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();
// tomcat서버가 자동으로 생성하고 해제하는 DataSource객체를
// 찾아서 가져온다
InitialContext initialContext = new InitialContext();
DataSource ds = (DataSource)initialContext.lookup(
"java:comp/env/jdbc/studydb");
MySqlMemberDao memberDao = new MySqlMemberDao();
memberDao.setDataSource(ds);
// Controller 객체를 '서블릿 컨텍스트' 영역에 저장한다
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));
sc.setAttribute("/member/add.do",
new MemberAddController().setMemberDao(memberDao));
sc.setAttribute("/member/update.do",
new MemberUpdateController().setMemberDao(memberDao));
sc.setAttribute("/member/delete.do",
new MemberDeleteController().setMemberDao(memberDao));
}catch(Exception e) {
e.printStackTrace();
}
}
}
DispatcherServlet
if 문 => 모두 객체를 만드는 것이었는데 이제는 자동으로 만들 것이니까 모두 지운다.
package spms.servlets;
import java.io.IOException;
import java.util.HashMap;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import spms.bind.DataBinding;
import spms.bind.ServletRequestDataBinders;
import spms.controls.Controller;
@WebServlet("*.do")
@SuppressWarnings("serial")
public class DispatcherServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
// pageController에 분기 전송을 위해 '서블릿 경로'를 얻는다
String servletPath = request.getServletPath();
try {
ServletContext sc = this.getServletContext();
// '서블릿 경로'와 일치하는 객체를 '서블릿 컨텍스트'공유 공간에서 꺼낸다.
Controller pageController =
(Controller)sc.getAttribute(servletPath);
HashMap<String, Object> model = new HashMap<String, Object>();
model.put("session", request.getSession());
// pageController 객체가 DataBinding의 상속을 받았다면
// => 만들어야할 객체가 존재하는 pageController라면
// 데이터 바인딩 타입이니? 혹은 데이터 바인딩을 상속받은 객체니?
if(pageController instanceof DataBinding) {
// pageController에 전달할 객체를 생성하여
// 브라우저가 보내는 Parameter를 객체에 자동 주입하여
// model 객체에 저장한다
//=>브라우저가 요청하는 데이터를 담아서 model에 저장한다
preparedRequestData(request, model, (DataBinding)pageController);
}
String viewUrl = "";
// POJO Page Controller
if (pageController != null) {
viewUrl = pageController.execute(model);
// ex) update의 경우 업데이트된 member 객체를 request 공간에 set하여 정보를 가져다 쓸 수 있도록 한다.
for (String key : model.keySet())
request.setAttribute(key, model.get(key));
}else {
System.out.println("주소 대상 Controller가 없습니다!");
}
// 경로가 'redirect:'로 시작하면 바로 이동
if (viewUrl.startsWith("redirect:")) {
response.sendRedirect(viewUrl.substring(9));
return;
} else {
RequestDispatcher rd = request.getRequestDispatcher(viewUrl);
rd.include(request, response);
}
} catch (Exception e) {
e.printStackTrace();
request.setAttribute("error", e);
RequestDispatcher rd = request.getRequestDispatcher("/Error.jsp");
rd.forward(request, response);
}
}
private void preparedRequestData(HttpServletRequest request,
HashMap<String, Object> model,
DataBinding dataBinding)
throws Exception{
//pageController마다 자기가 생성한 객체를 리턴
Object[] dataBinders = dataBinding.getDataBinders();
String dataName = null; //key 이름
Class<?> dataType = null; //생성할 객체의 클래스 정보
Object dataObj = null; //생선한 객체
//넘겨주는 dataBinding에는 pageController의 객체가 담기는데, 2개가 짝이니까 i+2
for(int i=0; i<dataBinders.length; i+=2) {
dataName = (String)dataBinders[i];
dataType = (Class<?>)dataBinders[i+1];
/*
request : 매개변수 추출을 위해 필요
dataType : 객체를 생성할 클래스 타입
dataNmae : 매개변수 이름
*/
//객체를 생성해준다
//request의 getparameter로 정보를 꺼내야 하기 때문에 매개변수로 주었다.
dataObj = ServletRequestDataBinders.bind(request, dataType, dataName);
//만들어진 객체를 model에 저장한다
model.put(dataName,dataObj);
}
}
}
reflection 방식으로 객체 생성
해당 객체의 field목록과 method 목록도 얻어낼 수 있다.
ServletRequestDataBinders
invoke = 메서드 호출
package spms.bind;
import java.lang.reflect.Method;
import java.sql.Date;
import java.util.Set;
import javax.servlet.ServletRequest;
public class ServletRequestDataBinders {
/* request : 매개변수 추출
* dataType : 클래스 타입으로 객체 생성
* dataName : 매개변수 이름
* */
public static Object bind(ServletRequest request,
Class<?> dataType,
String dataName) throws Exception {
// 생성해야 할 대상이 PrimitiveType일 경우
if(isPrimitiveType(dataType)) {
return createValueObject(dataType,
request.getParameter(dataName));
}
// 일반 vo객체일 경우
else {
// 브라우저가 보낸 매개변수들의 이름을 Set에 담는다.
Set<String> paramNames = request.getParameterMap().keySet();
// 클래스 타입대로 객체 생성
Object dataObject = dataType.newInstance();
// 브라우저가 보낸 매개변수를 객체의 필드를 찾아서 저장
Method m = null; // VO의 Setter를 찾아서 저장
// 브라우저가 보낸 매개변수를 1개씩 접근한다
for(String paramName : paramNames) {
// 매개변수에 해당하는 Setter 얻기
/* ex) no 매개변수 -> m은 setNo를 가리킨다
* email -> m은 setEmail를 가리킨다
* password -> m은 setPassword를 가리킨다
* */
m = findSetter(dataType, paramName);
if(m != null) {
// dataObject객체의 m메서드를 호출한다
// 첫번째 매개변수에 클라이언트의 매개변수값을 대입
m.invoke(dataObject,
createValueObject(m.getParameterTypes()[0],
request.getParameter(paramName)));
}
}
// 브라우저가 보낸 변수값까지 객체에 저장한 후 생성한 객체를 리턴
return dataObject;
}
}
private static Method findSetter(Class<?> type, String name) {
// 해당 클래스 타입이 가진 모든 메서드를 추출
Method[] methods = type.getMethods();
String propName = null;
for(Method m : methods) {
// 메서드 시작이름이 set이 아니면 돌아가라
if(!m.getName().startsWith("set"))
continue;
// set을 제외한 이름 ==> 프로퍼티
propName = m.getName().substring(3);
propName = propName.toLowerCase(); // 소문자로
// name으로 넘겨준 이름과 같으면 => 해당 프로퍼티의 setter를 찾았다
if(propName.equals(name.toLowerCase())) {
return m;
}
}
return null;
}
private static boolean isPrimitiveType(Class<?> type) {
if(type.getName().equals("int") || type==Integer.class ||
type.getName().equals("long") || type==Long.class ||
type.getName().equals("float") || type==Float.class ||
type.getName().equals("double") || type==Double.class ||
type.getName().equals("boolean") || type==Boolean.class ||
type==Date.class || type==String.class) {
return true;
}
return false;
}
private static Object createValueObject(Class<?> type, String value) {
if(type.getName().equals("int") || type==Integer.class)
return new Integer(value);
else if(type.getName().equals("float") || type==Float.class)
return new Float(value);
else if(type.getName().equals("double") || type==Double.class)
return new Double(value);
else if(type.getName().equals("long") || type==Long.class)
return new Long(value);
else if(type.getName().equals("boolean") || type==Boolean.class)
return new Boolean(value);
else if(type==Date.class)
return java.sql.Date.valueOf(value);
else
return value;
}
}
controller 자동화
ex) key
/auth/login.do = value
LogInController (이 객체가 담당한다)
#1. for ApplicationContext
# 1) [tomcat Object]
jndi.dataSource=java:comp/env/jdbc/studydb
# 2) [General Object]
memberDao=spms.dao.MySqlMemberDao
# 3) [Page Controller Object]
/auth/login.do=spms.controls.LogInController
/auth/logout.do=spms.controls.LogOutController
/member/list.do=spms.controls.MemberListController
/member/add.do=spms.controls.MemberAddController
/member/update.do=spms.controls.MemberUpdateController
/member/delete.do=spms.controls.MemberDeleteController
ApplicationContext
dependency = findObjectByType(m.getParameterTypes()[0]);
: memberDao 타입으로 만들어진 obj=MemberSqlDao, ProjectDao이고 그것을 return하여 dependency에 넣는다.
m.invoke(obj, dependency);
obj(Controller)에 MemberDao의 객체를 set시킨다.
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;
/*
application-context.properties 파일을 읽어서
파일에 등록된 객체를 생성하는 역할
*/
public class ApplicationContext {
// url : 대응객체 Hashtable (이 주소로 들어오면 이 객체를 줘라 )
// servlet 주소, pageController 객체
Hashtable<String, Object> objTable = new Hashtable<String, Object>();
public Object getBean(String key) {
//key(주소)에 대응하는 page컨트롤러 객체 리턴
return objTable.get(key);
}
//생성자
public ApplicationContext(String propertiesPath) throws Exception{
// 프로퍼티 파일 목록 읽어오기
Properties props = new Properties();
props.load(new FileReader(propertiesPath));
prepareObjects(props); //객체 생성
//listener에서 setMemberDao로 주입하던 것을
injectDependency(); //memberDao 객체를 pageController에 주입
}
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);
// tomcat의 DataSource객체를 찾아서 저장
if(key.startsWith("jndi.")) {
//key = jndi.dataSource / value = java:comp/env/jdbc/studydb
objTable.put(key, ctx.lookup(value));
} else{
// 나머지 클래스들은 직접 객체를 생성한다
objTable.put(key, Class.forName(value).newInstance());
}
}
}
private void injectDependency() throws Exception{
for(String key : objTable.keySet()) {
if(!key.startsWith("jndi.")) {
// 객체를 callSetter
callSetter(objTable.get(key));
}
}
}
private void callSetter(Object obj) throws Exception{
Object dependency = null;
// obj에는 properties의 객체들이 저장되어 있음. 거기서 setter메서드를 찾기
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) {
// ex) 로그인컨트롤러, memberDao
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
package spms.listeners;
import java.sql.Connection;
import java.sql.DriverManager;
import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import spms.context.ApplicationContext;
import spms.controls.*;
import spms.dao.MemberDao;
import spms.dao.MySqlMemberDao;
import spms.util.DBConnectionPool;
/* JNDI
* WAS(Web Application Server)의 리소스(자원)에 대한 고유 이름 정의
* 어플리케이션에서 서버의 리소스를 접근할 때 사용하는 명명 규칙
* 1) java:comp/env - 응용 프로그램 환경 항목
* 2) java:comp/env/jdbc - JDBC 데이터 소스
* 3) java:comp/ejb - EJB 컴포넌트
* 4) java:comp/UserTransaction - UserTransaction 객체
* 5) java:comp/env/mail - JavaMail 연결 객체
* 6) java:comp/env/url - URL 정보
* 7) java:comp/env/jms - JMS 연결 객체
* */
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"));
// propertiesPath : properties파일의 경로
// 생성자에 넣음으로써 ApplicationContext에서는 파일에 등록된 객체들을 생성할 것이다.
applicationContext = new ApplicationContext(propertiesPath);
}catch(Exception e) {
e.printStackTrace();
}
}
}
web.xml
: contextConfigLocation 추가
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
<display-name>_12_JDBCServlet_ServletInitParam</display-name>
<!-- 컨텍스트 초기화 매개변수(모든 서블릿에서 공유가능) -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/application-context.properties</param-value>
</context-param>
<!-- 웹 어플리케이션 시작/종료 이벤트 리스너 -->
<listener>
<listener-class>spms.listeners.ContextLoaderListener</listener-class>
</listener>
<!-- Servlet 2.4이전에는 '컨텍스트 초기화 매개변수' 다음에 위치해야 한다
2.4 이상부터는 순서에 관계 없다 -->
<!-- 필터 설정 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>spms.filters.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<!-- 모든 주소에 필터를 적용 -->
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!-- 들어오는 모든 것에 이 필터를 적용 (특정 주소만 주면 그 주소만 ) -->
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- tomcat 서버의 DataSource를 사용하기 위한 설정 -->
<!-- DataSource 설정은 Servers/context.xml에 있음 -->
<resource-ref>
<res-ref-name>jdbc/studydb</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
controller, jsp 만들고 properties 만들기
class 만들 때 setter 메서드(Dao)는 꼭 만들어주기