02-2 MVC
javaEE的設計模式
歷史沿革:
1. model1(jsp + javaBean)
jsp承擔了給使用者顯示和處理內部邏輯的任務,不安全,也不利於維護
2. model2 -> MVC(今天的主題)
Servlet + jsp + javaBean
關於model1和model2,這篇文章博主寫的很詳細:https://www.cnblogs.com/wzjhoutai/p/6829457.html
MVC
mvc是 一套設計模式, 裡面主要是裝飾模式
M - Model 模型 : javabean
V - View 檢視 : jsp -> 負責單純的頁面顯示
C - Controller 控制器 Servlet -> 處理使用者請求 / 獲取資料 / 呼叫業務模型, 拿到資料模型 / 傳遞資料 / 指定用哪個檢視顯示
MVC和三層架構的關係?
沒啥關係
三層架構: web層 + service層 + dao層
MVC 發生在web層
mvc小專案實戰:
實現如圖所示購物網站
要求如下:
1.mvc設計模式
2.有分頁效果
3.有商品詳細資訊頁
商品詳細資訊頁:
根據mvc設計模式思想,得出以下程式流程圖
設計的分頁效果流程圖
分頁效果的主要思想是建立一個泛型類PageBean,用於存放任何型別的商品資訊以及在資料庫中的記錄數等
package com.wowowo.bean; import java.util.List; public class PageBean<T> { // 存放當前頁數 private int pageNum; // 存放每頁最大記錄數 private int pageSize; // 存放從哪一條記錄開始讀取,用於查詢語句limit中 private int pageStart; // 存放記錄總數,用於末頁按鈕 private int pageMaxNum; // 存放最大頁數,用於末頁按鈕 private int pageMaxSize; private List<T> data; public PageBean(int pageNum, int pageSize, int pageMaxNum) { super(); // 構造方法傳三個成員屬性,其餘2個成員屬性通過計算得出 this.pageNum = pageNum; this.pageSize = pageSize; this.pageMaxNum = pageMaxNum; // 最大頁數通過記錄總數加上每頁記錄-1除以每頁記錄獲得 // 1.每頁5條 11條:3頁=(15+5-1)/5; 15條:3頁=(11+5-1)/5 pageMaxSize = (pageMaxNum + pageSize - 1) / pageSize; // 另一種計算最大頁數方法 // 先整除 // pageMaxSize=pageMaxNum/pageSize; // 有餘數就加1 // if(pageMaxNum%pageSize!=0){ // pageMaxSize++; // } /// 計算在資料庫中執行查詢語句時的初始位置 pageStart = (pageNum - 1) * pageSize; } //get, set,tostring方法略 }
使用者訪問首頁,預設首頁(localhost:8080/my1023hw)為index.jsp,因為只寫了index.jsp(這個在web.xml配置中)
原因:
<welcome-file-list>用於指定應用的首頁
裡面可以指定多個檔案,應用伺服器會按從上到下的順序搜尋,如果找到就了進入該頁面,如果都遍歷完了還沒找到就會返回404錯誤程式碼給瀏覽器。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>my1023hw</display-name>
<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>
index.jsp(首頁)
使用者訪問首頁,它會請求轉發到goodservlet(goodservlet設定了註解,和它相對映的url為/goods)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%-- 很重要:這個是jsp請求轉發,給後端看的,不用加web專案名 --%>
<jsp:forward page="/goods"></jsp:forward>
</body>
</html>
goodsServlet(首頁):
根據傳入的當前頁數和最大頁數,呼叫service層和dao層方法查詢記錄並返回pagebean
package com.wowowo.web;
import java.io.IOException;
import java.util.List;
import javax.servlet.RequestDispatcher;
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 org.eclipse.jdt.internal.compiler.parser.ParserBasicInformation;
import com.wowowo.bean.PageBean;
import com.wowowo.bean.Product;
import com.wowowo.service.ProductService;
import com.wowowo.service.impl.ProductServiceImpl;
@WebServlet("/goods")
public class GoodsServlet extends HttpServlet {
// 依賴於業務邏輯層
private ProductService ps = new ProductServiceImpl();
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 獲取使用者上傳的pageNum和pageSize
int pageNum = 1;
//pageNum放在了url中,get方法獲取
String s_pageNum = request.getParameter("pageNum");
if (s_pageNum != null) {
pageNum = Integer.parseInt(s_pageNum);
}
int pageSize = 5;
//pageSize放在了url中,get方法獲取
String s_pageSize = request.getParameter("pageSize");
if (s_pageSize != null) {
pageSize = Integer.parseInt(s_pageSize);
}
// 呼叫業務模型 獲取資料模型
PageBean<Product> pageBean = ps.queryAll(pageNum, pageSize);
// 放到request域中
request.setAttribute("pageBean", pageBean);
// 轉發到jsp頁面
request.getRequestDispatcher("/main.jsp").forward(request, response);
}
}
service層(參考架構基礎):
package com.wowowo.service.impl;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.wowowo.bean.PageBean;
import com.wowowo.bean.Product;
import com.wowowo.dao.ProductDao;
import com.wowowo.dao.impl.ProductDaoImpl;
import com.wowowo.database.ConnectionFactory;
import com.wowowo.service.ProductService;
public class ProductServiceImpl implements ProductService {
private ProductDao pd = new ProductDaoImpl();
@Override
public PageBean<Product> queryAll(int pageNum, int pageSize) {
Connection conn = null;
List<Product> list = new ArrayList<>();
try {
conn = ConnectionFactory.getConnection();
// 查詢獲得記錄數
int count = pd.queryCount(conn);
// 構造pageBean物件
PageBean<Product> pageBean = new PageBean<Product>(pageNum, pageSize, count);
//呼叫dao層方法查詢相關記錄,不返回值,在方法裡面對pageBean修改
//傳引用賦值和傳值賦值的區別建議再看看
pd.queryAll(conn, pageBean);
return pageBean;
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return null;
}
// 這個方法用於商品詳情頁面
@Override
public Product queryById(int uid) {
Connection conn = null;
List<Product> list = new ArrayList<>();
try {
conn = ConnectionFactory.getConnection();
return pd.queryById(conn, uid);
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return null;
}
}
dao層用於和資料庫互動(參考架構基礎);
package com.wowowo.dao.impl;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.wowowo.bean.PageBean;
import com.wowowo.bean.Product;
import com.wowowo.dao.ProductDao;
import com.wowowo.database.ConnectionFactory;
public class ProductDaoImpl implements ProductDao {
// 查記錄總數
@Override
public int queryCount(Connection conn) throws SQLException {
String sql = "select count(*)from product";
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
return rs.getInt(1);
}
return 0;
}
// 根據這一頁的的初始記錄位置,和每頁顯示數量查詢相關記錄並組裝成list賦值給pageBean,不用返回
@Override
public void queryAll(Connection conn, PageBean<Product> pageBean) throws SQLException {
List<Product> list = new ArrayList<>();
String sql = "select *from product limit ?,?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, pageBean.getPageStart());
ps.setInt(2, pageBean.getPageSize());
ResultSet rs = ps.executeQuery();
while (rs.next()) {
Product product = new Product();
product.setProdId(rs.getInt("prodId"));
product.setProdName(rs.getString("prodName"));
product.setProdImage(rs.getString("prodImage"));
product.setProdPrice(rs.getDouble("prodPrice"));
product.setProdAmount(rs.getInt("prodAmount"));
list.add(product);
}
pageBean.setData(list);
}
// 通過id查詢商品,用於顯示商品詳情
@Override
public Product queryById(Connection conn, int uid) throws SQLException {
String sql = "select *from product where prodid =?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, uid);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
Product product = new Product();
product.setProdId(rs.getInt("prodId"));
product.setProdName(rs.getString("prodName"));
product.setProdImage(rs.getString("prodImage"));
product.setProdPrice(rs.getDouble("prodPrice"));
product.setProdAmount(rs.getInt("prodAmount"));
return product;
}
return null;
}
}
對我而言,難點在於jsp頁面,畢竟剛學
首頁(main)的jsp(view層)
<%@page import="java.util.ArrayList"%>
<%@page import="java.util.List"%>
<%@page import="com.wowowo.bean.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
rel="stylesheet">
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"></script>
<style type="text/css">
.prod {
border: 1px solid black;
width: 230px;
}
.prod_price {
float: right;
}
</style>
</head>
<body>
<h1 class="text-center">歡迎訪問服裝商城</h1>
<div class="container">
<%
//request域物件裡拿到servlet傳過來的資料模型,強轉,拿出記錄list
PageBean<Product> pageBean = (PageBean<Product>) request.getAttribute("pageBean");
//拿出商品list,遍歷輸出每一條記錄
List<Product> list = pageBean.getData();
for (int i = 0; i < list.size(); i++) {
%>
<div class="prod col-md-3">
<div>
<%-- a標籤的屬性href屬性用於指定超連結目標的URL,在url尾部新增?uid=商品id跳轉到詳細資訊servlet --%>
<a href="/my1023hw/single?uid=<%=list.get(i).getProdId()%>"><img
<%-- 圖片路徑通過jsp指令碼實現拼接 --%>
src="images/<%=list.get(i).getProdImage()%>" /></a>
</div>
<div>
<%
//商品名,也可以用指令碼表示式 <%==list.get(i).getProdName()%》
out.write(list.get(i).getProdName());
%>
<span class="prod_price">價格:<%
//商品價格,也可以用指令碼表示式 <%==list.get(i).getProdName()%》
out.write(String.valueOf(list.get(i).getProdPrice())); %>
</span>
</div>
</div>
<%
}
%>
<%-- 迴圈結束 --%>
</div>
<div style="text-align: center;">
<%--a標籤的 href屬性用於指定超連結目標的 URL,通過在後面新增?k-v&k-v在servlet的get方式能獲取到引數--%>
<%-- 首頁為第一頁,url裡輸入首頁和點選首頁的效果一致,每頁5條記錄 --%>
<a href="goods?pageNum=1&pageSize=5" class="btn btn-danger">首頁</a>
<%-- 上一頁為當前頁-1,前提是當前頁不是第一頁,採用三目運算子實現,每頁5條記錄 --%>
<a
href="goods?pageNum=<%=pageBean.getPageNum() <= 1 ? 1 : pageBean.getPageNum() - 1%>&pageSize=5"
class="btn btn-danger">上一頁</a>
<%-- 下一頁為當前頁+1,前提是當前頁不是末頁,採用三目運算子實現,每頁5條記錄 --%>
<a
href="goods?pageNum=<%=pageBean.getPageNum() >= pageBean.getPageMaxSize() ? pageBean.getPageNum()
: pageBean.getPageNum() + 1%>&pageSize=5"
class="btn btn-danger">下一頁</a>
<%-- 末頁為在pagebean的構造方法中有計算,只需取出即可,每頁5條記錄 --%>
<a href="goods?pageNum=<%=pageBean.getPageMaxSize()%>&pageSize=5"
class="btn btn-danger">末頁</a>
</div>
</body>
</html>
詳細資訊頁面的設計:
在首頁的商品圖片設定a標籤(商品id放在url中)跳轉到singleservlet(get方式獲取url中的引數商品id並做業務處理,返回一個數據模型放到request)轉發到jsp顯示
singleservlet(controller層)
package com.wowowo.web;
import java.io.IOException;
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 com.wowowo.bean.Product;
import com.wowowo.service.ProductService;
import com.wowowo.service.impl.ProductServiceImpl;
/**
* Servlet implementation class SingleServlet
*/
@WebServlet("/single")
public class SingleServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private ProductService ps=new ProductServiceImpl();
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//get方式獲取的商品id
int uid = Integer.parseInt(request.getParameter("uid"));
//傳入邏輯模型
Product p=ps.queryById(uid);
//request新增資料模型
request.setAttribute("product", p);
//跳轉到single.jsp(view層)
request.getRequestDispatcher("/single.jsp").forward(request, response);;
}
}
singleservlet跳轉到的jsp(view層)
顯示單一商品資訊
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@page import="com.wowowo.bean.*"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<style type="text/css">
.single {
width: 500px;
text-align: center;
margin: auto;
}
.detail {
height: 195px;
float: left;
}
</style>
</head>
<body>
<%
Product p = (Product) request.getAttribute("product");
%>
<div class="single">
<div class="detail">
<img src="/my1023hw/images/<%=p.getProdImage()%>" />
</div>
<div class="detail">
商品名稱:<%=p.getProdName()%><br /> 商品單價:<%=p.getProdPrice()%><br />
商品庫存:<%=p.getProdAmount()%><br /> <input type="text" value="1">
<br /> <input type="button" value="新增到購物車"> <input
type="button" value="立即購買">
</div>
</div>
</body>
</html>