JavaWeb後端開發2024-08-08

bfs1201發表於2024-08-08

Maven

Apache Maven 是一個專案管理和構建工具,它基於專案物件模型(POM)的概念,透過一小段描述資訊來管理專案的構建。

image-20240709155020079

Maven作用

依賴管理

image-20240709155147967

統一的專案結構

image-20240709154759785

專案構建

image-20240709154912556

倉庫

image-20240709161419270

image-20240709161132316

安裝Maven

image-20240709161751323

image-20240709162417610

image-20240709162705286

<mirror>  
  <id>alimaven</id>  
  <name>aliyun maven</name>  
  <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
  <mirrorOf>central</mirrorOf>          
</mirror>

image-20240709163152423

image-20240709163127689

測試:

mvn -v

image-20240709163334394

配置Maven環境

image-20240709165900228

image-20240709165817714image-20240709165937198

image-20240709170419318

image-20240709165945726

建立Maven專案

image-20240709170135289

座標

image-20240709212930094

匯入Maven專案

image-20240709232811555

image-20240709232830925

依賴配置

image-20240710012523311

倉庫網址:https://mvnrepository.com/

依賴傳遞

image-20240710005706943

顯示關係圖

image-20240710012618346

A的依賴關係圖

image-20240710010730419

B的依賴關係圖

image-20240710010738544

C的依賴關係圖

image-20240710010744834

排除依賴

image-20240710010827016

image-20240710011430583

image-20240710011301251

依賴範圍

image-20240710012918023

生命週期

image-20240710014707654

image-20240710014840162

image-20240710020636804

在同一套生命週期中,當執行後面的階段時,前面的階段都會執行,上圖三套

image-20240710020733297

後端Web開發

SpringBoot

入門

image-20240710174538624

image-20240710174645183

image-20240710174703884

入門程式碼

package com.itheima.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @RequestMapping("/Hello")
    public String hello() {
        System.out.println("Hello!!!");
        return "Hello world!!!";
    }
}

啟動

image-20240710174836709

image-20240710174857394

image-20240710174909693

HTTP協議

image-20240710175709918

請求協議

image-20240710192544415

相應協議

image-20240710194620705

image-20240710194642257

狀態碼大類
狀態碼分類 說明
1xx 響應中——臨時狀態碼,表示請求已經接受,告訴客戶端應該繼續請求或者如果它已經完成則忽略它
2xx 成功——表示請求已經被成功接收,處理已完成
3xx 重定向——重定向到其它地方:它讓客戶端再發起一個請求以完成整個處理。
4xx 客戶端錯誤——處理發生錯誤,責任在客戶端,如:客戶端的請求一個不存在的資源,客戶端未被授權,禁止訪問等
5xx 伺服器端錯誤——處理發生錯誤,責任在服務端,如:服務端丟擲異常,路由出錯,HTTP版本不支援等
常見的響應狀態碼
狀態碼 英文描述 解釋
200 OK 客戶端請求成功,即處理成功,這是我們最想看到的狀態碼
302 Found 指示所請求的資源已移動到由Location響應頭給定的 URL,瀏覽器會自動重新訪問到這個頁面
304 Not Modified 告訴客戶端,你請求的資源至上次取得後,服務端並未更改,你直接用你本地快取吧。隱式重定向
400 Bad Request 客戶端請求有語法錯誤,不能被伺服器所理解
403 Forbidden 伺服器收到請求,但是拒絕提供服務,比如:沒有許可權訪問相關資源
404 Not Found 請求資源不存在,一般是URL輸入有誤,或者網站資源被刪除了
405 Method Not Allowed 請求方式有誤,比如應該用GET請求方式的資源,用了POST
428 Precondition Required 伺服器要求有條件的請求,告訴客戶端要想訪問該資源,必須攜帶特定的請求頭
429 Too Many Requests 指示使用者在給定時間內傳送了太多請求(“限速”),配合 Retry-After(多長時間後可以請求)響應頭一起使用
431 Request Header Fields Too Large 請求頭太大,伺服器不願意處理請求,因為它的頭部欄位太大。請求可以在減少請求頭域的大小後重新提交。
500 Internal Server Error 伺服器發生不可預期的錯誤。伺服器出異常了,趕緊看日誌去吧
503 Service Unavailable 伺服器尚未準備好處理請求,伺服器剛剛啟動,還未初始化好

狀態碼大全:https://cloud.tencent.com/developer/chapter/13553

協議解析

image-20240710195842007

image-20240710201506342

Tomcat

image-20240710201406147

基本使用

image-20240808012318690

image-20240710201750243

配置埠號:

image-20240710202615254

部署:將檔案移動到webapps資料夾下即可

image-20240710202918101

image-20240710203957125

https://docs.spring.io/spring-boot/docs/2.7.4/reference/htmlsingle/#using.build-systems.starters

image-20240710204119487

請求響應

image-20240711110803650

請求

postman

image-20240711121245498

簡單引數

如果方法形參名稱與請求引數名稱不匹配,可以使用 @RequestParam 完成對映

原始方法(不要求掌握)

//原始方法
@RestController
public class HelloController {
    @RequestMapping("/simpleParam")
    public String simpleParam(HttpServletRequest request) {
//        獲取請求引數
        String name = request.getParameter("name");
        String ageStr = request.getParameter("age");
        int age = Integer.parseInt(ageStr);
        System.out.println(name + ":" + age);
        return "OK";
    }
}

image-20240711123328280

image-20240711123339388

Springboot方式

@RestController
public class HelloController {    
//    Springboot方式
    @RequestMapping("/simpleParam")
    public String simpleParam(String name, int age) {
        System.out.println(name + ":" + age);
        return "OK";
    }
}

GET請求

image-20240711124046160

image-20240711124053167

POST請求

image-20240711124236196

image-20240711124244834

請求引數名與方法變數名不同,不能傳遞

    @RequestMapping("/simpleParam")
    public String simpleParam(String userName, int age) {
        System.out.println(userName + ":" + age);
        return "OK";
    }

image-20240711125056074

image-20240711125032405

強制傳遞:註釋@RequestParam(name/value="請求引數名", require = true(預設,必須傳遞name))

	@RequestMapping("/simpleParam")
    public String simpleParam(@RequestParam(name = "name") String userName, int age) {
        System.out.println(userName + ":" + age);
        return "OK";
    }

image-20240711125329677

image-20240711125422382

image-20240711125459798

不傳遞會報錯,因為預設required=true,除非required=false

