【JavaEE進階】圖書管理系統(未完待續)

目錄

用戶登錄

添加圖書

圖書列表

修改圖書

刪除圖書

批量刪除

攔截器

🍃前言

什么是攔截器?

攔截器的基本使用

自定義攔截器

注冊配置攔截器

攔截路徑

攔截器執行流程

項目實現統一攔截

定義攔截器

注冊配置攔截器


前?圖書管理系統, 咱們只完成了??登錄和圖書列表, 并且數據是模擬的. 接下來我們把其他功能進?完善.

這里沒圖書管理系統之前模板的可以參考我之前博客[SpringBoot]Spring MVC(6.0)----圖書管理系統(初)-CSDN博客

在那基礎上,我們繼續進行代碼書寫

用戶登錄

第一步引入依賴,以及配置yml文件

spring:datasource:url: jdbc:mysql://127.0.0.1:3306/book_test?characterEncoding=utf8&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:configuration: # ???? MyBatis ??? SQLlog-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true  #??????
# ???? MyBatis ??? SQL
logging:file:name: logs/springboot.log

引入依賴需要我們加上一個插件

然后在pom.xml文件中右鍵

?然后進行選擇即可,這里我們需要選擇我右面的依賴

其次,我們書寫Controller--Service--Mapper結構

package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/login")public boolean  login(String userName, String password, HttpSession session) {//校驗用戶信息是否合法.if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {return false;}UserInfo userInfo=userService.queryUserByName(userName);if(userInfo==null){return false;}//判斷用戶名和密碼是否正確//理論上應該從數據庫中獲取, 但是目前還沒學習 mybatis, 所以先這么寫.if(userInfo!=null&&password.equals(userInfo.getPassword())) {return true;}userInfo.setPassword("");session.setAttribute("userName",userName);return false;}
}

為什么要把userInfo的密碼設置為空再設置session呢?

因為存儲到session中了.你如果不將密碼設置為空的話,在獲取到session就能看到密碼


import com.example.demo.model.UserInfo;
import com.example.demo.model.UserInfoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;@Service
public class UserService{@Autowiredprivate UserInfoMapper userInfoMapper;public UserInfo queryUserByName(String name) {return userInfoMapper.queryUserByName(name);}
}
package com.example.demo.model;import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;@Mapper
public interface UserInfoMapper {@Select("select * from user_info where delete_flag=0 and user_name = #{name}")UserInfo queryUserByName(String name);
}

這樣我們就可以完成一個登錄,數據庫中存儲信息

同時我們需要修改前端代碼(因為我們將返回類型改變了)

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="css/bootstrap.min.css"><link rel="stylesheet" href="css/login.css"><script type="text/javascript" src="js/jquery.min.js"></script>
</head><body><div class="container-login"><div class="container-pic"><img src="pic/computer.png" width="350px"></div><div class="login-dialog"><h3>登陸</h3><div class="row"><span>用戶名</span><input type="text" name="userName" id="userName" class="form-control"></div><div class="row"><span>密碼</span><input type="password" name="password" id="password" class="form-control"></div><div class="row"><button type="button" class="btn btn-info btn-lg" onclick="login()">登錄</button></div></div></div><script src="js/jquery.min.js"></script><script>function login() {$.ajax({url : "/user/login",type : "post",data : {userName : $("#userName").val(),password : $("#password").val(),},success:function(result) {if(result==false) {alert("用戶名或密碼錯誤,請重新輸入");}else {location.href = "book_list.html";}}});}</script>
</body></html>

添加圖書

控制層:
@RequestMapping("/addBook")public String addBook(BookInfo bookInfo){System.out.println("添加圖書"+bookInfo);if (!StringUtils.hasLength(bookInfo.getBookName())|| !StringUtils.hasLength(bookInfo.getAuthor())|| bookInfo.getCount()==null|| bookInfo.getPrice()==null|| !StringUtils.hasLength(bookInfo.getPublish())|| bookInfo.getStatus() ==null){return "輸?參數不合法, 請檢查?參!";}try {bookService.addBook(bookInfo);return "";}catch (Exception e){System.out.println("增添錯誤");return e.getMessage();}}

業務層

public void addBook(BookInfo bookInfo) {bookInfoMapper.insertBook(bookInfo);}

數據層:

@Mapper
public interface BookInfoMapper {@Insert("insert into book_info (book_name,author,count,price,publish,status) " + "values (#{bookName},#{author},#{count},#{price},#{publish},#{status})")Integer insertBook(BookInfo bookInfo);
}

前端修改代碼

   function add() {$.ajax({type: "post",url: "/book/addBook",data: $("#addBook").serialize(),success: function (result) {if (result == "") {location.href = "book_list.html"} else {console.log(result);alert("添加失敗:" + result);}},error: function (error) {console.log(error);}});}

