- 서블릿 배치와 <load-on-startup> 태그
AppInitServlet 클래스의 배치 정보를 작성한다.
DD파일(web.xml)에 다음과 같이 AppInitServlet 클래스의 배치 정보를 추가한다.
<!-- 서블릿 선언 -->
<servlet>
<servlet-name>AppInitServlet</servlet-name>
<servlet-class>spms.servlets.AppInitServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
컨텍스트 매개변수 태그 아래에 서블릿을 선언하는 태그를 추가했다. 여기서 주목할 사항은 <load-on-startup> 태그이다.
<load-on-startup>1</load-on-startup>
서블릿 객체는 클라이언트의 최초 요청시 생성된다. 클라이언트가 단 한 번도 요청하지 않으면 그 서블릿은 생성되지 않는다. 그러나 AppInitServlet 클래스처럼 다른 서블릿이 생성되기 전에 미리 준비 작업을 해야 하는 서블릿이라면, 클라이언트 요청이 없더라도 생성되어야 한다. <load-on-startup> 태그는 바로 그런 용도의 서블릿을 배치할 때 사용한다. 서블릿을 배치할 때 <load-on-startup>태그를 지정하면, 해당 서블릿은 웹 애플리케이션이 시작될 때 자동으로 생성된다. 이 태그의 값은 생성 순서이다. 생성 순서가 같은 서블릿이 여러 개 있을 경우, 먼저 선언된 것이 먼저 생성된다.
※ URL 매핑이 없는 서블릿
web.xml을 보면 AppInitServlet 클래스에 대해 <servlet-mapping> 태그가 없다. 즉 AppInitServlet 클래스에 대해 URL을 지정하지 않았다. 이렇게 URL 매핑 정보가 없는 서블릿은 클라이언트에서 실행을 요청할 수 없다. AppInitServlet 클래스처럼 다른 서블릿을 위해 준비 작업을 수행하는 서블릿인 경우 URL을 지정하지 않는다.
- ServletContext에 저장된 DB 커넥션 사용
이제 서블릿에서 DB 커넥션을 직접 준비할 필요가 없이 ServletContext 보관소에 저장된 DB 커넥션 객체를 꺼내 쓰면 된다.
회원 목록을 처리하는 MemberListServlet 클래스를 수정한다.
package spms.servlets;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import javax.servlet.GenericServlet;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import spms.vo.Member;
//UI 출력 코드를 제거하고, UI 생성 및 출력을 JSP에게 위임한다.
@WebServlet("/member/list")
public class MemberListServlet extends HttpServlet {//extends GenericServlet
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
//데이터베이스 관련 코드를 위한 try ~ catch 블록 (JDBC API 사용시 예외가 발생할 수 있기 때문)
try {
//JDBC 드라이버를 로딩하고 데이터베이스 연결 시 컨텍스트 초기화 매개변수에서 해당 정보 가져와 처리.
ServletContext sc = this.getServletContext();
//DB 커넥션 준비하는 코드 삭제
/*Class.forName(sc.getInitParameter("driver"));
conn = DriverManager.getConnection(
sc.getInitParameter("url"),
sc.getInitParameter("username"),
sc.getInitParameter("password"));*/
//ServletContext에 저장된 DB 커넥션 사용
conn = (Connection) sc.getAttribute("conn");
stmt = conn.createStatement();
rs = stmt.executeQuery(
"SELECT MNO,MNAME,EMAIL,CRE_DATE" +
" FROM MEMBERS" +
" ORDER BY MNO ASC");
response.setContentType("text/html; charset=UTF-8");
/*
PrintWriter out = response.getWriter();
out.println("<html><head><title>회원목록</title></head>");
out.println("<body><h1>회원목록</h1>");
out.println("<p><a href='add'>신규 회원</a></p>");
while(rs.next()) {
out.println(
rs.getInt("MNO") + "," + //getInt(1)
"<a href='update?no=" + rs.getInt("MNO") + "'>" + //회원 상세정보 출력 서블릿 URL 추가
rs.getString("MNAME") + "</a>," + //getString(2)
rs.getString("EMAIL") + "," + //getString(3)
//rs.getDate("CRE_DATE") + "<br>" //getDate(4)
rs.getDate("CRE_DATE") +
"<a href='delete?no=" + rs.getInt("MNO") + "'>[삭제]</a><br>"
);
}
out.println("</body></html>");
*/
ArrayList<Member> members = new ArrayList<Member>();
//데이터베이스에서 회원 정보를 가져와 Member에 담는다.
//그리고 Member객체를 ArrayList에 추가한다.
while(rs.next()) {
members.add(new Member().setNo(rs.getInt("MNO"))
.setName(rs.getString("MNAME"))
.setEmail(rs.getString("EMAIL"))
.setCreatedDate(rs.getDate("CRE_DATE")) );
}
//request에 회원 목록 데이터 보관한다.
request.setAttribute("members", members);
//JSP로 출력을 위임한다.
RequestDispatcher rd = request.getRequestDispatcher("/member/MemberList.jsp");
rd.include(request, response); //인클루딩
} catch (Exception e) {
//throw new ServletException(e);
request.setAttribute("error", e); //예외 객체를 request에 보관
RequestDispatcher rd = request.getRequestDispatcher("/Error.jsp");
rd.forward(request, response); //포워딩
} finally {
try {if (rs != null) rs.close();} catch(Exception e) {}
try {if (stmt != null) stmt.close();} catch(Exception e) {}
//DB 커넥션 객체를 서블릿에서 관리하지 않으므로 삭제
//try {if (conn != null) conn.close();} catch(Exception e) {}
}
}
}
MemberListServlet 클래스에서 DB 커넥션을 준비하는 코드를 삭제했다.
/*Class.forName(sc.getInitParameter("driver"));
conn = DriverManager.getConnection(
sc.getInitParameter("url"),
sc.getInitParameter("username"),
sc.getInitParameter("password"));*/
DB 커넥션 객체를 서블릿에서 관리하지 않으므로 finally 블록에 있는 con.close() 명령문도 삭제한다.
//try {if (conn != null) conn.close();} catch(Exception e) {}
이제 서블릿은 ServletContext 보관소에서 DB 커넥션을 꺼낸다.
//ServletContext에 저장된 DB 커넥션 사용
conn = (Connection) sc.getAttribute("conn");
톰캣 서버를 재시작하고, http://localhost:9999/web05/member/list를 테스트한다(그림 1).
톰캣 서버의 콘솔창에 출력된 내용을 보면, AppInitServlet 클래스의 init() 메서드가 호출되었음을 확인할 수 있다. init() 메서드가 호출되었다는 것은 AppInitServlet 객체가 생성되었다는 의미이다. 웹 브라우저의 실행 결과는 이전과 같이 정상적으로 출력된다(그림 2).
나머지 서블릿(MemberAddServlet, MemberDeleteServlet, MemberUpdateServlet)들도 MemberListServlet 클래스처럼 변경했다.
참고도서 : https://freelec.co.kr/book/1674/
[열혈강의] 자바 웹 개발 워크북
[열혈강의] 자바 웹 개발 워크북
freelec.co.kr
'교재 실습 > 자바 웹 개발 워크북' 카테고리의 다른 글
48. 데이터 보관소 (4) (0) | 2022.06.19 |
---|---|
47. 데이터 보관소 (3) (0) | 2022.06.18 |
45. 데이터 보관소 (1) (0) | 2022.06.15 |
44. 포워딩과 인클루딩 (2) (0) | 2022.06.14 |
43. 포워딩과 인클루딩 (1) (0) | 2022.06.13 |
댓글