image-20240711125552021

image-20240711125634325

required=false,不傳遞name:

image-20240711125956568

image-20240711125935768

image-20240711124752750

實體引數

要求:請求引數名與形參物件屬性名相同

簡單實體引數

實體類

public class User {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Springboot方法

@RestController
public class HelloController {
//    實體引數
    @RequestMapping("/simplePojo")
    public String simplePojo(User user) {
        System.out.println(user);
        return "OK";
    }
}

image-20240711140008972

image-20240711140020470

複雜實體引數

實體類:address實體類不要封裝有參建構函式,否則報錯

public class Address {
    private String province;
    private String city;

//    不要寫有參建構函式
    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        return "Address{" +
                "province='" + province + '\'' +
                ", city='" + city + '\'' +
                '}';
    }
}
public class User {
    private String name;
    private int age;
    private Address address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address=" + address +
                '}';
    }
}

Springboot方法

@RestController
public class HelloController {
//  複雜實體方法
    @RequestMapping("/complexPojo")
    public String complexPojo(User user) {
        System.out.println(user);
        return "OK";
    }
}

image-20240711142633477

image-20240808012550880

陣列集合引數

image-20240711145749897

@RestController
public class HelloController {
//    陣列集合引數
    @RequestMapping("/arrayParam")
    public String arrayParam(String[] hobby) {
        System.out.println(Arrays.toString(hobby));
        return "OK";
    }
}
陣列引數

image-20240711144741417

image-20240711144721608

集合引數

請求引數名與形參集合名稱相同且請求引數為多個,@RequestParam 繫結引數關係

注意:一定要加註解image-20240711145449040

@RestController
public class HelloController {
//    集合引數
    @RequestMapping("/listParam")
    public String listParam(@RequestParam List<String> hobby) {
        System.out.println(hobby);
        return "OK";
    }
}

image-20240711145522033

image-20240711145310933

日期時間引數

使用 @DateTimeFormat 註解完成日期引數格式轉換

@RestController
public class HelloController {
//    日期時間引數
    @RequestMapping("/dateParam")
//    方法形參:加註解指定日期格式,宣告一個日期時間物件updateTime
    public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime updateTime) {
        System.out.println(updateTime);
        return "OK";
    }
}

image-20240711151746418

image-20240711151755037

JSON引數

JSON資料鍵名與形參物件屬性名相同,定義POJO型別形參即可接收引數,需要使用 @RequestBody 標識

注意:實體類不要寫有參構造器

@RestController
public class HelloController {
    @RequestMapping("/jsonParam")
    public String jsonParam(@RequestBody User user) {
        System.out.println(user);
        return "OK";
    }
}

image-20240711153821547

// Json引數如下
{
    "name": "itheima", 
    "age": 22, 
    "address": {
        "province": "安徽", 
        "city": "阜陽"
    }
}

image-20240711153138470

路徑引數

如果方法形參名稱與請求引數名稱不匹配,可以使用 @RequestParam 完成對映

@RestController
public class HelloController {
//    路徑引數-單個引數
    @RequestMapping("/path/{id}")
    public String pathParam(@PathVariable Integer id) {
        System.out.println(id);
        return "OK";
    }
}

image-20240711154632827

image-20240711154717633

多個引數

// 多個引數
@RequestMapping("/path/{id}/{name}")
public String pathParam(@PathVariable Integer id, @PathVariable String name) {
    System.out.println(id + ":" + name);
    return "OK";
}

image-20240711155220405

總結-引數

image-20240711155533572

響應

image-20240711200958031

@RestController
public class RequestController {
//		響應-字串
    @RequestMapping("/Hello")
    public String hello() {
        System.out.println("Hello!!!");
        return "Hello world!!!";
    }
//    響應-物件
    @RequestMapping("/getAddr")
    public Address getAddr() {
        Address addr = new Address();
        addr.setProvince("廣東");
        addr.setCity("深圳");
        return addr;
    }
//    響應-集合
    @RequestMapping("/listAddr")
    public List<Address> listAddr() {
        List<Address> list = new ArrayList<>();

        Address addr1 = new Address();
        addr1.setProvince("廣東");
        addr1.setCity("深圳");

        Address addr2 = new Address();
        addr2.setProvince("陝西");
        addr2.setCity("西安");

        list.add(addr1);
        list.add(addr2);
        return list;
    }
}

響應資料形式不一,不便管理,難以維護

image-20240711202342898

image-20240711202351688

image-20240711202405755

統一響應結果

image-20240711204736561

Result類

/**
 * 統一響應結果封裝類
 */
public class Result {
    private Integer code ;//1 成功 , 0 失敗
    private String msg; //提示資訊
    private Object data; //資料 data

    public Result() {
    }
    public Result(Integer code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }

    public static Result success(Object data){
        return new Result(1, "success", data);
    }
    public static Result success(){
        return new Result(1, "success", null);
    }
    public static Result error(String msg){
        return new Result(0, msg, null);
    }

