[ Singleton Design Pattern ]
: 시스템 상에서 객체를 단 한번 생성해서, 여러 곳에서 공유해서 사용하는 방식
(*참고 : Spring Framework에서는 기본 객체 운용방식이 singleton이다)
[ 적용 방안 ]
- private 생성자로 명시해 외부에서 객체 생성하는 것을 방지한다.
- Class Loading 시 ( Class 당 한 번 실행) ,
static 영역이 method area(class area)에 초기화 되는 것을 이용해
static 변수로 단 한번 객체를 생성한다. - 외부에서 단 한번 만든 객체를 사용하게 하기 위해,
public static 메서드로 객체의 주소값을 반환하도록 정의한다.
간단 적용 예제
Company class에 Singleton Design Pattern을 적용해본다.
/TestSingleton.java
package test;
//Company Class
class Company {
private String companyInfo = "회사 정보";
// 2. 생성 방법 1 - class loading시 단 한번 실행된다.
private static Company instance = new Company();
// 1. 생성자에 private을 명시해 외부에서 생성하는 것을 방지한다
private Company () {
System.out.println("Company 객체 생성");
}
// 3. 외부에서 사용할 수 있게 public static method를 instance로 반환한다.
public static Company getInstance() {
return instance;
}
// 사용자가 서비스 받을 메서드
public String getCompanyInfo() {
return companyInfo;
}
}
//Main Class
public class TestSingleton {
public static void main(String[] args) {
// private 생성자 이므로, error : 외부에서 객체 생성 할 수 없다
// Company c1 = new Company();
// 생성은 할 수 없지만, 사용할 수는 있다!
// static method는 [class명].[method명] 으로 접근한다
Company c1 = Company.getInstance();
System.out.println(c1); //test.Company@7852e922
Company c2 = Company.getInstance();
System.out.println(c2); //test.Company@7852e922 -- 동일한 주소값
// Company class의 인스턴스 변수(companyInfo) 가져오기
String companyInfo = c2.getCompanyInfo();
System.out.println(companyInfo); //회사 정보
System.out.println(Company.getInstance().getCompanyInfo()); //회사 정보
}
}
[ Model2 MVC 상품관리 예제 ]
DAO에 Singleton을 적용
Singleton Pattern으로 Model계층의 DAO에 적용시켜,
불필요하게 객체를 다수 생성하는 것을 방지하고 &
클래스 로딩 시, 단 한번의 객체를 생성해서 여러 컨트롤러에서 사용하도록 했다.
1. web_product TABLE의 총 상품 수 조회하기
index.jsp -- request -- ProductTotalCountServlet < -- > ProductDAO
ㅣ
forward 방식으로 이동
ㅣ
product-totalcount.jsp
2. id 로 상품 검색하기
index.jsp -- request -- FindProductByIdServlet < -- > ProductDAO.findProductById(id) : ProductVO
ㅣ
forward 방식으로 이동
ㅣ
ㅣ 상품 있으면 ㅣ 상품 없으면
find_ok.jsp find_falil.jsp
(테이블로 상품정보 조회)
3. 상품 리스트 전체 조회하기
전체 상품을 검색해서 product-list.jsp에서 상품 목록을 보여준다.
(*단, 아이디와 상품명만 보여준다! + 상품목록은 아이디 내림차순 )
SQL
web_product TABLE 생성 및 INSERT
CREATE TABLE web_product(
id NUMBER PRIMARY KEY,
name VARCHAR2(100) NOT NULL,
maker VARCHAR2(100) NOT NULL,
price NUMBER NOT NULL
)
CREATE SEQUENCE web_product_seq nocache;
INSERT INTO web_product VALUES(web_product_seq.nextval, '카스', '두산', 1500);
INSERT INTO web_product VALUES(web_product_seq.nextval, '테라', '진로', 1700);
INSERT INTO web_product VALUES(web_product_seq.nextval, '참이슬', '진로', 1300);
commit
Model
DAO class에 Singleton Design Pattern을 적용해보자!
/ProductDAO.java
package model;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
/*
* DAO class에 Singleton Design Pattern을 적용해보자!
* 1. private 생성자
* 2. private static 변수에 자신의 객체를 생성
* 3. public static method(getInstance())로 공유
*/
public class ProductDAO {
//2. private static 변수에 자신의 객체를 생성
// --> 딱 한 번만 실행됨 !
private static ProductDAO instance = new ProductDAO();
// JDBC (DB 연결)에 필요한 변수
private String driver = "oracle.jdbc.OracleDriver";
private String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
private String userName = "scott";
private String userPassword = "tiger";
//1. private 생성자
// 이 단계에서 Driver Loading 해줌
private ProductDAO() {
try {
Class.forName(driver);
System.out.println("ProductDAO 객체 생성");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//3. public static method(getInstance())로 공유
/* public static [return type] [method 명] () {
* return [DAO object-instance];
* }
*/
public static ProductDAO getInstance() {
return instance;
}
/* getAllMemberCount() method
* 서비스에 제공할 메서드 : 전체 상품 수 반환
*/
public int getAllMemberCount() throws SQLException {
int count = 0;
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
con = DriverManager.getConnection(url,userName , userPassword);
String sql = "SELECT COUNT(*) FROM web_product";
pstmt = con.prepareStatement(sql);
rs = pstmt.executeQuery();
if (rs.next()) {
count = rs.getInt(1);
}
closeAll(rs, pstmt, con);
return count;
}
/* findProductById() method
* 아이디에 맞는 상품 정보(ProductVO) 제공하는 메서드
*/
public ProductVO findProductById(String id) throws SQLException {
ProductVO vo = null;
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = DriverManager.getConnection(url, userName, userPassword);
String sql = "SELECT id, name, maker, price "
+ "FROM web_product "
+ "WHERE id = ?";
pstmt = con.prepareStatement(sql);
pstmt.setString(1, id);
rs = pstmt.executeQuery();
if (rs.next()) {
vo = new ProductVO(rs.getString("id"), rs.getString("name"),
rs.getString("maker"), rs.getInt("price"));
}
} finally {
closeAll(rs, pstmt, con);
}
return vo;
}
/* findAllProductList() method
* table에 있는 모든 상품 정보 list로 가져오기
*/
public ArrayList<ProductVO> findAllProductList() throws SQLException{
ArrayList<ProductVO> list = new ArrayList<ProductVO>();
Connection con = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
con = DriverManager.getConnection(url, userName, userPassword);
String sql = "SELECT id, name, maker, price FROM web_product ";
pstmt = con.prepareStatement(sql);
rs = pstmt.executeQuery();
while(rs.next()) {
ProductVO vo = new ProductVO();
vo.setId(rs.getString("id"));
vo.setName(rs.getString("name"));
list.add(vo);
}
} finally {
closeAll(rs, pstmt, con);
}
return list;
}
/* closeAll() method
* JDBD용 closeAll 메서드
*/
public void closeAll(ResultSet rs, PreparedStatement pstmt,
Connection con) throws SQLException {
if (rs!=null)
rs.close();
if (pstmt!=null)
pstmt.close();
if (con!=null)
con.close();
}
}
/ProductVO.java
package model;
public class ProductVO {
private String id;
private String name;
private String maker;
private int price;
public ProductVO() {
super();
}
public ProductVO(String id, String name, String maker, int price) {
super();
this.id = id;
this.name = name;
this.maker = maker;
this.price = price;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMaker() {
return maker;
}
public void setMaker(String maker) {
this.maker = maker;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
View
/index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Model2 MVC 상품관리</title>
</head>
<body>
<%--
index.jsp -- request -- ProductTotalCountServlet < -- > ProductDAO
ㅣ
forward 방식으로 이동
ㅣ
product-totalcount.jsp
--%>
<ul>
<li><a href = "ProductTotalCountServlet">전체 상품 수 조회</a></li>
</ul>
</body>
</html>
1. 총 상품 수 조회 jsp (View)
/product-totalcount.jsp
<%@page import="model.ProductDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>전체 상품 수 조회</title>
</head>
<body bgcolor="yellow">
<a href = "index.jsp">Home</a>
<hr>
<%--
Controller(ProductTotalCountServlet)에서
totalcount를 받아옴
--%>
전체 상품 수 : <%=request.getAttribute("totalcount") %>
</body>
</html>
2. 아이디로 상품 검색 jsp (View)
/find-product.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>상품 검색</title>
</head>
<body>
<form action="FindProductByIdServlet">
상품 아이디 <input type="number" name="productId" required="required">
<input type="submit" value="검색">
</form>
</body>
</html>
/find-ok.jsp
<%@page import="model.ProductVO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<link type="text/css" rel="stylesheet" href="css/mystyle.css">
<head>
<meta charset="UTF-8">
<title>상품 상세 정보</title>
</head>
<body>
<table>
<thead>
<tr>
<th>아이디</th>
<th>이름</th>
<th>상표</th>
<th>가격</th>
</tr>
</thead>
<tbody>
<tr>
<% ProductVO vo = (ProductVO) request.getAttribute("vo"); %>
<td><%=vo.getId() %></td>
<td><%=vo.getName() %></td>
<td><%=vo.getMaker() %></td>
<td><%=vo.getPrice() %></td>
</tr>
</tbody>
</table>
</body>
</html>
/find-fail.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>오류 페이지</title>
</head>
<body>
<script type="text/javascript">
alert("상품 정보가 없습니다.");
location.href = "find-product.jsp";
</script>
</body>
</html>
3. 전체 상품 리스트 조회하기 jsp (View)
/product-list.jsp
<%@page import="model.ProductVO"%>
<%@page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<link type="text/css" rel="stylesheet" href="css/mystyle.css">
<head>
<meta charset="UTF-8">
<title>상품 목록</title>
</head>
<body>
<%
@SuppressWarnings("unchecked")
ArrayList<ProductVO> list
= (ArrayList<ProductVO>) request.getAttribute("list");
%>
<table>
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<tr>
</thead>
<tbody>
<%-- 아래에 있는 상품명을 클릭하면 상품 상세정보가 조회된다. --%>
<% for (ProductVO vo:list) { %>
<tr>
<td><%=vo.getId() %></td>
<td><a href = "FindProductByIdServlet?productId=<%=vo.getId()%>">
<%=vo.getName()%></a></td>
</tr>
<% } %>
</tbody>
</table>
</body>
</html>
Controller
1. 총 상품 수 조회 Serlvet (Controller)
/ProductTotalCountServlet.java - Serlvet
package model;
import java.io.IOException;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/ProductTotalCountServlet")
public class ProductTotalCountServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// Model과 연동 (dao에서 service가 실행)
ProductDAO dao = ProductDAO.getInstance();
// count : 전체 상품 수
int count;
try {
count = dao.getAllMemberCount();
// 연동 결과 View에 공유 (-> product-totalcount.jsp)
// request.serAttrubute(name, value)
request.setAttribute("totalcount", count);
} catch (SQLException e) {
e.printStackTrace();
}
//forward 방식으로 view로 이동
// (product-totalcount.jsp)
request.getRequestDispatcher("product-totalcount.jsp").forward(request, response);
}
}
2. id로 상품 정보 검색하기 Serlvet (Controller)
/FindProductByIdServlet.java - Serlvet
package step2;
import java.io.IOException;
import java.sql.SQLException;
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 model.ProductDAO;
import model.ProductVO;
@WebServlet("/FindProductByIdServlet")
public class FindProductByIdServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// find-product.jsp에서 id를 받아서
String id = request.getParameter("productId");
// ProductDAO에 접근해서 ProductVO 받아오기
// Model과 연동 (dao에서 service가 실행)
try {
ProductVO vo = ProductDAO.getInstance().findProductById(id);
String viewName = null;
// 연동 결과 View에 공유 & View로 이동
if (vo == null) { // id가 없으면(-> find_fail.jsp)
viewName = "find_fail.jsp";
} else { // id가 있으면 정보 공유(-> find_ok.jsp)
viewName = "find_ok.jsp";
request.setAttribute("vo", vo);
}
request.getRequestDispatcher(viewName).forward(request, response);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3. 전체 상품 리스트 조회하기 Serlvet (Controller)
/ProductListServlet.java - Serlvet
package step3;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
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 model.ProductDAO;
import model.ProductVO;
@WebServlet("/ProductListServlet")
public class ProductListServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// ProductDAO에 접근해서 ProductVO 받아오기
// Model과 연동 (dao에서 service가 실행)
ArrayList<ProductVO> list = null;
try {
list = ProductDAO.getInstance().findAllProductList();
} catch (SQLException e) {
e.printStackTrace();
}
// 연동 결과 View에 공유
request.setAttribute("list", list);
//View로 이동
request.getRequestDispatcher("product-list.jsp").forward(request, response);
}
}
index.jsp 브라우저 화면 (초기화면)
1. 총 상품 수 조회 결과 화면
2. id로 상품 정보 검색 화면 ( find-product.jsp )
2_1. 있는 id를 검색했을 때( find_ok.jsp) 결과 화면
2_1. 없는 id를 검색했을 때 (find_fail.jsp) 결과 화면 -> 다시 find-product.jsp 화면으로 감
3. 전체 상품 리스트 테이블 조회 결과 화면
3_1. 이름 클릭하면 상세정보 나온다
'Java Web Programming > 4. JSP' 카테고리의 다른 글
[JSP/Model2] 2단계 _ Model2 FrontController + MVC + Singleton Pattern (2) | 2020.08.28 |
---|---|
[JSP/Model2] Model2 basic / 1단계_Model2 MVC pattern + Singleton pattern (forward 방식, redirect 방식) (0) | 2020.08.28 |
[JSP/Model2] Model 2 Architecture ( MVC Pattern ) / Model 1 설계방식과 차이점 ? (0) | 2020.08.27 |
[JSP] Model 1 Architecture (설계방식) (0) | 2020.08.27 |
[JSP] JSP와 DB 연동 - basic (0) | 2020.08.26 |