MVC 模式在實作的過程中分了 DAO,Service,Controller 和 View 幾個層面。先抄一段很多部落格都引用的關于各層的解釋。
DAO層
DAO層主要是做資料持久層的工作,負責與資料庫進行聯絡的一些任務都封裝在此,DAO層的設計首先是設計DAO的接口,然後在Spring的配置檔案中定義此接口的實作類,然後就可在子產品中調用此接口來進行資料業務的處理,而不用關心此接口的具體實作類是哪個類,顯得結構非常清晰,DAO層的資料源配置,以及有關資料庫連接配接的參數都在Spring的配置檔案中進行配置。
DAO設計的總體規劃需要和設計的表,和實作類之間一一對應。
DAO層所定義的接口裡的方法都大同小異,這是由我們在DAO層對資料庫通路的操作來決定的,對資料庫的操作,我們基本要用到的就是新增,更新,删除,查詢等方法。因而DAO層裡面基本上都應該要涵蓋這些方法對應的操作。除此之外,可以定義一些自定義的特殊的對資料庫通路的方法。
Service層
Service層主要負責業務子產品的邏輯應用設計。同樣是首先設計接口,再設計其實作的類,接着再Spring的配置檔案中配置其實作的關聯。這樣我們就可以在應用中調用Service接口來進行業務處理。Service層的業務實作,具體要調用到已定義的DAO層的接口,封裝Service層的業務邏輯有利于通用的業務邏輯的獨立性和重複利用性,程式顯得非常簡潔。
Service層是建立在DAO層之上的,建立了DAO層後才可以建立Service層,而Service層又是在Controller層之下的,因而Service層應該既調用DAO層的接口,又要提供接口給Controller層的類來進行調用,它剛好處于一個中間層的位置。每個模型都有一個Service接口,每個接口分别封裝各自的業務處理方法。
在DAO層定義的一些方法,在Service層并沒有使用,那為什麼還要在DAO層進行定義呢?這是由我們定義的需求邏輯所決定的。DAO層的操作 經過抽象後基本上都是通用的,因而我們在定義DAO層的時候可以将相關的方法定義完畢,這樣的好處是在對Service進行擴充的時候不需要再對DAO層進行修改,提高了程式的可擴充性。
Controller層
Controller層負責具體的業務子產品流程的控制,在此層裡面要調用Serice層的接口來控制業務流程,控制的配置也同樣是在Spring的配置檔案裡面進行,針對具體的業務流程,會有不同的控制器,我們具體的設計過程中可以将流程進行抽象歸納,設計出可以重複利用的子單元流程子產品,這樣不僅使程式結構變得清晰,也大大減少了代碼量。
View層
此層與控制層結合比較緊密,需要二者結合起來協同工發。
View層主要負責前台jsp頁面的表示。
開發側重
DAO層,Service層這兩個層次都可以單獨開發,互相的耦合度很低,完全可以獨立進行,這樣的一種模式在開發大項目的過程中尤其有優勢。本項目雖然是個非常小的練習項目,但依然采用了這種模式。這樣做既可以建立和鞏固MVC分層設計的理念和習慣,同時在遇到大項目的時候不至于對此茫然到一無所知而無從下手。隻不過因為項目小而使得 Service 中幾乎沒有更多的内容,單純調用了 DAO 的接口罷了。如果有較為複雜的查詢統計任務,直接用DAO的接口操作看起來會讓程式的靈活性和擴充性變差,而且DAO更多的是面向單個實體類,多個實體類的同時操作(如彙款:修改兩個賬戶的餘額并記錄兩條收支明細),在 Service 層就需要分别調用不同的方法去完成了。
Controller,View層因為耦合度比較高,因而要結合在一起開發,但是也可以看作一個整體獨立于前兩個層進行開發。這樣,在層與層之前我們隻需要知道接口的定義,調用接口即可完成所需要的邏輯單元應用,一切都顯得非常清晰簡單。實踐中,我發現的确 Controller 更關注于對頁面的請求,傳遞參數給後面的 Service 層以及将傳回的結果再交還給頁面。
下面以 Employee 實體為例,列出四層的設計代碼。
12.1 DAO 層
EmployeeMapper.java 代碼
package com.hh.ssm.dao;
import com.hh.ssm.bean.Employee;
import com.hh.ssm.bean.EmployeeExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
/**
* 定義了 DAO接口,其中的方法最終實際上是由 MyBatis 負責實作的。
* @author HH
*/
public interface EmployeeMapper {
long countByExample(EmployeeExample example);
int deleteByExample(EmployeeExample example);
int deleteByPrimaryKey(Integer employeeId);
int insert(Employee record);
int insertSelective(Employee record);
List<Employee> selectByExample(EmployeeExample example);
Employee selectByPrimaryKey(Integer employeeId);
List<Employee> selectByExampleWithDepartment(EmployeeExample example);
Employee selectByPrimaryKeyWithDepartment(Integer employeeId);
int updateByExampleSelective(@Param("record") Employee record, @Param("example") EmployeeExample example);
int updateByExample(@Param("record") Employee record, @Param("example") EmployeeExample example);
int updateByPrimaryKeySelective(Employee record);
int updateByPrimaryKey(Employee record);
}
12.2 Service 層
EmployeeService.java 代碼如下:
目前暫時隻是寫了一個業務:擷取全部員工資訊。
package com.hh.ssm.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.hh.ssm.bean.Employee;
import com.hh.ssm.dao.EmployeeMapper;
@Service
public class EmployeeService {
@Autowired
EmployeeMapper employeeMapper;
// 擷取全部員工資訊。
public List<Employee> getAll() {
return employeeMapper.selectByExampleWithDepartment(null);
}
}
12.3 Controller 層
EmployeeController.java 代碼如下:
package com.hh.ssm.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hh.ssm.bean.Employee;
import com.hh.ssm.service.EmployeeService;
@Controller
public class EmployeeController {
@Autowired
EmployeeService employeeService;
/**
* 查詢員工資料(分頁查詢)
*
* @return
*/
@RequestMapping("/employees")
public String getAllEmployees(
@RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum,
Model model) {
// 引入PageHelper分頁插件進行分頁
// 每頁的記錄數
int pageSize = ;
// 傳遞分頁參數
PageHelper.startPage(pageNum, pageSize);
// startPage 後面的第一個查詢,結果會分頁
List<Employee> employees = employeeService.getAll();
// 導航頁碼數量
int navigatePages = ;
// 将分頁結果封裝到 PageInfo
PageInfo pageInfo = new PageInfo(employees, navigatePages);
//System.out.println(pageInfo);
// 把 PageInfo 封裝到 model,傳回到傳回頁面
model.addAttribute("pageInfo", pageInfo);
// 傳回的頁面,根據dispatcher配置中,應該調用的是 /WEB-INF/view/employees.jsp
return "employees";
}
}
12.4 View層
這層是jsp,我先把傳回的頁面貼出來,下一篇總結這個是怎麼來的。
/ssm/src/main/webapp/WEB-INF/views/employees.jsp 的代碼如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- 引入JSTL -->
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<!--
<link rel="stylesheet"
href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" target="_blank" rel="external nofollow" >
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<script
src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
-->
<%
pageContext.setAttribute("APP_PATH", request.getContextPath());
%>
<link rel="stylesheet"
href="${APP_PATH }/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
<script src="${APP_PATH }/static/js/jquery-1.12.4.min.js"></script>
<script
src="${APP_PATH }/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
<title>員工清單</title>
</head>
<body>
<div class="container">
<h1 class="page-header">員工管理</h1>
<div class="row">
<h3 class="page-header col-md-10">員工清單</h3>
<button class="page-header col-sd-1 btn btn-info">
<span class="glyphicon glyphicon-plus"></span> 新增
</button>
<button class="page-header col-sd-1 btn btn-danger">
<span class="glyphicon glyphicon-trash"></span> 删除
</button>
</div>
<br>
<div class="row">
<table class="table table-striped table-hover">
<thead>
<tr>
<th class="col-md-1">#</th>
<th class="col-md-1">部門名稱</th>
<th class="col-md-1">員工姓名</th>
<th class="col-md-1">性别</th>
<th class="col-md-2">電子郵箱</th>
<th class="col-md-2">建立時間</th>
<th class="col-md-2">更新時間</th>
<th class="col-md-2">操作</th>
</tr>
</thead>
<tbody>
<c:forEach items="${pageInfo.list }" var="employee">
<tr>
<td class="col-md-1" style="vertical-align: middle;">${employee.employeeId }</td>
<td class="col-md-1" style="vertical-align: middle;">${employee.department.departmentName }</td>
<td class="col-md-1" style="vertical-align: middle;">${employee.employeeName }</td>
<td class="col-md-1" style="vertical-align: middle;">${employee.employeeGender }</td>
<td class="col-md-2" style="vertical-align: middle;">${employee.employeeEmail }</td>
<td class="col-md-2" style="vertical-align: middle;"><fmt:formatDate
value="${employee.gmtCreate }" pattern="yyyy-MM-dd" /></td>
<td class="col-md-2" style="vertical-align: middle;"><fmt:formatDate
value="${employee.gmtModified }" pattern="yyyy-MM-dd" /></td>
<td class="col-md-2" style="vertical-align: middle;">
<button class="btn btn-primary btn-xs">
<span class="glyphicon glyphicon-pencil"></span> 編輯
</button>
<button class="btn btn-danger btn-xs">
<span class="glyphicon glyphicon-trash"></span> 删除
</button>
</td>
</tr>
</c:forEach>
<tr>
<td style="vertical-align: middle;" colspan="5" class="col-md-6">目前 第
${pageInfo.pageNum } 頁,共 ${pageInfo.pages } 頁,共有 ${pageInfo.total }
條記錄。</td>
<td style="vertical-align: middle;" colspan="3" class="col-md-6" align="right">
<nav aria-label="Page navigation">
<ul class="pagination pagination">
<li><a href="${APP_PATH }/employees?pageNum=1">首頁</a></li>
<c:if test="${pageInfo.hasPreviousPage }">
<li><a
href="${APP_PATH }/employees?pageNum=${pageInfo.pageNum-1 }"
aria-label="Previous"> <span aria-hidden="true">«</span></a></li>
</c:if>
<c:forEach items="${pageInfo.navigatepageNums }"
var="navigatePageNum">
<c:if test="${navigatePageNum == pageInfo.pageNum }">
<li class="active"><a href="#">${navigatePageNum } <span
class="sr-only">(current)</span></a></li>
</c:if>
<c:if test="${navigatePageNum != pageInfo.pageNum }">
<li><a
href="${APP_PATH }/employees?pageNum=${navigatePageNum }">${navigatePageNum }</a></li>
</c:if>
</c:forEach>
<c:if test="${pageInfo.hasNextPage }">
<li><a
href="${APP_PATH }/employees?pageNum=${pageInfo.pageNum+1 }"
aria-label="Next"> <span aria-hidden="true">»</span>
</a></li>
</c:if>
<li><a
href="${APP_PATH }/employees?pageNum=${pageInfo.pages }">末頁</a></li>
</ul>
</nav>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
</html>