    @Override
    public String toString() {
        return "Result{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }
}
@RestController
public class ResponseController {
//    統一響應結果
    //     響應-字串
    @RequestMapping("/Hello")
    public Result hello() {
        System.out.println("Hello!!!");
        return Result.success("Hello world!!!");
    }
    //    響應-物件
    @RequestMapping("/getAddr")
    public Result getAddr() {
        Address addr = new Address();
        addr.setProvince("廣東");
        addr.setCity("深圳");
        System.out.println(addr);
        return Result.success(addr);
    }
    //    響應-集合
    @RequestMapping("/listAddr")
    public Result listAddr() {
        List<Address> list = new ArrayList<>();

        Address addr1 = new Address();
        addr1.setProvince("廣東");
        addr1.setCity("深圳");

        Address addr2 = new Address();
        addr2.setProvince("陝西");
        addr2.setCity("西安");

        list.add(addr1);
        list.add(addr2);
        System.out.println(addr1);
        System.out.println(addr2);
        System.out.println(list);
        return Result.success(list);
    }
}

image-20240711204431418

image-20240712103838316

image-20240711204452888

響應-案例

image-20240711204942051

image-20240711211038728

Springboot專案的靜態資源(html,css,js等前端資源)預設存放目錄為:classpath:/static 、 classpath:/public、 classpath:/resources

empController

package com.itheima.controller;

import com.itheima.pojo.Emp;
import com.itheima.pojo.Result;
import com.itheima.utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class empController {
    @RequestMapping("/listEmp")
    public Result listEmp() {
//        動態獲取獲取靜態檔案路徑,路徑中不含中文
        String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
//        實在不行就拿絕對路徑
//        String file = "C:\\Users\\bfs\\Desktop\\JavaWeb\\study\\JavaWeb_project\\springboot-web-quickstart\\src\\main\\resources\\emp.xml";
//        載入emp.xml,並解析資料
        List<Emp> empList = XmlParserUtils.parse(file, Emp.class);

//        處理gender、job欄位
        empList.stream().forEach(emp -> {
            String gender = emp.getGender(); // gender: 1 男 2 女
            if("1".equals(gender)) {
                emp.setGender("男");
            } else if("2".equals(gender)) {
                emp.setGender("女");
            }

            String job = emp.getJob(); // job: 1:講師 2:班主任 3:就業指導
            if("1".equals(job)) {
                emp.setJob("講師");
            } else if("2".equals(job)) {
                emp.setJob("班主任");
            } else if("3".equals(job)) {
                emp.setJob("就業指導");
            }
        });

//        返回結果
        return Result.success(empList);
    }
}

介面除錯

image-20240711214651173

前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>員工資訊</title>
</head>

<link rel="stylesheet" href="element-ui/index.css">
<script src="./js/vue.js"></script>
<script src="./element-ui/index.js"></script>
<script src="./js/axios-0.18.0.js"></script>

<body>
    <h1 align="center">員工資訊列表展示</h1>
    <div id="app">
        <el-table :data="tableData" style="width: 100%"  stripe border >
            <el-table-column prop="name" label="姓名" align="center" min-width="20%"></el-table-column>
            <el-table-column prop="age" label="年齡" align="center" min-width="20%"></el-table-column>
            <el-table-column label="影像" align="center"  min-width="20%">
                <template slot-scope="scope">
                    <el-image :src="scope.row.image" style="width: 80px; height: 50px;"></el-image>
                </template>
            </el-table-column>
            <el-table-column prop="gender" label="性別" align="center"  min-width="20%"></el-table-column>
            <el-table-column prop="job" label="職位" align="center"  min-width="20%"></el-table-column>
        </el-table>
    </div>
</body>

<style>
    .el-table .warning-row {
        background: oldlace;
    }
    .el-table .success-row {
        background: #f0f9eb;
    }
</style>

<script>
    new Vue({
        el: "#app",
        data() {
            return {
                tableData: []
            }
        },
        mounted(){
            axios.get('/listEmp').then(res=>{
                if(res.data.code){
                    this.tableData = res.data.data;
                }
            });
        },
        methods: {
        }
    });
</script>
</html>

渲染效果

image-20240711215003468

分層解耦

三層結構

image-20240712174037225

image-20240712174101121

image-20240712205524016

檔案結構

image-20240712205651627

dao介面

package com.itheima.dao;

import com.itheima.pojo.Emp;

import java.util.List;

public interface EmpDao {
    List<Emp> listEmp();
}

dao介面的實現類

package com.itheima.dao.impl;

import com.itheima.dao.EmpDao;
import com.itheima.pojo.Emp;
import com.itheima.utils.XmlParserUtils;

import java.util.List;

public class EmpDaoA implements EmpDao {
    @Override
    public List<Emp> listEmp() {
//        動態獲取獲取靜態檔案路徑,路徑中不含中文
        String file = this.getClass().getClassLoader().getResource("emp.xml").getFile();
//        實在不行就拿絕對路徑
//        String file = "C:\\Users\\bfs\\Desktop\\JavaWeb\\study\\JavaWeb_project\\springboot-web-quickstart\\src\\main\\resources\\emp.xml";
//        載入emp.xml,並解析資料
        List<Emp> empList = XmlParserUtils.parse(file, Emp.class);
        return empList;
    }
}

service介面

package com.itheima.service;

import com.itheima.pojo.Emp;

import java.util.List;

public interface EmpService {
    List<Emp> listEmp();
}

service介面實現類

package com.itheima.service.impl;

import com.itheima.dao.EmpDao;
import com.itheima.dao.impl.EmpDaoA;
import com.itheima.pojo.Emp;
import com.itheima.service.EmpService;

import java.util.List;

public class EmpServiceA implements EmpService {
    private EmpDao empDao = new EmpDaoA();
    @Override
    public List<Emp> listEmp() {
        List<Emp> empList = empDao.listEmp();

//        處理gender、job欄位
        empList.stream().forEach(emp -> {
            String gender = emp.getGender(); // gender: 1 男 2 女
            if("1".equals(gender)) {
                emp.setGender("男");
            } else if("2".equals(gender)) {
                emp.setGender("女");
            }

            String job = emp.getJob(); // job: 1:講師 2:班主任 3:就業指導
            if("1".equals(job)) {
                emp.setJob("講師");
            } else if("2".equals(job)) {
                emp.setJob("班主任");
            } else if("3".equals(job)) {
                emp.setJob("就業指導");
            }
        });
        return empList;
    }
}

controller

package com.itheima.controller;

import com.itheima.pojo.Emp;
import com.itheima.pojo.Result;
import com.itheima.service.EmpService;
import com.itheima.service.impl.EmpServiceA;
import com.itheima.utils.XmlParserUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class empController {
    private EmpService empService = new EmpServiceA();
    @RequestMapping("/listEmpA")
    public Result listEmpA() {
        List<Emp> list = empService.listEmp();
        return Result.success(list);
    }
}

image-20240712210013089

存在的問題:耦合

image-20240712214311380

分層解耦

