Maven
Apache Maven 是一個專案管理和構建工具,它基於專案物件模型(POM)的概念,透過一小段描述資訊來管理專案的構建。
Maven作用
依賴管理
統一的專案結構
專案構建
倉庫
安裝Maven
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
測試:
mvn -v
配置Maven環境
建立Maven專案
座標
匯入Maven專案
依賴配置
倉庫網址:https://mvnrepository.com/
依賴傳遞
顯示關係圖
A的依賴關係圖
B的依賴關係圖
C的依賴關係圖
排除依賴
依賴範圍
生命週期
在同一套生命週期中,當執行後面的階段時,前面的階段都會執行,上圖三套
後端Web開發
SpringBoot
入門
入門程式碼
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!!!";
}
}
啟動
HTTP協議
請求協議
相應協議
狀態碼大類
狀態碼分類 | 說明 |
---|---|
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
協議解析
Tomcat
基本使用
配置埠號:
部署:將檔案移動到webapps資料夾下即可
https://docs.spring.io/spring-boot/docs/2.7.4/reference/htmlsingle/#using.build-systems.starters
請求響應
請求
postman
簡單引數
如果方法形參名稱與請求引數名稱不匹配,可以使用 @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";
}
}
Springboot方式
@RestController
public class HelloController {
// Springboot方式
@RequestMapping("/simpleParam")
public String simpleParam(String name, int age) {
System.out.println(name + ":" + age);
return "OK";
}
}
GET請求
POST請求
請求引數名與方法變數名不同,不能傳遞
@RequestMapping("/simpleParam")
public String simpleParam(String userName, int age) {
System.out.println(userName + ":" + age);
return "OK";
}
強制傳遞:註釋@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";
}
不傳遞會報錯,因為預設required=true,除非required=false
required=false,不傳遞name:
實體引數
要求:請求引數名與形參物件屬性名相同
簡單實體引數
實體類
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";
}
}
複雜實體引數
實體類: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";
}
}
陣列集合引數
@RestController
public class HelloController {
// 陣列集合引數
@RequestMapping("/arrayParam")
public String arrayParam(String[] hobby) {
System.out.println(Arrays.toString(hobby));
return "OK";
}
}
陣列引數
集合引數
請求引數名與形參集合名稱相同且請求引數為多個,@RequestParam 繫結引數關係
注意:一定要加註解
@RestController
public class HelloController {
// 集合引數
@RequestMapping("/listParam")
public String listParam(@RequestParam List<String> hobby) {
System.out.println(hobby);
return "OK";
}
}
日期時間引數
使用 @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";
}
}
JSON引數
JSON資料鍵名與形參物件屬性名相同,定義POJO型別形參即可接收引數,需要使用 @RequestBody 標識
注意:實體類不要寫有參構造器
@RestController
public class HelloController {
@RequestMapping("/jsonParam")
public String jsonParam(@RequestBody User user) {
System.out.println(user);
return "OK";
}
}
// Json引數如下
{
"name": "itheima",
"age": 22,
"address": {
"province": "安徽",
"city": "阜陽"
}
}
路徑引數
如果方法形參名稱與請求引數名稱不匹配,可以使用 @RequestParam 完成對映
@RestController
public class HelloController {
// 路徑引數-單個引數
@RequestMapping("/path/{id}")
public String pathParam(@PathVariable Integer id) {
System.out.println(id);
return "OK";
}
}
多個引數
// 多個引數
@RequestMapping("/path/{id}/{name}")
public String pathParam(@PathVariable Integer id, @PathVariable String name) {
System.out.println(id + ":" + name);
return "OK";
}
總結-引數
響應
@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;
}
}
響應資料形式不一,不便管理,難以維護
統一響應結果
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);
}
}
響應-案例
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);
}
}
介面除錯
前端
<!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>
渲染效果
分層解耦
三層結構
檔案結構
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);
}
}
存在的問題:耦合
分層解耦
- 內聚:軟體中各個功能模組內部的功能聯絡。
- 耦合:衡量軟體中各個層/模組之間的依賴、關聯的程度。
- 軟體設計原則:高內聚,低耦合
控制反轉: Inversion Of Control,簡稱IOC。物件的建立控制權由程式自身轉移到外部(容器),這種思想稱為控制反轉。
依賴注入: Dependency Injection,簡稱DI。容器為應用程式提供執行時,所依賴的資源,稱之為依賴注入。
Bean物件:IOC容器中建立、管理的物件,稱之為bean。
IOC
Bean的宣告
要把某個物件交給IOC容器管理,需要在對應的類上加上如下註解之一
宣告bean的時候,可以透過value屬性指定bean的名字,如果沒有指定,預設為類名首字母小寫。
使用以上四個註解都可以宣告bean,但是在springboot整合web開發中,宣告控制器bean只能用@Controller。
Bean元件掃描
DI
Mybatis
Mybatis入門
Mybatis簡介
官網:https://mybatis.org/mybatis-3/zh/index.html
入門程式
查詢user表中資料
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);
});
}
}
執行結果
JDBC介紹(不需要掌握)
資料庫連線池
德魯伊:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
lombok
lombok是一個實用的Java類庫,能透過註解的形式自動生成構造器、getter/setter、equals、hashcode、toString等方法,並可以自動化生成日誌變數,簡化java開發、提高效率。
Mybatis基礎操作
刪除
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);
}
}
SQL隱碼攻擊是透過操作輸入的資料來修改事先定義好的SQL語句,以達到執行程式碼對伺服器進行攻擊的方法。
增添
實體類
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);
}
}
主鍵返回
描述:在資料新增成功後,需要獲取插入資料庫資料的主鍵。如:新增套餐資料時,還需要維護套餐菜品關係表資料。
更新
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);
}
}
執行結果在下面
資料封裝
問題:資料庫中欄位名與實體類中屬性名不一致
解決方案
- 方案一:起別名
@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
傳遞多個引數
Mapper介面,多個引數要寫@Param註解
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註解,否則傳參失敗報錯
XML對映檔案
三點規範:
-
XML對映檔案的名稱與Mapper介面名稱一致,並且將XML對映檔案和Mapper介面放置在相同包下(同包同名)。
-
XML對映檔案的namespace屬性為Mapper介面全限定名一致。
-
XML對映檔案中sql語句的id與Mapper 介面中的方法名一致,並保持返回型別一致。
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
注意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>
自動刪除語句最後的逗號
動態更新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>
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令牌
組成
依賴
<!-- 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);
}
}
過濾器Filter
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銷燬方法被執行了");
}
}
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);
}
}
攔截器
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);
}
}
區別
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);
}
}
全域性異常處理器
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註解
配置資訊
事務傳播
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);
}
}
AOP
- AOP:Aspect Oriented Programming(面向切面程式設計、面向方面程式設計),其實就是面向特定方法程式設計。
依賴
快速入門:記錄原始方法執行時間
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;
}
}
應用場景
優點
核心概念
- 連線點:JoinPoint,可以被AOP控制的方法(暗含方法執行時的相關資訊)
- 通知:Advice,指哪些重複的邏輯,也就是共性功能(最終體現為一個方法)
- 切入點:PointCut,匹配連線點的條件,通知僅會在切入點方法執行時被應用
- 切面:Aspect,描述通知與切入點的對應關係(通知+切入點)
- 目標物件:Target,通知所應用的物件
通知型別
- @Around:環繞通知,此註解標註的通知方法在目標方法前、後都被執行
- @Before:前置通知,此註解標註的通知方法在目標方法前被執行
- @After :後置通知,此註解標註的通知方法在目標方法後被執行,無論是否有異常都會執行
- @AfterReturning : 返回後通知,此註解標註的通知方法在目標方法後被執行,有異常不會執行
- @AfterThrowing : 異常後通知,此註解標註的通知方法發生異常後執行
@Pointcut
- 該註解的作用是將公共的切點表示式抽取出來,需要用到時引用該切點表示式即可。
- private:僅能在當前切面類中引用該表示式
- public:在其他外部的切面類中也可以引用該表示式
通知順序
1.不同切面類中,預設按照切面類的類名字母排序:
- 目標方法前的通知方法:字母排名靠前的先執行
- 目標方法後的通知方法:字母排名靠前的後執行
2.用 @Order(數字) 加在切面類上來控制順序
- 目標方法前的通知方法:數字小的先執行
- 目標方法後的通知方法:數字小的後執行
切入點表示式
- 切入點表示式:描述切入點方法的一種表示式
- 作用:主要用來決定專案中的哪些方法需要加入通知
- 常見形式:
- execution(……):根據方法的簽名來匹配
- @annotation(……) :根據註解匹配
execution
格式
- 根據業務需要,可以使用 且(&&)、或(||)、非(!) 來組合比較複雜的切入點表示式。
- 書寫建議
- 所有業務方法名在命名時儘量規範,方便切入點表示式快速匹配。如:查詢類方法都是 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 {
}
連線點
- 在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;
}
}