Controller
페이지 컨트롤러에서 servlet 제거하기 (종속 끊기)
req, resp, session 등을 이용하지 않는다
-->model 사용
Controller Interface
package spms.controls;
import java.util.Map;
/*
1) Front Controller에서 Page Controller를 동일한 방식으로 호출하기 위해
정의한 인터페이스
2) 모든 page Controller는 이 인터페이스를 상속받는다
3) Page Controller의 Servlet 종속을 제거하고 POJO 방식으로 만든다
*/
public interface Controller {
public String execute(Map<String, Object> model) throws Exception;
}
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.dao.MemberDao;
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");
MemberDao memberDao = new MemberDao();
memberDao.setDataSource(ds);
// conn객체 대신 memberDao객체를 공유
sc.setAttribute("memberDao", memberDao);
}catch(Exception e) {
e.printStackTrace();
}
}
}
DispatcherServlet
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.controls.Controller;
import spms.controls.LogInController;
import spms.controls.LogOutController;
import spms.controls.MemberAddController;
import spms.controls.MemberDeleteController;
import spms.controls.MemberListController;
import spms.controls.MemberUpdateController;
import spms.vo.Member;
/* 1) 의미 : tomcat으로 부터 바로 '제어 controller'로 전송하던 방식
* -> * tomcat은 Front controller로 전송
* Front controller : DispatcherServlet
* * Front controller는 PageController로 분기
* back controller : PageController
* 역할을 분할
* 2) 이유 :
* 2-1) 역할을 보다 상세히 나눠서 규격화/자동화(Framework)하려고 한다
* 2-2) 스프링 프레임워크의 제작 방식
* 2-3) Front Controller만 HttpServlet의 상속을 받고
* Page Controller는 일반 자바 클래스로 전환(POJO-Plain Old Java Object)
*
* 3) Front Controller(DispatcherServlet)의 역할
* 3-1) tomcat(서블릿 컨테이너)과 연결되어 다른 Page Controller에 분기
* 3-2) Page Controller에 전송할 VO 객체 생성 (향후 자동화)
* 3-3) 서블릿/jsp 이동 처리
* 3-4) Page Controller에서 발생하는 예외처리
*
* 4) 장점
* 4-1) 다른 WAS 이삭할 때 Front Controller만 수정하면 된다
* (Front Controller만 HttpServlet을 상속받으므로)
* 4-2) 브라우저한테 노출하고 싶은 않은 페이지를 감출 수 있다
* 4-3) Page Controller가 하는 공통 역할을 Front Controller에 담았으므로
* 자동화(Framework)하기 편리하다
*
*
* */
@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 {
Controller pageController = null;
//sc에 memberDao를 set 해놨으므로 가져와야 한다
ServletContext sc = this.getServletContext();
HashMap<String, Object> model =
new HashMap<String, Object>();
model.put("memberDao", sc.getAttribute("memberDao"));
model.put("session", request.getSession());
if("/member/list.do".equals(servletPath)) {
// 서블릿 Page Controller -> POJO Page Controller 전환
pageController = new MemberListController();
}else if("/member/add.do".equals(servletPath)) {
pageController = new MemberAddController();
// 아래 조건이 성립하지 않으면 doGet요청(입력 페이지 전송)
if(request.getParameter("email")!=null) {
// doPost (추가할 새로운 데이터가 존재할 때)
model.put("member", new Member()
.setEmail(request.getParameter("email"))
.setPassword(request.getParameter("password"))
.setName(request.getParameter("name")));
}
}else if("/member/update.do".equals(servletPath)) {
pageController = new MemberUpdateController();
// 아래 조건이 성립하지 않으면 doGet(상세정보보기)
if(request.getParameter("email")!=null) {
// doPost(수정데이터 db에 반영)
model.put("member", new Member()
.setNo(Integer.parseInt(request.getParameter("no")))
.setEmail(request.getParameter("email"))
.setName(request.getParameter("name")));
} else {
model.put("no", new Integer(request.getParameter("no")));
}
}else if("/member/delete.do".equals(servletPath)) {
pageController = new MemberDeleteController();
model.put("no", new Integer(request.getParameter("no")));
}else if("/auth/login.do".equals(servletPath)) {
pageController = new LogInController();
if(request.getParameter("email")!=null) {
model.put("loginInfo", new Member()
.setEmail(request.getParameter("email"))
.setPassword(request.getParameter("password")));
}
}else if("/auth/logout.do".equals(servletPath)) {
pageController = new LogOutController();
}
String viewUrl = "";
// POJO Page Controller
if(pageController != null) {
//execute()에서 반환된 주소를 viewUrl에 대입
viewUrl = pageController.execute(model);
//map에 있는 keySet을 가져와서 key 변수에 대입
for(String key : model.keySet())
//request 공간에 key와 value를 저장 -> 저장된 정보를 가지고 페이지 이동을 해야하니까
//jsp는 servlet을 활용해야 하기 때문에 (java로 만들 수 없으니까) request에 저장할 수 밖에 없다.
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);
}
}
}
MemberListController
package spms.controls;
import java.util.Map;
import spms.dao.MemberDao;
public class MemberListController implements Controller {
@Override
public String execute(Map<String, Object> model) throws Exception {
MemberDao memberDao = (MemberDao)model.get("memberDao");
//멤버들의 정보를 담은 members 객체를 model에 넣는다
model.put("members", memberDao.selectList());
return "/member/MemberList.jsp";
}
}
MemberUpdateController
package spms.controls;
import java.util.Map;
import spms.dao.MemberDao;
import spms.vo.Member;
public class MemberUpdateController implements Controller {
@Override
public String execute(Map<String, Object> model) throws Exception {
MemberDao memberDao = (MemberDao)model.get("memberDao");
//get
if(model.get("member") == null) {
Integer no = (Integer)model.get("no");
Member member = memberDao.selectOne(no);
model.put("member", member);
return "/member/MemberUpdateForm.jsp";
//post
}else {
Member member = (Member)model.get("member");
memberDao.update(member);
return "redirect:list.do";
}
}
}
MemberDeleteController
package spms.controls;
import java.util.Map;
import spms.dao.MemberDao;
public class MemberDeleteController implements Controller {
@Override
public String execute(Map<String, Object> model) throws Exception {
MemberDao memberDao = (MemberDao)model.get("memberDao");
Integer no = (Integer)model.get("no");
memberDao.delete(no);
return "redirect:list.do";
}
}
LogInController
package spms.controls;
import java.util.Map;
import javax.servlet.http.HttpSession;
import spms.dao.MemberDao;
import spms.vo.Member;
public class LogInController implements Controller {
@Override
public String execute(Map<String, Object> model) throws Exception {
if(model.get("loginInfo") == null) {
return "/auth/LogInForm.jsp";
// post 방식
}else {
MemberDao memberDao = (MemberDao)model.get("memberDao");
Member loginInfo = (Member)model.get("loginInfo");
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";
}
}
}
}
LogOutController
package spms.controls;
import java.util.Map;
import javax.servlet.http.HttpSession;
public class LogOutController implements Controller {
@Override
public String execute(Map<String, Object> model) throws Exception {
HttpSession session = (HttpSession)model.get("session");
session.invalidate();
return "redirect:login.do";
}
}
DI_DAO
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.MemberDao;
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");
MemberDao memberDao = new MemberDao();
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();
}
}
}
MemberListController
package spms.controls;
import java.util.Map;
import spms.dao.MemberDao;
public class MemberListController implements Controller {
/* DI(Dependency Injection)으로 변경한 이유
* 1) 클래스간의 의존성을 낮추기 위해
* 2) MemberDao 인터페이스를 선언하고 상속구현함으로써
* 다른 DBMS로의 전환을 용이하게 하려고
* 3) 나중에 변경할 자동화 작업에 사용하려고
* */
MemberDao memberDao = null;
public MemberListController setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
return this;
}
@Override
public String execute(Map<String, Object> model) throws Exception {
//이렇게 꺼내쓰면 의존도가 끈끈해지기 때문에 느슨하게 만들어 준다.
//모든 컨트롤러에서 이부분 지우기
// MemberDao memberDao = (MemberDao)model.get("memberDao");
model.put("members", memberDao.selectList());
return "/member/MemberList.jsp";
}
}
DAO Interface
MemberDao Interface
package spms.dao;
import java.util.List;
import spms.vo.Member;
/* 굳이 MemberDao 인터페이스를 만든 이유는?
*
* 향후 Oracle, SQL-Server, ...... 확장해 나갈때
* 이 인터페이스를 상속받아서 기능은 각 DBMS별로 특성에 맞게 구현하되
* 메서드의 역할/리턴값은 동일하게 해줌으로써
* DBMS의 교체를 매우 쉽게 해준다
* */
public interface MemberDao {
public List<Member> selectList() throws Exception;
public int insert(Member member) throws Exception;
public int delete(int no) throws Exception;
public Member selectOne(int no) throws Exception;
public int update(Member member) throws Exception;
public Member exist(String email, String password) throws Exception;
}
MySqlMemberDao
package spms.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import spms.util.DBConnectionPool;
import spms.vo.Member;
public class MySqlMemberDao implements MemberDao{
DataSource ds = null;
public void setDataSource(DataSource ds) {
this.ds = ds;
}
public List<Member> selectList() throws Exception {
Connection connection = null;
Statement stmt = null;
ResultSet rs = null;
final String sqlSelect = "SELECT mno,mname,email,cre_date" + "\r\n" + "FROM members" + "\r\n"
+ "ORDER BY mno ASC";
try {
// 커넥션풀에서 Connection객체를 빌려온다
connection = ds.getConnection();
stmt = connection.createStatement();
rs = stmt.executeQuery(sqlSelect);
ArrayList<Member> members = new ArrayList<Member>();
while (rs.next()) {
members.add(new Member().setNo(rs.getInt("mno")).setName(rs.getString("mname"))
.setEmail(rs.getString("email")).setCreatedDate(rs.getDate("cre_date")));
}
return members;
} catch (Exception e) {
throw e;
} finally {
try {
if (rs != null)
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
if (stmt != null)
stmt.close();
} catch (Exception e) {
e.printStackTrace();
}
/* ds에서 제공하는 Connection객체의 close()의 의미는
* 연결을 종료하는 것이 아니라
* 객체를 ds내부의 커넥션 풀에 반납한다는 의미이다
* */
try {
if(connection != null)
connection.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
public int insert(Member member) throws Exception {
Connection connection = null;
int result = 0;
PreparedStatement stmt = null;
final String sqlInsert = "INSERT INTO members(email,pwd," + "\r\n" + "mname,cre_date,mod_date)" + "\r\n"
+ "VALUES(?, ?, ?, NOW(), NOW())";
try {
// 커넥션풀에서 Connection객체를 빌려온다
connection = ds.getConnection();
stmt = connection.prepareStatement(sqlInsert);
stmt.setString(1, member.getEmail());
stmt.setString(2, member.getPassword());
stmt.setString(3, member.getName());
result = stmt.executeUpdate();
} catch (Exception e) {
throw e;
} finally {
try {
if (stmt != null)
stmt.close();
} catch (Exception e) {
e.printStackTrace();
}
// 다 썼으면 반납하자
try {
if(connection != null)
connection.close();
}catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
public int delete(int no) throws Exception {
Connection connection = null;
int result = 0;
Statement stmt = null;
final String sqlDelete = "DELETE FROM MEMBERS WHERE MNO=";
try {
// 커넥션풀에서 Connection객체를 빌려온다
connection = ds.getConnection();
stmt = connection.createStatement();
result = stmt.executeUpdate(sqlDelete + no);
} catch (Exception e) {
throw e;
} finally {
try {
if (stmt != null)
stmt.close();
} catch (Exception e) {
e.printStackTrace();
}
// 다 썼으면 반납하자
try {
if(connection != null)
connection.close();
}catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
public Member selectOne(int no) throws Exception {
Connection connection = null;
Member member = null;
Statement stmt = null;
ResultSet rs = null;
final String sqlSelectOne = "SELECT MNO,EMAIL,MNAME,CRE_DATE FROM MEMBERS" + " WHERE MNO=";
try {
// 커넥션풀에서 Connection객체를 빌려온다
connection = ds.getConnection();
stmt = connection.createStatement();
rs = stmt.executeQuery(sqlSelectOne + no);
if (rs.next()) {
member = new Member().setNo(rs.getInt("MNO")).setEmail(rs.getString("EMAIL"))
.setName(rs.getString("MNAME")).setCreatedDate(rs.getDate("CRE_DATE"));
} else {
throw new Exception("해당 번호의 회원을 찾을 수 없습니다.");
}
} catch (Exception e) {
throw e;
} finally {
try {
if (rs != null)
rs.close();
} catch (Exception e) {
}
try {
if (stmt != null)
stmt.close();
} catch (Exception e) {
}
// 다 썼으면 반납하자
try {
if(connection != null)
connection.close();
}catch (Exception e) {
e.printStackTrace();
}
}
return member;
}
public int update(Member member) throws Exception {
Connection connection = null;
int result = 0;
PreparedStatement stmt = null;
final String sqlUpdate = "UPDATE MEMBERS SET EMAIL=?,MNAME=?,MOD_DATE=now()" + " WHERE MNO=?";
try {
// 커넥션풀에서 Connection객체를 빌려온다
connection = ds.getConnection();
stmt = connection.prepareStatement(sqlUpdate);
stmt.setString(1, member.getEmail());
stmt.setString(2, member.getName());
stmt.setInt(3, member.getNo());
result = stmt.executeUpdate();
} catch (Exception e) {
throw e;
} finally {
try {
if (stmt != null)
stmt.close();
} catch (Exception e) {
}
// 다 썼으면 반납하자
try {
if(connection != null)
connection.close();
}catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
public Member exist(String email, String password) throws Exception {
Connection connection = null;
Member member = null;
PreparedStatement stmt = null;
ResultSet rs = null;
final String sqlExist = "SELECT MNAME,EMAIL FROM MEMBERS" + " WHERE EMAIL=? AND PWD=?";
try {
// 커넥션풀에서 Connection객체를 빌려온다
connection = ds.getConnection();
stmt = connection.prepareStatement(sqlExist);
stmt.setString(1, email);
stmt.setString(2, password);
rs = stmt.executeQuery();
if (rs.next()) {
member = new Member().setName(rs.getString("MNAME")).setEmail(rs.getString("EMAIL"));
} else {
return null;
}
} catch (Exception e) {
throw e;
} finally {
try {
if (rs != null)
rs.close();
} catch (Exception e) {
}
try {
if (stmt != null)
stmt.close();
} catch (Exception e) {
}
try {
if(connection != null)
connection.close();// 다 썼으면 반납하자
}catch (Exception e) {
e.printStackTrace();
}
}
return member;
}
}