  • 內聚:軟體中各個功能模組內部的功能聯絡。
  • 耦合:衡量軟體中各個層/模組之間的依賴、關聯的程度。
  • 軟體設計原則:高內聚,低耦合

控制反轉: Inversion Of Control,簡稱IOC。物件的建立控制權由程式自身轉移到外部(容器),這種思想稱為控制反轉。

依賴注入: Dependency Injection,簡稱DI。容器為應用程式提供執行時,所依賴的資源,稱之為依賴注入。

Bean物件:IOC容器中建立、管理的物件,稱之為bean。

image-20240712212247135

image-20240712214735786

image-20240712214746554

IOC

Bean的宣告

要把某個物件交給IOC容器管理,需要在對應的類上加上如下註解之一

image-20240714150227500

宣告bean的時候,可以透過value屬性指定bean的名字,如果沒有指定,預設為類名首字母小寫。

使用以上四個註解都可以宣告bean,但是在springboot整合web開發中,宣告控制器bean只能用@Controller。

Bean元件掃描

image-20240714174813769

DI

image-20240714212833177

image-20240714212816267

image-20240714212751700

Mybatis

Mybatis入門

Mybatis簡介

image-20240717163525634

官網:https://mybatis.org/mybatis-3/zh/index.html

入門程式

查詢user表中資料

image-20240717165442863

image-20240717163657396

image-20240717163714267

image-20240717163722705

image-20240717163733064

image-20240717163806068

mapper介面

package com.itheima.mapper;

import com.itheima.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

//執行時,會自動生成該介面的實現類物件(動態代理,代理物件),並且將該物件交給IOC容器管理
@Mapper
public interface UserMapper {

//    查詢sql語句
    @Select("select * from user")
    public List<User> list();

}

實體User類

package com.itheima.pojo;

public class User {
    private Integer id;
    private String name;
    private Short age;
    private Short gender;
    private String phone;

    public User(Integer id, String name, Short age, Short gender, String phone) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.phone = phone;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Short getAge() {
        return age;
    }

    public void setAge(Short age) {
        this.age = age;
    }

    public Short getGender() {
        return gender;
    }

    public void setGender(Short gender) {
        this.gender = gender;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                ", phone='" + phone + '\'' +
                '}';
    }
}

資料庫配置資訊

application.properties檔案

#驅動類名稱
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#資料庫連線的url
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
#連線資料庫的使用者名稱
spring.datasource.username=root
#連線資料庫的密碼
spring.datasource.password=1234

測試類

package com.itheima;

import com.itheima.mapper.UserMapper;
import com.itheima.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

//Springboot整合單元測試的註解
@SpringBootTest
class SpringbootMybatisQuickstartApplicationTests {

    @Test
    void contextLoads() {
    }

//    依賴注入
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testListUser() {
        List<User> userList = userMapper.list();
        userList.stream().forEach(user -> {
            System.out.println(user);
        });
    }

}

執行結果

image-20240717165531588

JDBC介紹(不需要掌握)

image-20240717182920729

image-20240717182857435

image-20240717182958182

資料庫連線池

image-20240717191426526

image-20240717191521564

德魯伊:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter

image-20240717191554964

lombok

lombok是一個實用的Java類庫,能透過註解的形式自動生成構造器、getter/setter、equals、hashcode、toString等方法,並可以自動化生成日誌變數,簡化java開發、提高效率。

image-20240717192720905

Mybatis基礎操作

刪除

image-20240717204053364

package com.itheima.mapper;

import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;

//Mapper介面
@Mapper
public interface EmpMapper {

    //    刪除員工
    @Delete("delete from emp where id = #{id}")
//    void delete(Integer id);
//    返回刪除幾條資料
    int delete(Integer id);
}
package com.itheima;

import com.itheima.mapper.EmpMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

//測試類
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
//    防止標紅 required
    @Autowired(required = false)
    private EmpMapper empMapper;

    @Test
    void contextLoads() {
        empMapper.delete(17);
    }
}

image-20240717210708749

image-20240717210730651

SQL隱碼攻擊是透過操作輸入的資料來修改事先定義好的SQL語句,以達到執行程式碼對伺服器進行攻擊的方法。

image-20240717210758688

image-20240717210819139

增添

image-20240717213805969

image-20240717213817065

實體類

package com.itheima.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDate;
import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
    private Integer id;
    private String username;
    private String password;
    private String name;
    private Short gender;
    private String image;
    private Short job;
    private LocalDate entrydate;
    private Integer deptId;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

介面

package com.itheima.mapper;

import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;

//Mapper介面
@Mapper
public interface EmpMapper {
    
//    新增員工
    @Insert("insert into emp (username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (" +
            "#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
    void insert(Emp emp);

}

測試介面

package com.itheima;

import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDate;
import java.time.LocalDateTime;

//測試類
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
//    防止標紅 required
    @Autowired(required = false)
    private EmpMapper empMapper;
    @Test
    void testAdd() {
//        建立一個實體類emp
        Emp emp = new Emp();
        emp.setUsername("Tom3");
        emp.setName("湯姆3");
        emp.setImage("1.jpg");
        emp.setGender((short)1);
        emp.setJob((short)1);
        emp.setEntrydate(LocalDate.of(2000,1,1));
        emp.setCreateTime(LocalDateTime.now());
        emp.setUpdateTime(LocalDateTime.now());
        emp.setDeptId(1);

        empMapper.insert(emp);
    }

}

主鍵返回

描述:在資料新增成功後,需要獲取插入資料庫資料的主鍵。如:新增套餐資料時,還需要維護套餐菜品關係表資料。

image-20240717214308023

image-20240717214638806

更新

Mapper介面

package com.itheima.mapper;

import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;

//Mapper介面
//執行時,會自動生成該介面的實現類物件(動態代理,代理物件),並且將該物件交給IOC容器管理
@Mapper
public interface EmpMapper {
//    修改員工
    @Update("update emp set username = #{username}, name = #{name}, gender = #{gender}, image = #{image}, job = #{job}, " +
            "entrydate = #{entrydate}, dept_id = #{deptId}, update_time = #{updateTime} where id = #{id}")
    void update(Emp emp);
}

測試類

package com.itheima;

import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDate;
import java.time.LocalDateTime;

//測試類
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
//    依賴注入
//    防止標紅 required
    @Autowired(required = false)
    private EmpMapper empMapper;

    @Test
    void testUpdate() {
        Emp emp = new Emp();
        emp.setId(22);
        emp.setUsername("更新操作");
        emp.setName("湯姆666");
        emp.setImage("1.jpg");
        emp.setGender((short)1);
        emp.setJob((short)1);
        emp.setEntrydate(LocalDate.of(2000,1,1));
        emp.setUpdateTime(LocalDateTime.now());
        emp.setDeptId(2);

        empMapper.update(emp);
    }
}

查詢

Mapper介面

package com.itheima.mapper;

import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;

//Mapper介面
//執行時,會自動生成該介面的實現類物件(動態代理,代理物件),並且將該物件交給IOC容器管理
@Mapper
public interface EmpMapper {
//    查詢員工
    @Select("select * from emp where id = #{id}")
    Emp selectById(Integer id);
}

測試類

package com.itheima;

import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDate;
import java.time.LocalDateTime;