然后頁面就可以正常訪問了

?前后對比:

圖書列表

可以看到, 添加圖書之后, 跳轉到圖書列表??, 并沒有顯?剛才添加的圖書信息, 接下來我們來實現圖 書列表
我們之前做的表?墻查詢功能,是將數據庫中所有的數據查詢出來并展?到??上,試想如果數據庫 中的數據有很多(假設有??萬條)的時候,將數據全部展?出來肯定不現實,那如何解決這個問題呢?

使?分?解決這個問題。每次只展???的數據,?如:??展?10條數據,如果還想看其他的數
據,可以通過點擊?碼進?查詢

1.添加數據

INSERT INTO `book_info` ( book_name, author, count, price, publish )
VALUES
( '圖書2', '作者2', 29, 22.00, '出版社2' ),( '圖書3', '作者2', 29, 22.00, '出版社
3' ),
( '圖書4', '作者2', 29, 22.00, '出版社1' ),( '圖書5', '作者2', 29, 22.00, '出版社
1' ),
( '圖書6', '作者2', 29, 22.00, '出版社1' ),( '圖書7', '作者2', 29, 22.00, '出版社
1' ),
( '圖書8', '作者2', 29, 22.00, '出版社1' ),( '圖書9', '作者2', 29, 22.00, '出版社
1' ),
( '圖書10', '作者2', 29, 22.00, '出版社1'),( '圖書11', '作者2', 29, 22.00, '出版 社1'),
( '圖書12', '作者2', 29, 22.00, '出版社1'),( '圖書13', '作者2', 29, 22.00, '出版 社1'),
( '圖書14', '作者2', 29, 22.00, '出版社1'),( '圖書15', '作者2', 29, 22.00, '出版 社1'),
( '圖書16', '作者2', 29, 22.00, '出版社1'),( '圖書17', '作者2', 29, 22.00, '出版 社1'),
( '圖書18', '作者2', 29, 22.00, '出版社1'),( '圖書19', '作者2', 29, 22.00, '出版 社1'),
( '圖書20', '作者2', 29, 22.00, '出版社1'),( '圖書21', '作者2', 29, 22.00, '出版 社1');

2.了解分頁的sql原理

查找第1到10條

SELECT * FROM book_info LIMIT 0,10

3.算法可知:開始索引 = (當前?碼 - 1) * 每?顯?條數

4.前端在發起查詢請求時,需要向服務端傳遞的參數

????????currentPage 當前?碼 //默認值為1
? ? ? ??pageSize 每?顯?條數 //默認值為10
5后端響應時, 需要響應給前端的數據
records 所查詢到的數據列表(存儲到List 集合中)
?total 總記錄數 (?于告訴前端顯?多少?, 顯??數為: (total + pageSize -1)/pageSize
顯??數totalPage 計算公式為 : total % pagesize == 0 ? total / pagesize : (total /
pagesize)+1;)

?6.翻?請求和響應部分, 我們通常封裝在兩個對象中

翻頁請求對象

@Data
public class PageRequest {private int currentPage = 1; // 當前?private int pageSize = 10; // 每?中的記錄數 }

我們需要根據currentPage 和pageSize ,計算出來開始索引

所以PageRequest修改為

package com.example.demo.model;import lombok.Data;@Data
public class PageRequest {private int currentPage = 1; // 當前?private int pageSize = 10; // 每?中的記錄數private int offset=1;public int getOffset() {return (currentPage-1) * pageSize;}
}

翻?列表結果類:

import lombok.Data;
import java.util.List;
@Data
public class PageResult<T> {private int total;//所有記錄數private List<T> records; // 當前?數據public PageResult(Integer total, List<T> records) {this.total = total;this.records = records;}
}

7.約定前后端交互接?

我們約定, 瀏覽器給服務器發送?個 /book/getListByPage 這樣的 HTTP 請求, 通過
currentPage 參數告訴服務器, 當前請求為第??的數據, 后端根據請求參數, 返回對應?的數據
第??可以不傳參數, currentPage默認值為 1
7.創建enmus?錄, 創建BookStatus類:
package com.example.demo.model;public enum BookStatus {DELETED(0,"?效"),NORMAL(1,"可借閱"),FORBIDDEN(2,"不可借閱");private Integer code;private String name;BookStatus(int code, String name) {this.code = code;this.name = name;}public static BookStatus getNameByCode(Integer code){switch (code){case 0: return DELETED;case 1: return NORMAL;case 2: return FORBIDDEN;}return null;}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

8.完善PageResult類

package com.example.demo.model;import lombok.Data;
import java.util.List;
@Data
public class PageResult<T> {public int total;//所有記錄數private List<T> records; // 當前?數據private PageRequest pageRequest;public PageResult(Integer total, PageRequest pageRequest, List<T> records){this.total = total;this.pageRequest = pageRequest;this.records = records;}
}

將PageRequest類引入,便于后面的分頁插件獲取信息

分?組件需要提供?些信息: totalCounts: 總記錄數, pageSize: 每?的個數, visiblePages: 可視?數 ,currentPage: 當前?碼
這些信息中, pageSize 和 visiblePages 前端直接設置即可. totalCounts 后端已經提供, currentPage 也 可以從參數中取到, 但太復雜了, 咱們直接由后端返回即可.

9.完善 BookController,BookService,BookInfoMapper

  @RequestMapping("/getListByPage")public PageResult<BookInfo> getListByPage(PageRequest pageRequest) {log.info("獲取圖書列表{}",pageRequest);PageResult<BookInfo> pageResult = bookService.getBookListByPage(pageRequest);return pageResult;}
public PageResult<BookInfo> getBookListByPage(PageRequest pageRequest) {Integer count = bookInfoMapper.count();List<BookInfo> books = bookInfoMapper.queryBookListByPage(pageRequest);for (BookInfo book:books){book.setStatusCN(BookStatus.getNameByCode(book.getStatus()).getName());}return new PageResult<>(count,pageRequest, books);}
@Mapper
public interface BookInfoMapper {@Insert("insert into book_info (book_name,author,count,price,publish,status) " + "values (#{bookName},#{author},#{count},#{price},#{publish},#{status})")Integer insertBook(BookInfo bookInfo);@Select("select count(1) from book_info where status<>0")Integer count();@Select("select * from book_info where status !=0 order by id desc limit #{offset}, #{pageSize}")List<BookInfo> queryBookListByPage(PageRequest pageRequest);
}

10.接下來處理分?信息

jQuery 分頁插件 : jqPaginator | 菜鳥教程

使用教程,修改里面的部分值即可

onPageChange :回調函數,當換?時觸發(包括初始化第??的時候),會傳?兩個參數:
1、"?標?"的?碼page,Number類型
2、觸發類型type,可能的值:"init"(初始化),"change"(點擊分?)

11.編寫前端代碼(前后端交互,個人認為最為難人的部分,需要重點理解)

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>圖書列表展示</title><link rel="stylesheet" href="css/bootstrap.min.css"><link rel="stylesheet" href="css/list.css"><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/bootstrap.min.js"></script><script src="js/jq-paginator.js"></script></head><body><div class="bookContainer"><h2>圖書列表展示</h2><div class="navbar-justify-between"><div><button class="btn btn-outline-info" type="button" onclick="location.href='book_add.html'">添加圖書</button><button class="btn btn-outline-info" type="button" onclick="batchDelete()">批量刪除</button></div></div><table><thead><tr><td>選擇</td><td class="width100">圖書ID</td><td>書名</td><td>作者</td><td>數量</td><td>定價</td><td>出版社</td><td>狀態</td><td class="width200">操作</td></tr></thead><tbody></tbody></table><div class="demo"><ul id="pageContainer" class="pagination justify-content-center"></ul></div><script>function getQueryParam(name) {const urlParams = new URLSearchParams(window.location.search);return urlParams.get(name);}// 修復:設置默認參數currentPage = 1,避免初始調用時參數缺失function getBookList(currentPage=1) {$.ajax({type: "get",// 修復:確保參數正確拼接,避免出現undefinedurl: "/book/getListByPage?currentPage=" + currentPage,success: function (result) {console.log(result);if (result != null && result.records) { // 修復:字段名records(原resords拼寫錯誤)var finalHtml = "";// 遍歷圖書數據(修復字段名resords -> records)for (var book of result.records) {finalHtml += '<tr>';finalHtml += '<td><input type="checkbox" name="selectBook" value="' + book.id + '" class="book-select"></td>';finalHtml += '<td>' + book.id + '</td>';finalHtml += '<td>' + book.bookName + '</td>';finalHtml += '<td>' + book.author + '</td>';finalHtml += '<td>' + book.count + '</td>';finalHtml += '<td>' + book.price + '</td>';finalHtml += '<td>' + book.publish + '</td>';finalHtml += '<td>' + book.statusCN + '</td>';finalHtml += '<td><div class="op">';finalHtml += '<a href="book_update.html?bookId=' + book.id + '">修改</a>'; // 修復:鏈接中去除空格(bookId后無空格)finalHtml += '<a href="javascript:void(0)" onclick="deleteBook(' + book.id + ')">刪除</a>';finalHtml += '</div></td>';finalHtml += "</tr>";}$("tbody").html(finalHtml);// 初始化分頁插件$("#pageContainer").jqPaginator({totalCounts: result.total, // 總記錄數(需與后端返回字段一致)pageSize: 10,             // 每頁條數visiblePages: 5,          // 可見頁碼數currentPage: result.pageRequest.currentPage, // 修復:從返回結果中獲取當前頁碼(根據后端實際字段名調整)first: '<li class="page-item"><a class="page-link">首頁</a></li>',prev: '<li class="page-item"><a class="page-link" href="javascript:void(0);">上一頁</a></li>',next: '<li class="page-item"><a class="page-link" href="javascript:void(0);">下一頁</a></li>',last: '<li class="page-item"><a class="page-link" href="javascript:void(0);">最后一頁</a></li>',page: '<li class="page-item"><a class="page-link" href="javascript:void(0);">{{page}}</a></li>',// 修復:分頁切換時直接調用getBookList加載數據,而非跳轉頁面onPageChange: function (Page, type) {if (type != 'init') {location.href = "book_list.html?currentPage=" + Page;//  getBookList(Page);}}});}},error: function (xhr) {console.error("請求失敗:", xhr.responseText); // 增加錯誤日志,便于調試}});}// 初始調用時無需傳參(函數已設置默認值)const initialPage = parseInt(getQueryParam("currentPage")) || 1;getBookList(initialPage);// getBookList();function deleteBook(id) {var isDelete = confirm("確認刪除?");if (isDelete) {//刪除圖書alert("刪除成功");}}function batchDelete() {var isDelete = confirm("確認批量刪除?");if (isDelete) {//獲取復選框的idvar ids = [];$("input:checkbox[name='selectBook']:checked").each(function () {ids.push($(this).val());});console.log(ids);alert("批量刪除成功");}}</script></div>
</body></html>

修改圖書

進?修改??, 需要顯?當前圖書的信息

[請求]
/book/queryBookById?bookId=25
[參數] ?[響應] {"id": 25,"bookName": "圖書21","author": "作者2","count": 999,"price": 222.00,"publish": "出版社1","status": 2,"statusCN": null,"createTime": "2023-09-04T04:01:27.000+00:00","updateTime": "2023-09-05T03:37:03.000+00:00"
}

點擊修改按鈕, 修改圖書信息

[請求]
/book/updateBook
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
[參數]
id=1&bookName=圖書1&author=作者1&count=23&price=36&publish=出版社1&status=1
[響應]
"" //失敗信息, 成功時返回空字符串
我們約定, 瀏覽器給服務器發送?個 /book/updateBook 這樣的 HTTP 請求, form表單的形式來 提交數據
服務器返回處理結果, 返回""表?添加圖書成功, 否則, 返回失敗信息

實現服務器代碼

BookController代碼

   @RequestMapping("/queryBookById")public BookInfo queryBookById(Integer bookId) {if (bookId == null || bookId <= 0) {return new BookInfo();}BookInfo bookInfo = bookService.queryBookById(bookId);return bookInfo;}@RequestMapping("/updateBook")public String updateBook(BookInfo bookInfo) {log.info("修改圖書:{}", bookInfo);try {bookService.updateBook(bookInfo);return "";} catch (Exception e) {log.error("修改圖書失敗", e);return e.getMessage();}}

BookService代碼

  public BookInfo queryBookById(Integer bookId) {return bookInfoMapper.queryBookById(bookId);}public void updateBook(BookInfo bookInfo) {bookInfoMapper.updateBook(bookInfo);}

BookInfoMapper代碼

@Select("select id, book_name, author, count, price, publish, `status`, 
create_time, update_time " +"from book_info where id=#{bookId} and status<>0")
BookInfo queryBookById(Integer bookId);

前端代碼

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>修改圖書</title><link rel="stylesheet" href="css/bootstrap.min.css"><link rel="stylesheet" href="css/add.css">
</head><body><div class="container"><div class="form-inline"><h2 style="text-align: left; margin-left: 10px;"><svg xmlns="http://www.w3.org/2000/svg" width="40"fill="#17a2b8" class="bi bi-book-half" viewBox="0 0 16 16"><pathd="M8.5 2.687c.654-.689 1.782-.886 3.112-.752 1.234.124 2.503.523 3.388.893v9.923c-.918-.35-2.107-.692-3.287-.81-1.094-.111-2.278-.039-3.213.492V2.687zM8 1.783C7.015.936 5.587.81 4.287.94c-1.514.153-3.042.672-3.994 1.105A.5.5 0 0 0 0 2.5v11a.5.5 0 0 0 .707.455c.882-.4 2.303-.881 3.68-1.02 1.409-.142 2.59.087 3.223.877a.5.5 0 0 0 .78 0c.633-.79 1.814-1.019 3.222-.877 1.378.139 2.8.62 3.681 1.02A.5.5 0 0 0 16 13.5v-11a.5.5 0 0 0-.293-.455c-.952-.433-2.48-.952-3.994-1.105C10.413.809 8.985.936 8 1.783z" /></svg><span>修改圖書</span></h2></div><form id="updateBook"><input type="hidden" class="form-control" id="bookId" name="id"><div class="form-group"><label for="bookName">圖書名稱:</label><input type="text" class="form-control" id="bookName" name="bookName"></div><div class="form-group"><label for="bookAuthor">圖書作者</label><input type="text" class="form-control" id="bookAuthor" name="author" /></div><div class="form-group"><label for="bookStock">圖書庫存</label><input type="text" class="form-control" id="bookStock" name="count" /></div><div class="form-group"><label for="bookPrice">圖書定價:</label><input type="number" class="form-control" id="bookPrice" name="price"></div><div class="form-group"><label for="bookPublisher">出版社</label><input type="text" id="bookPublisher" class="form-control" name="publish" /></div><div class="form-group"><label for="bookStatus">圖書狀態</label><select class="custom-select" id="bookStatus" name="status"><option value="1" selected>可借閱</option><option value="2">不可借閱</option></select></div><div class="form-group" style="text-align: right"><button type="button" class="btn btn-info btn-lg" onclick="update()">確定</button><button type="button" class="btn btn-secondary btn-lg" onclick="javascript:history.back()">返回</button></div></form></div><script type="text/javascript" src="js/jquery.min.js"></script><script>$.ajax({type: "get",url: "/book/queryBookById" + location.search,success: function (book) {if (book != null) {$("#bookId").val(book.id);$("#bookName").val(book.bookName);$("#bookAuthor").val(book.author);$("#bookStock").val(book.count);$("#bookPrice").val(book.price);$("#bookPublisher").val(book.publish);$("#bookStatus").val(book.status);}}});function update() {$.ajax({type: "post",url: "/book/updateBook",data: $("#updateBook").serialize(),success: function (result) {if (result == "") {location.href = "book_list.html"} else {console.log(result);alert("修改失敗:" + result);}},error: function (error) {console.log(error);}});}</script>
</body></html>
我們修改圖書信息, 是根據圖書ID來修改的, 所以需要前端傳遞的參數中, 包含圖書ID
在form表單中, 再增加?個隱藏輸?框, 存儲圖書ID, 隨 $("#updateBook").serialize()
?起提交到后端
1. 獲取url中參數的值(?較復雜, 需要拆分url)
2. 在form表單中, 再增加?個隱藏輸?框, 存儲圖書ID, 隨 $("#updateBook").serialize()
?起提交到后端
我們采?第?種?式
在form表單中,添加隱藏輸?框
<form id="updateBook"><input type="hidden" class="form-control" id="bookId" name="id"><div class="form-group"><label for="bookName">圖書名稱:</label><input type="text" class="form-control" id="bookName" name="bookName"></div>
hidden 類型的 <input> 元素
隱藏表單, ??不可?、不可改的數據,在??提交表單時,這些數據會?并發送出
使?場景: 正被請求或編輯的內容的 ID. 這些隱藏的 input 元素在渲染完成的??中完全不可?,? 且沒有?法可以使它重新變為可?
??加載時, 給該hidden框賦值
     $("#bookId").val(book.id);

刪除圖書

刪除分為 邏輯刪除 和物理刪除
邏輯刪除
邏輯刪除也稱為軟刪除、假刪除、Soft Delete,即不真正刪除數據,?在某?數據上增加類型 is_deleted的刪除標識,?般使?UPDATE語句
物理刪除
物理刪除也稱為硬刪除,從數據庫表中刪除某??或某?集合數據,一般用DELETE語句
數據是公司的重要財產, 通常情況下, 我們采?邏輯刪除的?式, 當然也可以采?[物理刪除+歸檔]
實現客?端代碼
function deleteBook(id) {var isDelete = confirm("確認刪除?");if (isDelete) {//刪除圖書$.ajax({type: "post",url: "/book/updateBook",data: {id: id,status: 0},success: function () {//重新刷新??location.href = "book_list.html"}});}}

批量刪除

批量刪除, 其實就是批量修改數據
點擊[批量刪除]按鈕時, 只需要把復選框選中的圖書的ID,發送到后端即可
多個id, 我們使?List的形式來傳遞參數
實現服務器代碼
?
@RequestMapping("/batchDeleteBook")public boolean batchDeleteBook(@RequestParam List<Integer> ids){log.info("批量刪除圖書, ids:{}",ids);try{bookService.batchDeleteBook(ids);}catch (Exception e){log.error("批量刪除異常,e:",e);return false;}return true;}public void batchDeleteBook(List<Integer> ids) {bookInfoMapper.batchDeleteBook(ids);
    void batchDeleteBook(List<Integer> ids);

xml

<update id="batchDeleteBook">update book_info set status=0 where id in<foreach collection="ids" item="id" separator="," open="(" close=")">#{id}</foreach></update>

攔截器

🍃前言

前面我們基本實現了圖書股管理系統的功能,但是我們依舊存在一個問題。

就是我們不論是否登錄,我們直接訪問圖書列表。也可以進行訪問及修改

而我們希望達到的效果是,必須要進行登錄后才能進行一系列操作

這里我們使用攔截器來完成著一系列操作

什么是攔截器?

攔截器是Spring框架提供的核?功能之?,主要?來攔截??的請求,在指定?法前后,根據業務需要執?預先設定的代碼.

也就是說,允許開發?員提前預定義?些邏輯,在??的請求響應前后執?.也可以在??請求前阻?其執?.

在攔截器當中,開發?員可以在應?程序中做?些通?性的操作,?如通過攔截器來攔截前端發來的請求,判斷Session中是否有登錄??的信息.如果有就可以放?,如果沒有就進?攔截.

就?如我們去銀?辦理業務,在辦理業務前后,就可以加?些攔截操作,辦理業務之前,先取號,如果帶?份證了就取號成功。業務辦理結束,給業務辦理?員的服務進?評價.這些就是"攔截器"做的?作

攔截器的基本使用

攔截器的使?步驟分為兩步:

  1. 定義攔截器
  2. 注冊配置攔截器

自定義攔截器

?定義攔截器:實現HandlerInterceptor接?,并重寫其所有?法

package com.example.demo.configuration;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("執行目標方法前的代碼");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("目標執行完后的代碼");HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {log.info("目標試圖渲染后的代碼");HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}

這里涉及到的三個方法

preHandle()?法:?標?法執?前執?. 返回true:繼續執?后續操作;返回false:中斷后續操作.
postHandle()?法:?標?法執?后執?
afterCompletion()?法:視圖渲染完畢后執?,最后執?(后端開發現在?乎不涉及視圖,暫不了解)

注冊配置攔截器

注冊配置攔截器:實現WebMvcConfigurer接?,并重寫addInterceptors?法

WebMvcConfigurer這個接口并不是只給攔截器使用的,而是WebMVC相關的配置都在這里

package com.example.demo.configuration;import com.example.demo.model.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**");}
}

啟動服務,試試訪問任意請求,觀察后端?志

們把攔截器中preHandle?法的返回值改為false,再觀察運?結果?

運行結果:可以看到,攔截器攔截了請求,沒有進?響應

攔截路徑

關于注冊配置攔截器的攔截路勁,攔截路徑是指我們定義的這個攔截器,對哪些請求?效.我們在注冊配置攔截器的時候,通過 addPathPatterns() ?法指定要攔截哪些請求.也可以通過
excludePathPatterns() 指定不攔截哪些請求
.上述代碼中,我們配置的是 /** ,表?攔截所有的請求.
攔截器中除了可以設置 /** 攔截所有資源外,還有?些常?攔截路徑設置,比如在該項目中

攔截器執行流程

我們先來看一下正常的執行流程

當我們有了攔截器以后,我們的執行流程為

1.添加攔截器后,執?Controller的?法之前,請求會先被攔截器攔截住.執? preHandle() ?法,這個?法需要返回?個布爾類型的值.如果返回true,就表?放?本次操作,繼續訪問controller中的?法.如果返回false,則不會放?(controller中的?法也不會執?).
2.controller當中的?法執?完畢后,再回過來執? postHandle() 這個?法以及afterCompletion() ?法,執?完畢之后,最終給瀏覽器響應數據.

項目實現統一攔截

定義攔截器

首先定義一個常量類來存放我們的sessionid常量等等

package com.example.demo.model;public class Constants {public static final String SESSION_USER_KEY="userName";
}

再來就是攔截器(邏輯簡單,查看有沒有現在的這個session即可)?

@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("登錄攔截器校驗...");HttpSession session = request.getSession();UserInfo userInfo = (UserInfo) session.getAttribute(Constants.SESSION_USER_KEY);if (userInfo!=null && userInfo.getId()>=0){return true;}response.setStatus(401);//401 表示未認證登錄return false;}
}

代碼解釋如下:

  1. 對服務中所存在的session進行判斷,如果存在,則返回true,放行
  2. 若不存在,則使用setStatus()方法設置http狀態碼為401,前端收到響應進行跳轉到登錄頁面

當前按照上述配置攔截器的代碼來看,會攔截所有的路徑,那么此時在沒有登錄的情況下,訪問每個接口都會進行攔截,包括登錄接口

所以我們需要把上述配置攔截器中的攔截路徑重新配置一下

注冊配置攔截器

注意:攔截器也會攔截前端請求

 @Configurationpublic class WebConfig implements WebMvcConfigurer {//?定義的攔截器對象 @Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注冊?定義攔截器對象registry.addInterceptor(loginInterceptor).addPathPatterns("/**")//設置攔截器攔截的請求路徑(/**表?攔截所有請求 .excludePathPatterns("/user/login")//設置攔截器排除攔截的路徑.excludePathPatterns("/**/*.js") //排除前端靜態資源.excludePathPatterns("/**/*.css").excludePathPatterns("/**/*.png").excludePathPatterns("/**/*.html");}}

訪問用戶登錄接口:此時就可以訪問了

希望你們可以從這一個簡單項目中學到一個項目的基礎思路知識

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/90800.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/90800.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/90800.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于同花順API的熊市與牛市識別模型開發及因子分析

基于同花順API的熊市與牛市識別模型開發及因子分析 1. 引言 1.1 研究背景與意義 金融市場中的牛市與熊市識別一直是投資者和研究人員關注的重點問題。牛市(Bull Market)通常指價格持續上漲的市場環境,投資者信心充足,交易活躍;而熊市(Bear Market)則指價格持續下跌的市場…

AMD 銳龍 AI MAX+ 395 處理器與端側 AI 部署的行業實踐

2025 年 7 月 10 日&#xff0c;AMD 在深圳召開 Mini AI 工作站行業解決方案峰會&#xff0c;正式發布基于銳龍 AI MAX 395 處理器的端側 AI 部署方案&#xff0c;與 200 余家生態伙伴共同探討 AI 技術在千行百業的落地路徑。這一硬件平臺通過異構計算架構與開放生態設計&#…

期權盤位是什么意思?

本文主要介紹期權盤位是什么意思&#xff1f;“期權盤位”并非金融交易中的標準術語&#xff0c;可能是口語化表達或對某些概念的簡化描述。期權盤位是什么意思&#xff1f;1. 期權盤口的“價位”&#xff08;買賣報價位置&#xff09;在期權交易中&#xff0c;“盤口”通常指實…

【Trea】Trea國內版|國際版|海外版下載|Mac版|Windows版|Linux下載配置教程

【Trea】Trea國內版&#xff5c;國際版&#xff5c;海外版下載&#xff5c;Mac版&#xff5c;Windows版下載配置教程 本文適用讀者&#xff1a; 想要第一次安裝 Trea需要在 Windows 或 macOS 上完成環境配置想深入了解 Doubao、DeepSeek、ChatGPT、Claude 等模型在 Trea 中的接…

MyBatis實現分頁查詢-蒼穹外賣筆記

首先分頁查詢的原理是SQL的limit關鍵字。LIMIT 子句用于限制 SQL 查詢返回的記錄數。它接受一個或兩個整數參數&#xff0c;第一個參數表示偏移量&#xff0c;第二個參數表示返回的最大記錄數。我們完全可以使用前端傳給我們的page,pageSize,自己去計算limit的參數&#xff0c;…

系統性能評估方法深度解析:從經典到現代

評估本質&#xff1a;系統性能評估是通過量化分析衡量計算機系統在特定工作負載下的表現能力&#xff0c;核心目標是建立可比較的性能基準&#xff0c;為系統設計、選型和優化提供科學依據。一、評估方法分類體系 #mermaid-svg-0ceD4AA2KDwzwtb6 {font-family:"trebuchet …

WebSocket實現多人實時在線聊天

最近公司在做一個婚戀app&#xff0c;需要增加一個功能&#xff0c;實現多人實時在線聊天。基于WebSocket在Springboot中的使用&#xff0c;前端使用vue開發。 一&#xff1a;后端 1. 引入 websocket 的 maven 依賴 <dependency><groupId>org.springframework.bo…

學習筆記隨記-FPGA/硬件加速

一、FPGA&#xff1a;Field Programmable Gate Array 現場可編程門陣列 可編程輸入/輸出單元、基本可編程邏輯單元、嵌入式塊RAM、豐富的布線資源、底層嵌入功能單元和內嵌專用硬核。 可編程輸入/輸出單元&#xff08;I/O&#xff09;單元 輸入/輸出&#xff08;Input/Ouput&…

docker宿主機修改ip后起不來問題解決

確保容器已經連接到了正確的網絡。如果沒有&#xff0c;你可以使用以下命令將容器連接到網絡&#xff1a; 1、停止docker網絡 ifconfig docker0 down1. 停止 Docker 服務 sudo systemctl stop docker2. 刪除 docker0 接口 sudo ip link delete docker03、刪除舊的網橋 docker n…

G1 垃圾回收算法詳解

目錄 簡介 G1 GC 的設計目標 內存結構 回收過程 初始標記&#xff08;Initial Mark&#xff09;并發標記&#xff08;Concurrent Mark&#xff09;最終標記&#xff08;Final Mark / Remark&#xff09;篩選回收&#xff08;Cleanup / Evacuation&#xff09; 混合回收&…

JavaEE多線程——鎖策略 CAS synchronized優化

目錄前言1.鎖策略1.1 樂觀鎖和悲觀鎖1.2 重量級鎖和輕量級鎖1.3 掛起等待鎖和自旋鎖1.4 公平鎖和非公平鎖1.5 可重入鎖和不可重入鎖1.6 讀寫鎖2.CAS2.1 CAS的應用2.2 CAS的ABA問題3.synchronized優化3.1鎖升級3.2鎖消除3.3鎖粗化總結前言 本篇文章主要介紹多線程中鎖策略、CAS…

Windows符號鏈接解決vscode和pycharm占用C盤空間太大的問題

Windows符號鏈接解決vscode和pycharm占用C盤空間太大的問題 參考文章&#xff1a;Windows符號鏈接 1、找到vscode和pycharm在C盤的緩存文件夾。 C:\Users\用戶名\AppData\Roaming\Code C:\Users\用戶名\.vscode\extensionsC:\Users\用戶名\AppData\Local\JetBrains C:\Users…

賦能家庭、行業與工業場景,智微智能新一代Twin Lake 全棧智能終端發布

在數字化浪潮席卷全球的今天&#xff0c;智能終端已成為連接物理世界與數字世界的核心樞紐。智微智能基于Intel Twin Lake平臺&#xff0c;推出覆蓋家庭/行業應用及工業物聯網的全場景產品矩陣&#xff0c;為不同場景下的用戶提供高效、可靠的產品和解決方案。Intel Twin Lake架…

復習筆記 31

前言 好好復習。今天距離考研初試還剩一百六十一天。我的時間其實沒剩多少了呀。我得好好加油。 歸并排序 #include<algorithm> #include<iostream>using namespace std;const int N 100010; int n; int a[N], tmp[N];void merge ( int a[], int l, int r ) {if (…

el-tree 懶加載 loadNode

el-tree 是 Element UI 提供的樹形組件&#xff0c;其懶加載功能通過 loadNode 方法實現&#xff0c;可以在用戶展開節點時動態加載子節點數據&#xff0c;避免一次性加載大量數據。下面介紹 loadNode 的具體用法和示例。基本用法loadNode 是 el-tree 的一個重要屬性&#xff0…

【機器學習入門巨詳細】(研0版)二創OPEN MLSYS

自學機器學習&#xff0c;從入門到精通導論機器學習的基本框架設計目標機器學習框架基本組成原理機器學習生態機器學習工作流環境配置數據處理模型定義損失函數和優化器訓練及保存模型測試及驗證模型定義深度神經網絡以層為核心定義神經網絡神經網絡層實現原理自定義神經網絡層…

Excel 轉 JSON by WTSolutions API 文檔

Excel 轉 JSON by WTSolutions API 文檔 簡介 Excel 轉 JSON API 提供了一種簡單的方式將 Excel 和 CSV 數據轉換為 JSON 格式。該 API 接受制表符分隔或逗號分隔的文本數據&#xff0c;并返回結構化的 JSON。 接口端點 POST https://mcp.wtsolutions.cn/excel-to-json-api 請求…

git版本發布

cvs和svn都是集中式版本控制系統,而git是分布式版本控制系統。 1、集中式版本控制系統必須聯網才能工作&#xff0c;如果在局域網內還好&#xff0c;帶寬夠大&#xff0c;速度夠快&#xff0c;可如果在互聯網上&#xff0c;遇到網速慢的話&#xff0c;呵呵。分布式版本控制系統…

138-EMD-KPCA-CPO-CNN-BiGRU-Attention模型!

138-EMD-KPCA-CPO-CNN-BiGRU-Attention基于經驗模態分解和核主成分分析的長短期記憶網絡改進多維時間序列預測MATLAB代碼&#xff01;其中&#xff08;含CPO-CNN-BiGRU-attention、EMD-CPO-CNN-BiGRU-Attention、EMD-KPCA-CPO-CNN-BiGRU-Attention三個模型的對比&#xff09; 可…

系統思考:多元勝過能力

系統思考&#xff1a;從整體出發&#xff0c;打破瓶頸&#xff0c;擁抱多元 我們是否曾經陷入過這樣的困境&#xff1f; 1、專注能力提升&#xff0c;卻無法突破瓶頸&#xff1a;我和團隊日復一日地努力提升個人能力&#xff0c;投入無數時間和精力&#xff0c;但始終無法打破現…