三層架構(該部分引自菜鳥教程)
什麼是三層?
UI(表現層): 主要是指與用戶交互的界面。用於接收用戶輸入的數據和顯示處理後用戶需要的數據。
BLL:(業務邏輯層): UI層和DAL層之間的橋樑。實現業務邏輯。業務邏輯具體包含:驗證、計算、業務規則等等。
DAL:(數據訪問層): 與數據庫打交道。主要實現對數據的增、刪、改、查。將存儲在數據庫中的數據提交給業務層,同時將業務層處理的數據保存到數據庫。(當然這些操作都是基於UI層的。用戶的需求反映給界面(UI),UI反映給BLL,BLL反映給DAL,DAL進行數據的操作,操作後再一一返回,直到將用戶所需數據反饋給用戶)
每一層都各負其責,那麼該如何將三層聯繫起來呢?
1、單項引用(見下圖)
2、這時候實體層(Entity)來了。(注:當然,實體層的作用不止這些)
**Entity(實體層):**它不屬於三層中的任何一層,但是它是必不可少的一層。
Entity在三層架構中的作用:
- 1、實現面向對象思想中的"封裝";
- 2、貫穿於三層,在三層之間傳遞數據;(注:確切的說實體層貫穿於三層之間,來連接三層)
- 3、對於初學者來說,可以這樣理解:每張數據表對應一個實體,即每個數據表中的字段對應實體中的屬性(注:當然,事實上不是這樣。爲什麼?1>,可能我們需要的實體在數據表對應的實體中並不存在;2>,我們完全可以將所有數據表中的所有字段都放在一個實體裏)
- 4、每一層(UI—>BLL—>DAL)之間的數據傳遞(單向)是靠變量或實體作爲參數來傳遞的,這樣就構造了三層之間的聯繫,完成了功能的實現。
但是對於大量的數據來說,用變量做參數有些複雜,因爲參數量太多,容易搞混。比如:我要把員工信息傳遞到下層,信息包括:員工號、姓名、年齡、性別、工資....用變量做參數的話,那麼我們的方法中的參數就會很多,極有可能在使用時,將參數匹配搞混。這時候,如果用實體做參數,就會很方便,不用考慮參數匹配的問題,用到實體中哪個屬性拿來直接用就可以,很方便。這樣做也提高了效率。
*(***注:***這裏爲什麼說可以暫時理解爲每個數據表對應一個實體??答:大家都知道,我們做系統的目的,是爲用戶提供服務,用戶可不關心你的系統後臺是怎麼工作的,用戶只關心軟件是不是好用,界面是不是符合自己心意。用戶在界面上輕鬆的增、刪、改、查,那麼數據庫中也要有相應的增、刪、改、查,而增刪改查具體操作對象就是數據庫中的數據,說白了就是表中的字段。所以,將每個數據表作爲一個實體類,實體類封裝的屬性對應到表中的字段,這樣的話,實體在貫穿於三層之間時,就可以實現增刪改查數據了)*
綜上所述:三層及實體層之間的依賴關係:
例:
**服務員:**只管接待客人;
**廚師:**只管做客人點的菜;
**採購員:**只管按客人點菜的要求採購食材;
他們各負其職,服務員不用瞭解廚師如何做菜,不用瞭解採購員如何採購食材;廚師不用知道服務員接待了哪位客人,不用知道採購員如何採購食材;同樣,採購員不用知道服務員接待了哪位客人,不用知道廚師如何做菜。
他們三者是如何聯繫的?
比如:廚師會做:炒茄子、炒雞蛋、炒麪——此時構建三個方法( cookEggplant()、cookEgg()、cookNoodle())
顧客直接和服務員打交道,顧客和服務員(UI層)說:我要一個炒茄子,而服務員不負責炒茄子,她就把請求往上遞交,傳遞給廚師(BLL層),廚師需要茄子,就把請求往上遞交,傳遞給採購員(DAL層),採購員從倉庫裏取來茄子傳回給廚師,廚師響應cookEggplant()方法,做好炒茄子後,又傳回給服務員,服務員把茄子呈現給顧客。
這樣就完成了一個完整的操作。
在此過程中,茄子作爲參數在三層中傳遞,如果顧客點炒雞蛋,則雞蛋作爲參數(這是變量做參數)。如果,用戶增加需求,我們還得在方法中添加參數,一個方法添加一個,一個方法設計到三層;何況實際中並不止設計到一個方法的更改。所以,爲了解決這個問題,我們可以把茄子、雞蛋、麪條作爲屬性定義到顧客實體中,一旦顧客增加了炒雞蛋需求,直接把雞蛋屬性拿出來用即可,不用再去考慮去每層的方法中添加參數了,更不用考慮參數的匹配問題。
爲什麼使用三層?
使用三層架構的目的:解耦!!!
同樣拿上面飯店的例子來講:
(1)服務員(UI層)請假——另找服務員;廚師(BLL層)辭職——招聘另一個廚師;採購員(DAL)辭職——招聘另一個採購員; (2)顧客反映:
- 1、你們店服務態度不好——服務員的問題。開除服務員;
- 2、你們店菜裏有蟲子——廚師的問題。換廚師;
任何一層發生變化都不會影響到另外一層!!!
與兩層的區別?
兩層:
(當任何一個地方發生變化時,都需要重新開發整個系統。"多層"放在一層,分工不明確耦合度高——難以適應需求變化,可維護性低、可擴展性低)
三層
(發生在哪一層的變化,只需更改該層,不需要更改整個系統。層次清晰,分工明確,每層之間耦合度低——提高了效率,適應需求變化,可維護性高,可擴展性高)
綜上,三層架構的優勢:
- 1,結構清晰、耦合度低
- 2,可維護性高,可擴展性高
- 3,利於開發任務同步進行, 容易適應需求變化
三層架構的優劣勢:
- 1、降低了系統的性能。這是不言而喻的。如果不採用分層式結構,很多業務可以直接造訪數據庫,以此獲取相應的數據,如今卻必須通過中間層來完成。
- 2、有時會導致級聯的修改。這種修改尤其體現在自上而下的方向。如果在表示層中需要增加一個功能,爲保證其設計符合分層式結構,可能需要在相應的業務邏輯層和數據訪問層中都增加相應的代碼
- 3、增加了代碼量,增加了工作量
Http協議
**什麼是Http協議?
它叫做超文本傳輸協議。瀏覽器和服務器之間進行交互,需要遵守一定的規則,那麼這個規則就是Http協議
協議版本: 0.0 版本 【淘汰】 1.0 版本【幾乎不再使用】 1.1 版本【主流】
http協議內容包含請求(Request)和響應(Response)兩段內容。 請求包含請求頭、請求行、請求體 響應包含響應
頭、響應行、響應體
Http的請求內容
-
請求行:請求消息中的第一行 請求頭:向服務器端傳遞一些附加消息 請求(體)內容:請求消息傳遞的真實數據 -指的是form( 表單)內容的傳遞 1.1 請求方式: get 和post嗎? 面試題: get 和post 的區別? get: 1. 地址欄中暴
露參數(安全性較低) 2. 默認是get 3. 速度快 4. 可傳遞的參數長度有限制 post 1.地址中不暴露參數 (安全性
高) 2. 速度較慢 3.傳遞參數沒有限制 4.上傳時,必須採用post方式 -
響應行:響應消息中的第一行 響應頭:向瀏覽器端傳遞一些附加消息 響應(體)內容:響應消息傳遞的真實數據 -指的是後臺的數據的傳遞到前臺的內容
Http狀態碼是由三位數字構成的,它的第一位數字有五種可能的取值。
狀態碼 status code: 1xx: 表示 請求接受 ,需要繼續處理 2xx:表示請求已成功被服務器接受 3xx:需要進一步細化請求的內容 4xx:客戶端的請求是有問題的。 如:404(查看前臺網頁數據傳遞的URL) 5xx:服務器端出現錯誤。如:500(和前臺網頁無關,是後臺代碼出現了問題)
繼續上次項目
實體類
/**
* @program:
* @description:
* @author:稽_智
* @create: 2020-03-11 16:42
*/
public class Student {
private int id; //學生id(學號)
private String username; //賬戶
private String password; //密碼
private String email; //郵箱
private String tel; //電話
@Override
public String toString() {
return "Student{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
", tel='" + tel + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
}
工具類
import java.io.IOException;
import java.sql.Connection;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Properties;
/**
* @author:稽_智
* @create: 2020-03-11 16:51
* 由於是工具類,所以方法、屬性均爲靜態
*/
public class DbcpUtils {
private static DataSource ds = null;
static{
InputStream in = DbcpUtils.class.getClassLoader().getResourceAsStream("dbcp.properties");//此處的"dbcp.properties"爲配置文件,應在src
Properties p = new Properties();
try {
p.load(in);
ds = BasicDataSourceFactory.createDataSource(p);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static DataSource getDataSource() {
return ds;
}
public static Connection getConnection() throws IOException {
try {
return ds.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
數據訪問層
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.sql.SQLException;
import java.util.List;
/**
* @author:稽_智
* @create: 2020-03-11 16:48
* 這裏需要用到實體類和工具類
* 異常均在方法中解決
*/
/*
第一部分:增刪改----> update 更改、更新
第二部分:查 ---> query 查詢
*/
public class StudentDao {
QueryRunner qr = new QueryRunner(DbcpUtils.getDataSource());
public List findAll() {
try {
return qr.query("select * from user",new BeanListHandler<Student>(Student.class));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public void addUser(String username, String password, String email, String tel) {
try {
//語句中的?類似佔位符,與後面的String變量一一對應
qr.update("insert into user(username,password,email,tel) values(?,?,?,?)",username,password, email, tel);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
服務層/邏輯層
import java.util.List;
/**
* @author:稽_智
* @create: 2020-03-11 16:41
*/
public class StudentService {
private StudentDao studenDao = new StudentDao();
public List findAll() {
return studenDao.findAll();
}
public void addUser(String username, String password, String email, String tel) {
//判斷各個字符串是否爲空字符串,任一爲空則不再執行用戶添加(註冊)
if(username.equals("") || username == null)
return;
if(password.equals("") || password == null)
return;
if(email.equals("") || email == null)
return;
if(tel.equals("") || tel == null)
return;
studenDao.addUser(username,password,email, tel);
}
}
表現層
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 java.io.IOException;
import java.util.List;
@WebServlet("/studenServlet")
public class StudenServlet extends HttpServlet {
private StudentService studentService = new StudentService();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//設置字符編碼,防止亂碼
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//獲取變量名爲type的參數值
String type = request.getParameter("type");
//根據獲取到的參數值判斷執行註冊or查找方法
if(type.equals("reg"))
reg(request,response);
else if(type.equals("findAll"))
findAll(request,response);
}
private void findAll(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
List list = studentService.findAll();
response.getWriter().write(list.toString());
System.out.println(list);
//"student"爲一個記號,記號的實際內容爲list
request.setAttribute("student",list);
//studentlist.jsp爲將要跳轉到的頁面
request.getRequestDispatcher("/studentlist.jsp").forward(request, response);
}
private void reg(HttpServletRequest request, HttpServletResponse response) {
studentService.addUser(
request.getParameter("username"),
request.getParameter("password"),
request.getParameter("email"),
request.getParameter("tel")
);
}
}
studentlist.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<table border="1px">
<tr>
<th>username</th>
<th>password</th>
<th>email</th>
<th>tel</th>
</tr>
<c:forEach items="${student}" var="s" varStatus="vs">
<tr>
<td>${s.username}</td>
<td>${s.password}</td>
<td>${s.email}</td>
<td>${s.tel}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
dbcp.properties:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/{數據庫名}
username=數據庫用戶名
password=數據庫用戶密碼
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-LdN8AR38-1584358340309)(D:\桌面\筆記_文章\JavaWeb\增刪改查.png)]