//測試類
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
//    依賴注入
//    防止標紅 required
    @Autowired(required = false)
    private EmpMapper empMapper;
    
    @Test
    void testSelect() {
        Emp emp = empMapper.selectById(22);
        System.out.println(emp);
    }
}

執行結果在下面

資料封裝

問題:資料庫中欄位名與實體類中屬性名不一致

image-20240719011156267

image-20240719133102585

解決方案

image-20240719133236436

  • 方案一:起別名
@Select("select id, username, password, name, gender, image, job, entrydate, " +
        "dept_id deptId, create_time createTime, update_time updateTime from emp where id = #{id}")
Emp selectById(Integer id);
  • 方案二:@Results({@Result()})註解
@Results({
        @Result(column = "dept_id", property = "deptId"),
        @Result(column = "create_time", property = "createTime"),
        @Result(column = "update_time", property = "updateTime")
})
@Select("select * from emp where id = #{id}")
Emp selectById(Integer id);
  • 方案三:開啟駝峰命名
#開啟駝峰命名用於主句封裝,前提是資料庫欄位明明嚴格採用下劃線命名並且實體類屬性名採用駝峰明明
mybatis.configuration.map-underscore-to-camel-case=true

然後直接select

傳遞多個引數

image-20240719133305882

image-20240719133334298

Mapper介面,多個引數要寫@Param註解

image-20240719133430405

package com.itheima.mapper;

import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;

import java.time.LocalDate;
import java.util.List;

//Mapper介面
//執行時,會自動生成該介面的實現類物件(動態代理,代理物件),並且將該物件交給IOC容器管理
@Mapper
public interface EmpMapper {
//    單個引數查詢
//    用$代表字串拼接
//    @Select("select * from emp where name like '%${name}%'" +
//            " order by update_time desc ")
//    List<Emp> selectByInfo(String name);

//    多引數要@Param註釋
    @Select("select * from emp where name like concat('%', #{name}, '%') and gender = #{gender} and " +
            "entrydate between #{begin} and #{end} order by update_time desc ")
//    List<Emp> selectByInfo(String name, Short gender, LocalDate begin , LocalDate end);
    List<Emp> selectByInfo(@Param("name")String name, @Param("gender")Short gender, @Param("begin")LocalDate begin , @Param("end")LocalDate end);

//    或者多個引數封裝到物件裡
    @Select("select * from emp where name like concat('%', #{name}, '%') and gender = #{gender} and entrydate >= #{entrydate} order by update_time desc")
    List<Emp> selectMulti(Emp emp);
}
package com.itheima;

import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

//測試類
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
//    依賴注入
//    防止標紅 required
    @Autowired(required = false)
    private EmpMapper empMapper;
    
    @Test
    void testSelectByInfo() {
//        測試查詢單個引數,帶有模糊匹配
//        List<Emp> list = empMapper.selectByInfo((short) 1, LocalDate.of(2010, 1, 1), LocalDate.of(2020, 1, 1));
//        List<Emp> empList = empMapper.selectByInfo("張");
//        測試查詢多個引數
        List<Emp> empList = empMapper.selectByInfo("張", (short) 1, LocalDate.of(2010, 1, 1), LocalDate.of(2020, 1, 1));
        System.out.println(empList);
        System.out.println(empList.size());
    }

//    測試查詢多個引數
    @Test
    void testSelectMulti() {
        Emp emp = new Emp();
        emp.setName("張");
        emp.setGender((short) 1);
        emp.setEntrydate(LocalDate.of(2010, 1, 1));
        List<Emp> list = empMapper.selectMulti(emp);
        System.out.println(list);
    }
}

可能存在的問題,Mapper傳參要寫@Param註解,否則傳參失敗報錯

image-20240719134007575

XML對映檔案

三點規範:

  • XML對映檔案的名稱與Mapper介面名稱一致,並且將XML對映檔案和Mapper介面放置在相同包下(同包同名)。

  • XML對映檔案的namespace屬性為Mapper介面全限定名一致。

  • XML對映檔案中sql語句的id與Mapper 介面中的方法名一致,並保持返回型別一致。

image-20240719135831555

image-20240719135944597

XML檔案

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--    resultType:單條記錄所封裝的型別的全限定類名即Emp類-->
    <select id="selectByInfo" resultType="com.itheima.pojo.Emp">
        select * from emp where name like concat('%', #{name}, '%') and gender = #{gender} and entrydate between #{begin} and #{end} order by update_time desc
    </select>

</mapper>

Mapper中的XML對映

package com.itheima.mapper;

import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;

import java.time.LocalDate;
import java.util.List;

//Mapper介面
//執行時,會自動生成該介面的實現類物件(動態代理,代理物件),並且將該物件交給IOC容器管理
@Mapper
public interface EmpMapper {
//    xml對映
    List<Emp> selectByInfo(@Param("name")String name, @Param("gender")Short gender, @Param("begin")LocalDate begin , @Param("end")LocalDate end);
}
package com.itheima;

import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

//測試類
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
//    依賴注入
//    防止標紅 required
    @Autowired(required = false)
    private EmpMapper empMapper;

    @Test
    void testSelectByInfo() {
//        測試查詢單個引數,帶有模糊匹配
//        List<Emp> list = empMapper.selectByInfo((short) 1, LocalDate.of(2010, 1, 1), LocalDate.of(2020, 1, 1));
//        List<Emp> empList = empMapper.selectByInfo("張");
//        測試查詢多個引數
        List<Emp> empList = empMapper.selectByInfo("張", (short) 1, LocalDate.of(2010, 1, 1), LocalDate.of(2020, 1, 1));
        System.out.println(empList);
        System.out.println(empList.size());
    }
}

動態SQL

<if> <where>

where標籤可以自動刪除語句開頭的and

image-20240719152004989

注意SQL語句中的and在每個if開頭不可省略

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">

<!--    動態查詢-->
    <!--    resultType:單條記錄所封裝的型別的全限定類名即Emp類-->
        <select id="selectByInfo" resultType="com.itheima.pojo.Emp">
            select * from emp
            <where>
                <if test="name != null">
                    name like concat('%', #{name}, '%')
                </if>
                <if test="gender != null">
                    and gender = #{gender}
                </if>
                <if test="begin != null and end != null">
                    and entrydate between #{begin} and #{end}
                </if>
            </where>
            order by update_time desc
        </select>
</mapper>

mapper

package com.itheima.mapper;

import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;

import java.time.LocalDate;
import java.util.List;

//Mapper介面
//執行時,會自動生成該介面的實現類物件(動態代理,代理物件),並且將該物件交給IOC容器管理
@Mapper
public interface EmpMapper {
//    xml對映
//    動態查詢
    List<Emp> selectByInfo(@Param("name")String name, @Param("gender")Short gender, @Param("begin")LocalDate begin , @Param("end")LocalDate end);
}

test測試類

package com.itheima;

import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

//測試類
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
//    依賴注入
//    防止標紅 required
    @Autowired(required = false)
    private EmpMapper empMapper;

    @Test
    void testSelectByInfo() {

//        動態SQL
//        List<Emp> empList = empMapper.selectByInfo("張", (short) 1, null, null);
//        List<Emp> empList = empMapper.selectByInfo("張", null, null, null);
        List<Emp> empList = empMapper.selectByInfo(null, null, null, null);
        System.out.println(empList);
    }
}

<set>

image-20240719152709497

自動刪除語句最後的逗號

image-20240719165836108

動態更新xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">

<!--    動態更新操作-->
    <update id="updateDynamic">
        update emp
        <set>
            <if test="username != null">username = #{username},</if>
            <if test="name != null">name = #{name},</if>
            <if test="gender != null">gender = #{gender},</if>
            <if test="image != null">image = #{image},</if>
            <if test="job != null">job = #{job},</if>
            <if test="entrydate != null">entrydate = #{entrydate},</if>
            <if test="deptId != null">dept_id = #{deptId},</if>
            <if test="updateTime != null">update_time = #{updateTime},</if>
        </set>
        where id = #{id}
    </update>
</mapper>

mapper

package com.itheima.mapper;

import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;

import java.time.LocalDate;
import java.util.List;

//Mapper介面
//執行時,會自動生成該介面的實現類物件(動態代理,代理物件),並且將該物件交給IOC容器管理
@Mapper
public interface EmpMapper {
//    動態修改
    void updateDynamic(Emp emp);
}

測試類

package com.itheima;

import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

//測試類
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
//    依賴注入
//    防止標紅 required
    @Autowired(required = false)
    private EmpMapper empMapper;

    //    案例:動態更新
    @Test
    void testUpdateDynamic() {
//        封裝的物件屬性是根據每一條SQL語句set,不靈活,如果缺少了某條會直接賦值null
        Emp emp = new Emp();
        emp.setId(26);
        emp.setUsername("動態更新");
        emp.setName("動態更新666");
//        emp.setImage("1.jpg");
        emp.setGender((short)2);
        emp.setJob((short)1);
//        emp.setEntrydate(LocalDate.of(2000,1,1));
        emp.setUpdateTime(LocalDateTime.now());
        emp.setDeptId(2);

//        empMapper.update(emp);
//        動態更新
        empMapper.updateDynamic(emp);
    }
}

<foreach>

image-20240719172120553

image-20240719172101053

image-20240719172051725

xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">

<!--    批次刪除-->
<!--
    collection: 遍歷的集合
    item: 遍歷出來的元素
    separator: 分隔符
    open: 遍歷開始前拼接的SQL片段
    close: 遍歷結束後拼接的SQL片段
-->
    <delete id="deleteByIds">
        delete from emp where id in
        <foreach collection="ids" item="id" separator=", " open="(" close=")">
            #{id}
        </foreach>
    </delete>

</mapper>

mapper

package com.itheima.mapper;

import com.itheima.pojo.Emp;
import org.apache.ibatis.annotations.*;

import java.time.LocalDate;
import java.util.List;

//Mapper介面
//執行時,會自動生成該介面的實現類物件(動態代理,代理物件),並且將該物件交給IOC容器管理
@Mapper
public interface EmpMapper {
//    xml對映
//    批次刪除,返回刪除幾條資料
    int deleteByIds(@Param("ids") List<Integer> ids);
}

test測試類

package com.itheima;

import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

//測試類
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
//    依賴注入
//    防止標紅 required
    @Autowired(required = false)
    private EmpMapper empMapper;

    @Test
    void testDeleteByIds() {
//        工具類Arrays的asList方法建立集合
        List<Integer> ids = Arrays.asList(13, 14, 15);
        int cnt = empMapper.deleteByIds(ids);
        System.out.println(cnt);
    }
}

<sql><include>

  • :定義可重用的 SQL 片段。

  • :透過屬性refid,指定包含的sql片段。

以動態查詢為例

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">

<!--    sql片段-->
    <sql id="commonSelect">select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp</sql>

<!--    動態查詢-->
        <select id="selectByInfo" resultType="com.itheima.pojo.Emp">
            <!--    引入sql片段-->
            <include refid="commonSelect"/>
            <where>
                <if test="name != null">
                    name like concat('%', #{name}, '%')
                </if>
                <if test="gender != null">
                    and gender = #{gender}
                </if>
                <if test="begin != null and end != null">
                    and entrydate between #{begin} and #{end}
                </if>
            </where>
            order by update_time desc
        </select>
</mapper>

登入認證

JWT令牌

image-20240728005633150

組成

image-20240728005754826

依賴

<!--        JWT令牌-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

jwt令牌生成和校驗:

package com.itheima;

import com.itheima.controller.UploadController;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@SpringBootTest
class TliasApplicationTests {

//    測試生成jwt令牌
    @Test
    public void testJWT() {
//        Map容器儲存自定義內容
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", 1);
        claims.put("name", "tom");

        String jwt = Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, "itheima") // 官網獲得簽名演算法
                .setClaims(claims) // 存入自定義內容
                .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000)) // 設定時間為一小時以後
                .compact();
        System.out.println(jwt);
    }

//    解析jwt令牌
    @Test
    public void testParseJWT() {
        Claims claims = Jwts.parser()
                .setSigningKey("itheima")
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTcyMjEwNDgyMH0.U6oovJ5AJz7IpTjnMwQxuSxauHtwzGtPkm6ZRMH2hoo")
                .getBody();
        System.out.println(claims);
    }
}

image-20240728015545607

過濾器Filter

image-20240728015615492

package com.itheima.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;


//配置filter
//攔截所有請求
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {

//    初始化方法,只呼叫一次
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
//        Filter.super.init(filterConfig);
        System.out.println("init初始化方法被執行了");
    }

//    每次攔截請求之後都會呼叫,會呼叫多次
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("攔截到了請求,Demo放行前邏輯");

//        放行
        filterChain.doFilter(servletRequest, servletResponse);

        System.out.println("攔截到了請求,Demo放行後邏輯");
    }

//    銷燬方法,只呼叫一次
    @Override
    public void destroy() {
//        Filter.super.destroy();
        System.out.println("destroy銷燬方法被執行了");
    }
}

image-20240728234830934

image-20240728235018873

image-20240729205439160

package com.itheima.filter;

import com.alibaba.fastjson.JSONObject;
import com.itheima.pojo.Result;
import com.itheima.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
//@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//        獲取請求url
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

//        判斷請求中是否包含login,包含就放行
        String url = request.getRequestURL().toString();
        log.info("請求的URL:{}", url);

//        獲取請求頭中的令牌token
        if(url.contains("login")) {
            log.info("登入操作,放行");
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

//        獲取請求頭中的令牌
        String jwt = request.getHeader("token");
//        除錯,檢視令牌
        log.info("jwt:{}", jwt);
//        判斷令牌是否存在,如果不存在,返回錯誤資訊
//        字串是否有長度
        if(!StringUtils.hasLength(jwt)) {
            log.info("請求頭token為空,返回未登入的資訊");
            Result error = Result.error("NOT_LOGIN");

//            手動轉換,物件-->JSON---使用阿里fastJSON
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);
            return;
        }

//        解析token,如果解析失敗返回錯誤結果(未登入);
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) { // 出現異常,jwt令牌解析失敗
//            throw new RuntimeException(e);
            e.printStackTrace();
            log.info("解析令牌失敗,返回未登入的資訊");
            Result error = Result.error("NOT_LOGIN");

//            手動轉換,物件-->JSON---使用阿里fastJSON
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);
            return;
        }

//        放行
        log.info("令牌合法,放行");
        filterChain.doFilter(servletRequest, servletResponse);

    }
}

攔截器

image-20240729205520543

package com.itheima.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.itheima.pojo.Result;
import com.itheima.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
//    目標資源方法執行前執行,返回true代表放行,返回false代表不放請
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle...");

//        //        獲取請求url
//        HttpServletRequest request = (HttpServletRequest) servletRequest;
//        HttpServletResponse response = (HttpServletResponse) servletResponse;

//        判斷請求中是否包含login,包含就放行
        String url = request.getRequestURL().toString();
        log.info("請求的URL:{}", url);

//        獲取請求頭中的令牌token
        if(url.contains("login")) {
            log.info("登入操作,放行");
//            filterChain.doFilter(servletRequest, servletResponse);
            return true;
        }

//        獲取請求頭中的令牌
        String jwt = request.getHeader("token");
//        除錯,檢視令牌
        log.info("jwt:{}", jwt);
//        判斷令牌是否存在,如果不存在,返回錯誤資訊
//        字串是否有長度
        if(!StringUtils.hasLength(jwt)) {
            log.info("請求頭token為空,返回未登入的資訊");
            Result error = Result.error("NOT_LOGIN");

//            手動轉換,物件-->JSON---使用阿里fastJSON
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);
//            不放行
            return false;
        }

//        解析token,如果解析失敗返回錯誤結果(未登入);
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) { // 出現異常,jwt令牌解析失敗
//            throw new RuntimeException(e);
            e.printStackTrace();
            log.info("解析令牌失敗,返回未登入的資訊");
            Result error = Result.error("NOT_LOGIN");

//            手動轉換,物件-->JSON---使用阿里fastJSON
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);
            return true;
        }

//        放行
        log.info("令牌合法,放行");
        filterChain.doFilter(servletRequest, servletResponse);
//        return HandlerInterceptor.super.preHandle(request, response, handler);
        return true;
//        return false;
    }

//    目標方法執行後執行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...");
//        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

//    檢視渲染玩不後執行,最後執行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...");
//        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

image-20240729211423580

區別

image-20240729212708347

image-20240729212736770

package com.itheima.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.itheima.pojo.Result;
import com.itheima.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
//    目標資源方法執行前執行,返回true代表放行,返回false代表不放請
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle...");

//        //        獲取請求url
//        HttpServletRequest request = (HttpServletRequest) servletRequest;
//        HttpServletResponse response = (HttpServletResponse) servletResponse;

//        判斷請求中是否包含login,包含就放行
        String url = request.getRequestURL().toString();
        log.info("請求的URL:{}", url);

//        獲取請求頭中的令牌token
        if(url.contains("login")) {
            log.info("登入操作,放行");
//            filterChain.doFilter(servletRequest, servletResponse);
            return true;
        }

//        獲取請求頭中的令牌
        String jwt = request.getHeader("token");
//        除錯,檢視令牌
        log.info("jwt:{}", jwt);
//        判斷令牌是否存在,如果不存在,返回錯誤資訊
//        字串是否有長度
        if(!StringUtils.hasLength(jwt)) {
            log.info("請求頭token為空,返回未登入的資訊");
            Result error = Result.error("NOT_LOGIN");

//            手動轉換,物件-->JSON---使用阿里fastJSON
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);
//            不放行
            return false;
        }

//        解析token,如果解析失敗返回錯誤結果(未登入);
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) { // 出現異常,jwt令牌解析失敗
//            throw new RuntimeException(e);
            e.printStackTrace();
            log.info("解析令牌失敗,返回未登入的資訊");
            Result error = Result.error("NOT_LOGIN");

//            手動轉換,物件-->JSON---使用阿里fastJSON
            String notLogin = JSONObject.toJSONString(error);
            response.getWriter().write(notLogin);
            return true;
        }

//        放行
        log.info("令牌合法,放行");
//        filterChain.doFilter(servletRequest, servletResponse);
//        return HandlerInterceptor.super.preHandle(request, response, handler);
        return true;
//        return false;
    }

//    目標方法執行後執行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...");
//        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

//    檢視渲染玩不後執行,最後執行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...");
//        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

全域性異常處理器

image-20240730000543803

package com.itheima.exception;

import com.itheima.pojo.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

//全域性異常處理器
@RestControllerAdvice
public class GlobalExceptionHandler {

//    捕獲所有異常
    @ExceptionHandler(Exception.class)
    public Result ex(Exception e) {
//        輸出堆疊資訊
        e.printStackTrace();
        return Result.error("對不起,操作失敗,請聯絡管理員");
    }
}

事務管理

@Transactional註解

image-20240730163706666

配置資訊

image-20240730012207590

image-20240730012252681

事務傳播

image-20240730125548749

image-20240730164714996

package com.itheima.service.impl;

import com.itheima.mapper.DeptLogMapper;
import com.itheima.mapper.DeptMapper;
import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Dept;
import com.itheima.pojo.DeptLog;
import com.itheima.service.DeptLogService;
import com.itheima.service.DeptService;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;


@Service
public class DeptServiceImpl implements DeptService {

    @Autowired
     private DeptMapper deptMapper;
    @Autowired
    private EmpMapper empMapper;
    @Autowired
    private DeptLogService deptLogService;

    @Override
    public List<Dept> list() {
        return deptMapper.list();
    }

    //事務管理,處理任何異常,預設只處理執行時異常
    @Transactional(rollbackFor = Exception.class)
//    @Transactional
//    刪除部門以及部門下的員工
    @Override
    public void delete(Integer id) throws Exception {
        try {
            deptMapper.deleteById(id);
//        用於測試事務
//            int i = 1 / 0;

//        用於測試事務
//            if(true){
//                throw new Exception("出錯啦...");
//            }
            empMapper.deleteByDeptId(id);
        } finally {
            DeptLog deptLog = new DeptLog();
            deptLog.setCreateTime(LocalDateTime.now());
            deptLog.setDescription("執行了解散部門的操作,此次解散的是" + id + "號部門");
            deptLogService.insert(deptLog);
        }
    }

    @Override
    public void add(Dept dept) {
//        補全時間資訊
        dept.setCreateTime(LocalDateTime.now());
        dept.setUpdateTime(LocalDateTime.now());
        deptMapper.addDept(dept);
    }
}

image-20240730192343855

AOP

  • AOP:Aspect Oriented Programming(面向切面程式設計、面向方面程式設計),其實就是面向特定方法程式設計。

image-20240805174854302

依賴

image-20240805183148613

快速入門:記錄原始方法執行時間

package com.itheima.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@Aspect // AOP類
public class TimeAspect {
    @Around("execution(* com.itheima.service.*.*(..))")  // 切入點表示式
    public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {

//        記錄時間
        long begin = System.currentTimeMillis();

//        呼叫原始方法
        Object proceed = joinPoint.proceed();

//        記錄時間
        long end = System.currentTimeMillis();

//        列印日誌:得到原始方法方法簽名 + 執行耗時
        log.info(joinPoint.getSignature() + "方法耗時{}s", (end - begin) / 1e3);

//        返回原始方法返回值
        return proceed;
    }
}

image-20240805183334096

應用場景

image-20240805183244464

優點

image-20240805183228240

核心概念

  • 連線點:JoinPoint,可以被AOP控制的方法(暗含方法執行時的相關資訊)
  • 通知:Advice,指哪些重複的邏輯,也就是共性功能(最終體現為一個方法)
  • 切入點:PointCut,匹配連線點的條件,通知僅會在切入點方法執行時被應用
  • 切面:Aspect,描述通知與切入點的對應關係(通知+切入點)
  • 目標物件:Target,通知所應用的物件

image-20240805184306032

image-20240805184319621

通知型別

  • @Around:環繞通知,此註解標註的通知方法在目標方法前、後都被執行
  • @Before:前置通知,此註解標註的通知方法在目標方法前被執行
  • @After :後置通知,此註解標註的通知方法在目標方法後被執行,無論是否有異常都會執行
  • @AfterReturning : 返回後通知,此註解標註的通知方法在目標方法後被執行,有異常不會執行
  • @AfterThrowing : 異常後通知,此註解標註的通知方法發生異常後執行

@Pointcut

  • 該註解的作用是將公共的切點表示式抽取出來,需要用到時引用該切點表示式即可。
  • private:僅能在當前切面類中引用該表示式
  • public:在其他外部的切面類中也可以引用該表示式

image-20240805190403967

通知順序

1.不同切面類中,預設按照切面類的類名字母排序:

  • 目標方法前的通知方法:字母排名靠前的先執行
  • 目標方法後的通知方法:字母排名靠前的後執行

2.用 @Order(數字) 加在切面類上來控制順序

  • 目標方法前的通知方法:數字小的先執行
  • 目標方法後的通知方法:數字小的後執行

image-20240805191432172

切入點表示式

  • 切入點表示式:描述切入點方法的一種表示式
  • 作用:主要用來決定專案中的哪些方法需要加入通知
  • 常見形式:
    • execution(……):根據方法的簽名來匹配
    • @annotation(……) :根據註解匹配

execution

格式

image-20240805195158269

image-20240805195210752

image-20240805195231108

  • 根據業務需要,可以使用 且(&&)、或(||)、非(!) 來組合比較複雜的切入點表示式。
  • 書寫建議
    • 所有業務方法名在命名時儘量規範,方便切入點表示式快速匹配。如:查詢類方法都是 find 開頭,更新類方法都是 update開頭。
    • 描述切入點方法通常基於介面描述,而不是直接描述實現類,增強擴充性。
    • 在滿足業務需要的前提下,儘量縮小切入點的匹配範圍。如:包名匹配儘量不使用 ..,使用 * 匹配單個包。

@annotation

  • 用自定義註解提前標識切入點,能夠靈活的標註切入點
  • @annotation註解中寫標識的全類名
package com.itheima.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyLog {
}

image-20240805201203501

image-20240805200133702

連線點

  • 在Spring中用JoinPoint抽象了連線點,用它可以獲得方法執行時的相關資訊,如目標類名、方法名、方法引數等。
  • 對於 @Around 通知,獲取連線點資訊只能使用 ProceedingJoinPoint
  • 對於其他四種通知,獲取連線點資訊只能使用 JoinPoint ,它是 ProceedingJoinPoint 的父型別
package com.itheima.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Slf4j
@Aspect
@Component
public class Aspect1 {
    @Pointcut("execution(* com.itheima.service.DeptService.*(..))")
    private void pc() {
    }

    @Around("pc()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("before");

//        獲取物件類名
        String className = joinPoint.getTarget().getClass().getName();
        log.info("物件類名:{}", className);

//        獲取方法名
        String methodName = joinPoint.getSignature().getName();
        log.info("目標的方法名:{}", methodName);

//        獲取目標方法執行時傳入的引數
        Object[] args = joinPoint.getArgs();
        log.info("方法傳入的引數:{}", Arrays.toString(args));

//        放行 目標方法執行
        Object proceed = joinPoint.proceed();

//        獲取目標方法執行的返回值
        log.info("目標方法執行的返回值:{}", proceed);

        log.info("after");
        return proceed;
    }
}

相關文章