SpringBootWeb案例
目錄
- SpringBootWeb案例
- 1. 新增員工
- 1.1 需求
- 1.2 接口文檔
- 1.3 思路分析
- 1.4 功能開發
- 1.5 功能測試
- 1.6 前后端聯調
- 2. 文件上傳
- 2.1 簡介
- 2.2 本地存儲
- 2.3 阿里云OSS
- 2.3.1 準備
- 2.3.2 入門
- 2.3.3 集成
- 3. 修改員工
- 3.1 查詢回顯
- 3.1.1 接口文檔
- 3.1.2 實現思路
- 3.1.3 代碼實現
- 3.1.4 postman測試
- 3.2 修改員工
- 3.2.1 接口文檔
- 3.2.2 實現思路
- 3.2.3 代碼實現
- 3.2.4 postman測試
- 3.2.5 前后端聯調測試
- 4. 配置文件
- 4.1 參數配置化
- 4.2 yml配置文件
- 4.3 @ConfigurationProperties
1. 新增員工
1.1 需求
在新增用戶時,我們需要保存用戶的基本信息,并且還需要上傳的員工的圖片,目前我們先完成第一步操作,保存用戶的基本信息。
1.2 接口文檔
我們參照接口文檔來開發新增員工功能
-
基本信息
請求路徑:/emps請求方式:POST接口描述:該接口用于添加員工的信息
-
請求參數
參數格式:application/json
參數說明:
名稱 類型 是否必須 備注 username string 必須 用戶名 name string 必須 姓名 gender number 必須 性別, 說明: 1 男, 2 女 image string 非必須 圖像 deptId number 非必須 部門id entrydate string 非必須 入職日期 job number 非必須 職位, 說明: 1 班主任,2 講師, 3 學工主管, 4 教研主管, 5 咨詢師 請求數據樣例:
{"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-03-07-37-38222.jpg","username": "linpingzhi","name": "林平之","gender": 1,"job": 1,"entrydate": "2022-09-18","deptId": 1 }
-
響應數據
參數格式:application/json
參數說明:
參數名 類型 是否必須 備注 code number 必須 響應碼,1 代表成功,0 代表失敗 msg string 非必須 提示信息 data object 非必須 返回的數據 響應數據樣例:
{"code":1,"msg":"success","data":null }
1.3 思路分析
新增員工的具體的流程:
接口文檔規定:
- 請求路徑:/emps
- 請求方式:POST
- 請求參數:Json格式數據
- 響應數據:Json格式數據
問題1:如何限定請求方式是POST?
@PostMapping
問題2:怎么在controller中接收json格式的請求參數?
@RequestBody //把前端傳遞的json數據填充到實體類中
1.4 功能開發
EmpController
@Slf4j
@RestController
@RequestMapping("/emps")
public class EmpController {@Autowiredprivate EmpService empService;//新增@PostMappingpublic Result save(@RequestBody Emp emp){//記錄日志log.info("新增員工, emp:{}",emp);//調用業務層新增功能empService.save(emp);//響應return Result.success();}//省略...
}
EmpService
public interface EmpService {/*** 保存員工信息* @param emp*/void save(Emp emp);//省略...
}
EmpServiceImpl
@Slf4j
@Service
public class EmpServiceImpl implements EmpService {@Autowiredprivate EmpMapper empMapper;@Overridepublic void save(Emp emp) {//補全數據emp.setCreateTime(LocalDateTime.now());emp.setUpdateTime(LocalDateTime.now());//調用添加方法empMapper.insert(emp);}//省略...
}
EmpMapper
@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);//省略...
}
1.5 功能測試
代碼開發完成后,重啟服務器,打開Postman發送 POST 請求,請求路徑:http://localhost:8080/emps
1.6 前后端聯調
功能測試通過后,我們再進行通過打開瀏覽器,測試后端功能接口:
2. 文件上傳
在我們完成的新增員工功能中,還存在一個問題:沒有頭像(圖片缺失)
上述問題,需要我們通過文件上傳技術來解決。下面我們就進入到文件上傳技術的學習。
文件上傳技術這塊我們主要講解三個方面:首先我們先對文件上傳做一個整體的介紹,接著再學習文件上傳的本地存儲方式,最后學習云存儲方式。
接下來我們就先來學習下什么是文件上傳。
2.1 簡介
文件上傳,是指將本地圖片、視頻、音頻等文件上傳到服務器,供其他用戶瀏覽或下載的過程。
文件上傳在項目中應用非常廣泛,我們經常發微博、發微信朋友圈都用到了文件上傳功能。
在我們的案例中,在新增員工的時候,要上傳員工的頭像,此時就會涉及到文件上傳的功能。在進行文件上傳時,我們點擊加號或者是點擊圖片,就可以選擇手機或者是電腦本地的圖片文件了。當我們選擇了某一個圖片文件之后,這個文件就會上傳到服務器,從而完成文件上傳的操作。
想要完成文件上傳這個功能需要涉及到兩個部分:
- 前端程序
- 服務端程序
我們先來看看在前端程序中要完成哪些代碼:
<form action="/upload" method="post" enctype="multipart/form-data">姓名: <input type="text" name="username"><br>年齡: <input type="text" name="age"><br>頭像: <input type="file" name="image"><br><input type="submit" value="提交">
</form>
上傳文件的原始form表單,要求表單必須具備以下三點(上傳文件頁面三要素):
-
表單必須有file域,用于選擇要上傳的文件
<input type="file" name="image"/>
-
表單提交方式必須為POST
通常上傳的文件會比較大,所以需要使用 POST 提交方式
-
表單的編碼類型enctype必須要設置為:multipart/form-data
普通默認的編碼格式是不適合傳輸大型的二進制數據的,所以在文件上傳時,表單的編碼格式必須設置為multipart/form-data
前端頁面的3要素我們了解后,接下來我們就來驗證下所講解的文件上傳3要素。
在提供的"課程資料"中有一個名叫"文件上傳"的文件夾,直接將里的"upload.html"文件,復制到springboot項目工程下的static目錄里面。
下面我們來驗證:刪除form表單中enctype屬性值,會是什么情況?
- 在IDEA中直接使用瀏覽器打開upload.html頁面
- 選擇要上傳的本地文件
- 點擊"提交"按鈕,進入到開發者模式觀察
我們再來驗證:設置form表單中enctype屬性值為multipart/form-data,會是什么情況?
<form action="/upload" method="post" enctype="multipart/form-data">姓名: <input type="text" name="username"><br>年齡: <input type="text" name="age"><br>頭像: <input type="file" name="image"><br><input type="submit" value="提交"></form>
知道了前端程序中需要設置上傳文件頁面三要素,那我們的后端程序又是如何實現的呢?
-
首先在服務端定義這么一個controller,用來進行文件上傳,然后在controller當中定義一個方法來處理
/upload
請求 -
在定義的方法中接收提交過來的數據 (方法中的形參名和請求參數的名字保持一致)
- 用戶名:String name
- 年齡: Integer age
- 文件: MultipartFile image
Spring中提供了一個API:MultipartFile,使用這個API就可以來接收到上傳的文件
問題:如果表單項的名字和方法中形參名不一致,該怎么辦?
public Result upload(String username,Integer age, MultipartFile file) //file形參名和請求參數名image不一致
解決:使用@RequestParam注解進行參數綁定
public Result upload(String username,Integer age, @RequestParam("image") MultipartFile file)
UploadController代碼:
@Slf4j
@RestController
public class UploadController {@PostMapping("/upload")public Result upload(String username, Integer age, MultipartFile image) {log.info("文件上傳:{},{},{}",username,age,image);return Result.success();}}
后端程序編寫完成之后,打個斷點,以debug方式啟動SpringBoot項目
打開瀏覽器輸入:http://localhost:8080/upload.html , 錄入數據并提交
通過后端程序控制臺可以看到,上傳的文件是存放在一個臨時目錄
打開臨時目錄可以看到以下內容:
表單提交的三項數據(姓名、年齡、文件),分別存儲在不同的臨時文件中:
當我們程序運行完畢之后,這個臨時文件會自動刪除。
所以,我們如果想要實現文件上傳,需要將這個臨時文件,要轉存到我們的磁盤目錄中。
2.2 本地存儲
前面我們已分析了文件上傳功能前端和后端的基礎代碼實現,文件上傳時在服務端會產生一個臨時文件,請求響應完成之后,這個臨時文件被自動刪除,并沒有進行保存。下面呢,我們就需要完成將上傳的文件保存在服務器的本地磁盤上。
代碼實現:
- 在服務器本地磁盤上創建images目錄,用來存儲上傳的文件(例:E盤創建images目錄)
- 使用MultipartFile類提供的API方法,把臨時文件轉存到本地磁盤目錄下
MultipartFile 常見方法:
- String getOriginalFilename(); //獲取原始文件名
- void transferTo(File dest); //將接收的文件轉存到磁盤文件中
- long getSize(); //獲取文件的大小,單位:字節
- byte[] getBytes(); //獲取文件內容的字節數組
- InputStream getInputStream(); //獲取接收到的文件內容的輸入流
@Slf4j
@RestController
public class UploadController {@PostMapping("/upload")public Result upload(String username, Integer age, MultipartFile image) throws IOException {log.info("文件上傳:{},{},{}",username,age,image);//獲取原始文件名String originalFilename = image.getOriginalFilename();//將文件存儲在服務器的磁盤目錄image.transferTo(new File("E:/images/"+originalFilename));return Result.success();}}
利用postman測試:
注意:請求參數名和controller方法形參名保持一致
通過postman測試,我們發現文件上傳是沒有問題的。但是由于我們是使用原始文件名作為所上傳文件的存儲名字,當我們再次上傳一個名為1.jpg文件時,發現會把之前已經上傳成功的文件覆蓋掉。
解決方案:保證每次上傳文件時文件名都唯一的(使用UUID獲取隨機文件名)
@Slf4j
@RestController
public class UploadController {@PostMapping("/upload")public Result upload(String username, Integer age, MultipartFile image) throws IOException {log.info("文件上傳:{},{},{}",username,age,image);//獲取原始文件名String originalFilename = image.getOriginalFilename();//構建新的文件名String extname = originalFilename.substring(originalFilename.lastIndexOf("."));//文件擴展名String newFileName = UUID.randomUUID().toString()+extname;//隨機名+文件擴展名//將文件存儲在服務器的磁盤目錄image.transferTo(new File("E:/images/"+newFileName));return Result.success();}}
在解決了文件名唯一性的問題后,我們再次上傳一個較大的文件(超出1M)時發現,后端程序報錯:
報錯原因呢是因為:在SpringBoot中,文件上傳時默認單個文件最大大小為1M
那么如果需要上傳大文件,可以在application.properties進行如下配置:
#配置單個文件最大上傳大小
spring.servlet.multipart.max-file-size=10MB#配置單個請求最大上傳大小(一次請求可以上傳多個文件)
spring.servlet.multipart.max-request-size=100MB
到時此,我們文件上傳的本地存儲方式已完成了。但是這種本地存儲方式還存在一問題:
如果直接存儲在服務器的磁盤目錄中,存在以下缺點:
- 不安全:磁盤如果損壞,所有的文件就會丟失
- 容量有限:如果存儲大量的圖片,磁盤空間有限(磁盤不可能無限制擴容)
- 無法直接訪問
為了解決上述問題呢,通常有兩種解決方案:
- 自己搭建存儲服務器,如:fastDFS 、MinIO
- 使用現成的云服務,如:阿里云,騰訊云,華為云
2.3 阿里云OSS
2.3.1 準備
阿里云是阿里巴巴集團旗下全球領先的云計算公司,也是國內最大的云服務提供商 。
云服務指的就是通過互聯網對外提供的各種各樣的服務,比如像:語音服務、短信服務、郵件服務、視頻直播服務、文字識別服務、對象存儲服務等等。
當我們在項目開發時需要用到某個或某些服務,就不需要自己來開發了,可以直接使用阿里云提供好的這些現成服務就可以了。比如:在項目開發當中,我們要實現一個短信發送的功能,如果我們項目組自己實現,將會非常繁瑣,因為你需要和各個運營商進行對接。而此時阿里云完成了和三大運營商對接,并對外提供了一個短信服務。我們項目組只需要調用阿里云提供的短信服務,就可以很方便的來發送短信了。這樣就降低了我們項目的開發難度,同時也提高了項目的開發效率。(大白話:別人幫我們實現好了功能,我們只要調用即可)
云服務提供商給我們提供的軟件服務通常是需要收取一部分費用的。
阿里云對象存儲OSS(Object Storage Service),是一款海量、安全、低成本、高可靠的云存儲服務。使用OSS,您可以通過網絡隨時存儲和調用包括文本、圖片、音頻和視頻等在內的各種文件。
在我們使用了阿里云OSS對象存儲服務之后,我們的項目當中如果涉及到文件上傳這樣的業務,在前端進行文件上傳并請求到服務端時,在服務器本地磁盤當中就不需要再來存儲文件了。我們直接將接收到的文件上傳到oss,由 oss幫我們存儲和管理,同時阿里云的oss存儲服務還保障了我們所存儲內容的安全可靠。
那我們學習使用這類云服務,我們主要學習什么呢?其實我們主要學習的是如何在項目當中來使用云服務完成具體的業務功能。而無論使用什么樣的云服務,阿里云也好,騰訊云、華為云也罷,在使用第三方的服務時,操作的思路都是一樣的。
SDK:Software Development Kit 的縮寫,軟件開發工具包,包括輔助軟件開發的依賴(jar包)、代碼示例等,都可以叫做SDK。
簡單說,sdk中包含了我們使用第三方云服務時所需要的依賴,以及一些示例代碼。我們可以參照sdk所提供的示例代碼就可以完成入門程序。
第三方服務使用的通用思路,我們做一個簡單介紹之后,接下來我們就來介紹一下我們當前要使用的阿里云oss對象存儲服務具體的使用步驟。
Bucket:存儲空間是用戶用于存儲對象(Object,就是文件)的容器,所有的對象都必須隸屬于某個存儲空間。
下面我們根據之前介紹的使用步驟,完成準備工作:
- 注冊阿里云賬戶(注冊完成后需要實名認證)
- 注冊完賬號之后,就可以登錄阿里云
- 通過控制臺找到對象存儲OSS服務
如果是第一次訪問,還需要開通對象存儲服務OSS
- 開通OSS服務之后,就可以進入到阿里云對象存儲的控制臺
- 點擊左側的 “Bucket列表”,創建一個Bucket
大家可以參照"資料\04. 阿里云oss"中提供的文檔,開通阿里云OSS服務。
2.3.2 入門
阿里云oss 對象存儲服務的準備工作我們已經完成了,接下來我們就來完成第二步操作:參照官方所提供的sdk示例來編寫入門程序。
首先我們需要來打開阿里云OSS的官方文檔,在官方文檔中找到 SDK 的示例代碼:
如果是在實際開發當中,我們是需要從前往后仔細的去閱讀這一份文檔的,但是由于現在是教學,我們就只挑重點的去看。有興趣的同學大家下來也可以自己去看一下這份官方文檔。
參照官方提供的SDK,改造一下,即可實現文件上傳功能:
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;import java.io.FileInputStream;
import java.io.InputStream;public class AliOssTest {public static void main(String[] args) throws Exception {// Endpoint以華東1(杭州)為例,其它Region請按實際情況填寫。String endpoint = "oss-cn-shanghai.aliyuncs.com";// 阿里云賬號AccessKey擁有所有API的訪問權限,風險很高。強烈建議您創建并使用RAM用戶進行API訪問或日常運維,請登錄RAM控制臺創建RAM用戶。String accessKeyId = "LTAI5t9MZK8iq5T2Av5GLDxX";String accessKeySecret = "C0IrHzKZGKqU8S7YQcevcotD3Zd5Tc";// 填寫Bucket名稱,例如examplebucket。String bucketName = "web-framework01";// 填寫Object完整路徑,完整路徑中不能包含Bucket名稱,例如exampledir/exampleobject.txt。String objectName = "1.jpg";// 填寫本地文件的完整路徑,例如D:\\localpath\\examplefile.txt。// 如果未指定本地路徑,則默認從示例程序所屬項目對應本地路徑中上傳文件流。String filePath= "C:\\Users\\Administrator\\Pictures\\1.jpg";// 創建OSSClient實例。OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);try {InputStream inputStream = new FileInputStream(filePath);// 創建PutObjectRequest對象。PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, inputStream);// 設置該屬性可以返回response。如果不設置,則返回的response為空。putObjectRequest.setProcess("true");// 創建PutObject請求。PutObjectResult result = ossClient.putObject(putObjectRequest);// 如果上傳成功,則返回200。System.out.println(result.getResponse().getStatusCode());} catch (OSSException oe) {System.out.println("Caught an OSSException, which means your request made it to OSS, "+ "but was rejected with an error response for some reason.");System.out.println("Error Message:" + oe.getErrorMessage());System.out.println("Error Code:" + oe.getErrorCode());System.out.println("Request ID:" + oe.getRequestId());System.out.println("Host ID:" + oe.getHostId());} catch (ClientException ce) {System.out.println("Caught an ClientException, which means the client encountered "+ "a serious internal problem while trying to communicate with OSS, "+ "such as not being able to access the network.");System.out.println("Error Message:" + ce.getMessage());} finally {if (ossClient != null) {ossClient.shutdown();}}}
}
在以上代碼中,需要替換的內容為:
- accessKeyId:阿里云賬號AccessKey
- accessKeySecret:阿里云賬號AccessKey對應的秘鑰
- bucketName:Bucket名稱
- objectName:對象名稱,在Bucket中存儲的對象的名稱
- filePath:文件路徑
AccessKey :
運行以上程序后,會把本地的文件上傳到阿里云OSS服務器上:
2.3.3 集成
阿里云oss對象存儲服務的準備工作以及入門程序我們都已經完成了,接下來我們就需要在案例當中集成oss對象存儲服務,來存儲和管理案例中上傳的圖片。
在新增員工的時候,上傳員工的圖像,而之所以需要上傳員工的圖像,是因為將來我們需要在系統頁面當中訪問并展示員工的圖像。而要想完成這個操作,需要做兩件事:
- 需要上傳員工的圖像,并把圖像保存起來(存儲到阿里云OSS)
- 訪問員工圖像(通過圖像在阿里云OSS的存儲地址訪問圖像)
- OSS中的每一個文件都會分配一個訪問的url,通過這個url就可以訪問到存儲在阿里云上的圖片。所以需要把url返回給前端,這樣前端就可以通過url獲取到圖像。
我們參照接口文檔來開發文件上傳功能:
-
基本信息
請求路徑:/upload請求方式:POST接口描述:上傳圖片接口
-
請求參數
參數格式:multipart/form-data
參數說明:
參數名稱 參數類型 是否必須 示例 備注 image file 是 -
響應數據
參數格式:application/json
參數說明:
參數名 類型 是否必須 備注 code number 必須 響應碼,1 代表成功,0 代表失敗 msg string 非必須 提示信息 data object 非必須 返回的數據,上傳圖片的訪問路徑 響應數據樣例:
{"code": 1,"msg": "success","data": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-02-00-27-0400.jpg" }
引入阿里云OSS上傳文件工具類(由官方的示例代碼改造而來)
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;@Component
public class AliOSSUtils {private String endpoint = "https://oss-cn-shanghai.aliyuncs.com";private String accessKeyId = "LTAI5t9MZK8iq5T2Av5GLDxX";private String accessKeySecret = "C0IrHzKZGKqU8S7YQcevcotD3Zd5Tc";private String bucketName = "web-framework01";/*** 實現上傳圖片到OSS*/public String upload(MultipartFile multipartFile) throws IOException {// 獲取上傳的文件的輸入流InputStream inputStream = multipartFile.getInputStream();// 避免文件覆蓋String originalFilename = multipartFile.getOriginalFilename();String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));//上傳文件到 OSSOSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);ossClient.putObject(bucketName, fileName, inputStream);//文件訪問路徑String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;// 關閉ossClientossClient.shutdown();return url;// 把上傳到oss的路徑返回}
}
修改UploadController代碼:
import com.itheima.pojo.Result;
import com.itheima.utils.AliOSSUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;@Slf4j
@RestController
public class UploadController {@Autowiredprivate AliOSSUtils aliOSSUtils;@PostMapping("/upload")public Result upload(MultipartFile image) throws IOException {//調用阿里云OSS工具類,將上傳上來的文件存入阿里云String url = aliOSSUtils.upload(image);//將圖片上傳完成后的url返回,用于瀏覽器回顯展示return Result.success(url);}}
使用postman測試:
3. 修改員工
需求:修改員工信息
在進行修改員工信息的時候,我們首先先要根據員工的ID查詢員工的信息用于頁面回顯展示,然后用戶修改員工數據之后,點擊保存按鈕,就可以將修改的數據提交到服務端,保存到數據庫。 具體操作為:
- 根據ID查詢員工信息
- 保存修改的員工信息
3.1 查詢回顯
3.1.1 接口文檔
根據ID查詢員工數據
-
基本信息
請求路徑:/emps/{id}請求方式:GET接口描述:該接口用于根據主鍵ID查詢員工的信息
-
請求參數
參數格式:路徑參數
參數說明:
參數名 類型 是否必須 備注 id number 必須 員工ID 請求參數樣例:
/emps/1
-
響應數據
參數格式:application/json
參數說明:
名稱 類型 是否必須 默認值 備注 code number 必須 響應碼, 1 成功 , 0 失敗 msg string 非必須 提示信息 data object 必須 返回的數據 |- id number 非必須 id |- username string 非必須 用戶名 |- name string 非必須 姓名 |- password string 非必須 密碼 |- entrydate string 非必須 入職日期 |- gender number 非必須 性別 , 1 男 ; 2 女 |- image string 非必須 圖像 |- job number 非必須 職位, 說明: 1 班主任,2 講師, 3 學工主管, 4 教研主管, 5 咨詢師 |- deptId number 非必須 部門id |- createTime string 非必須 創建時間 |- updateTime string 非必須 更新時間 響應數據樣例:
{"code": 1,"msg": "success","data": {"id": 2,"username": "zhangwuji","password": "123456","name": "張無忌","gender": 1,"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-02-00-27-53B.jpg","job": 2,"entrydate": "2015-01-01","deptId": 2,"createTime": "2022-09-01T23:06:30","updateTime": "2022-09-02T00:29:04"} }
3.1.2 實現思路
3.1.3 代碼實現
- EmpMapper
@Mapper
public interface EmpMapper {//根據ID查詢員工信息@Select("select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time " +"from emp " +"where id = #{id}")public Emp findById(Integer id);//省略...
}
- EmpService
public interface EmpService {/*** 根據ID查詢員工* @param id* @return*/public Emp getById(Integer id);//省略...
}
- EmpServiceImpl
@Slf4j
@Service
public class EmpServiceImpl implements EmpService {@Autowiredprivate EmpMapper empMapper;@Overridepublic Emp getById(Integer id) {return empMapper.findById(id);}//省略...
}
- EmpController
@Slf4j
@RestController
@RequestMapping("/emps")
public class EmpController {@Autowiredprivate EmpService empService;//根據id查詢@GetMapping("/{id}")public Result getById(@PathVariable Integer id){Emp emp = empService.getById(id);return Result.success(emp);}//省略...
}
3.1.4 postman測試
3.2 修改員工
當用戶修改完數據之后,點擊保存按鈕,就需要將數據提交到服務端,然后服務端需要將修改后的數據更新到數據庫中。
3.2.1 接口文檔
-
基本信息
請求路徑:/emps請求方式:PUT接口描述:該接口用于修改員工的數據信息
-
請求參數
參數格式:application/json
參數說明:
名稱 類型 是否必須 備注 id number 必須 id username string 必須 用戶名 name string 必須 姓名 gender number 必須 性別, 說明: 1 男, 2 女 image string 非必須 圖像 deptId number 非必須 部門id entrydate string 非必須 入職日期 job number 非必須 職位, 說明: 1 班主任,2 講師, 3 學工主管, 4 教研主管, 5 咨詢師 請求數據樣例:
{"id": 1,"image": "https://web-framework.oss-cn-hangzhou.aliyuncs.com/2022-09-03-07-37-38222.jpg","username": "linpingzhi","name": "林平之","gender": 1,"job": 1,"entrydate": "2022-09-18","deptId": 1 }
-
響應數據
參數格式:application/json
參數說明:
參數名 類型 是否必須 備注 code number 必須 響應碼,1 代表成功,0 代表失敗 msg string 非必須 提示信息 data object 非必須 返回的數據 響應數據樣例:
{"code":1,"msg":"success","data":null }
3.2.2 實現思路
3.2.3 代碼實現
- EmpMapper
@Mapper
public interface EmpMapper {//修改員工信息public void update(Emp emp);//省略...
}
- EmpMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper"><!--更新員工信息--><update id="update">update emp<set><if test="username != null and username != ''">username = #{username},</if><if test="password != null and password != ''">password = #{password},</if><if test="name != null and name != ''">name = #{name},</if><if test="gender != null">gender = #{gender},</if><if test="image != null and image != ''">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>
- EmpService
public interface EmpService {/*** 更新員工* @param emp*/public void update(Emp emp);//省略...
}
- EmpServiceImpl
@Slf4j
@Service
public class EmpServiceImpl implements EmpService {@Autowiredprivate EmpMapper empMapper;@Overridepublic void update(Emp emp) {emp.setUpdateTime(LocalDateTime.now()); //更新修改時間為當前時間empMapper.update(emp);}//省略...
}
- EmpController
@Slf4j
@RestController
@RequestMapping("/emps")
public class EmpController {@Autowiredprivate EmpService empService;//修改員工@PutMappingpublic Result update(@RequestBody Emp emp){empService.update(emp);return Result.success();}//省略...
}
3.2.4 postman測試
3.2.5 前后端聯調測試
4. 配置文件
員工管理的增刪改查功能我們已開發完成,但在我們所開發的程序中還一些小問題,下面我們就來分析一下當前案例中存在的問題以及如何優化解決。
4.1 參數配置化
在我們之前編寫的程序中進行文件上傳時,需要調用AliOSSUtils工具類,將文件上傳到阿里云OSS對象存儲服務當中。而在調用工具類進行文件上傳時,需要一些參數:
- endpoint //阿里云OSS域名
- accessKeyID //用戶身份ID
- accessKeySecret //用戶密鑰
- bucketName //存儲空間的名字
關于以上的這些阿里云相關配置信息,我們是直接寫死在java代碼中了(硬編碼),如果我們在做項目時每涉及到一個第三方技術服務,就將其參數硬編碼,那么在Java程序中會存在兩個問題:
- 如果這些參數發生變化了,就必須在源程序代碼中改動這些參數,然后需要重新進行代碼的編譯,將Java代碼編譯成class字節碼文件再重新運行程序。(比較繁瑣)
- 如果我們開發的是一個真實的企業級項目, Java類可能會有很多,如果將這些參數分散的定義在各個Java類當中,我們要修改一個參數值,我們就需要在眾多的Java代碼當中來定位到對應的位置,再來修改參數,修改完畢之后再重新編譯再運行。(參數配置過于分散,是不方便集中的管理和維護)
為了解決以上分析的問題,我們可以將參數配置在配置文件中。如下:
#自定義的阿里云OSS配置信息
aliyun.oss.endpoint=https://oss-cn-hangzhou.aliyuncs.com
aliyun.oss.accessKeyId=LTAI4GCH1vX6DKqJWxd6nEuW
aliyun.oss.accessKeySecret=yBshYweHOpqDuhCArrVHwIiBKpyqSL
aliyun.oss.bucketName=web-tlias
在將阿里云OSS配置參數交給properties配置文件來管理之后,我們的AliOSSUtils工具類就變為以下形式:
@Component
public class AliOSSUtils {/*以下4個參數沒有指定值(默認值:null)*/private String endpoint;private String accessKeyId;private String accessKeySecret;private String bucketName;//省略其他代碼...
}
而此時如果直接調用AliOSSUtils類當中的upload方法進行文件上傳時,這4項參數全部為null,原因是因為并沒有給它賦值。
此時我們是不是需要將配置文件當中所配置的屬性值讀取出來,并分別賦值給AliOSSUtils工具類當中的各個屬性呢?那應該怎么做呢?
因為application.properties是springboot項目默認的配置文件,所以springboot程序在啟動時會默認讀取application.properties配置文件,而我們可以使用一個現成的注解:@Value,獲取配置文件中的數據。
@Value 注解通常用于外部配置的屬性注入,具體用法為: @Value(“${配置文件中的key}”)
@Component
public class AliOSSUtils {@Value("${aliyun.oss.endpoint}")private String endpoint;@Value("${aliyun.oss.accessKeyId}")private String accessKeyId;@Value("${aliyun.oss.accessKeySecret}")private String accessKeySecret;@Value("${aliyun.oss.bucketName}")private String bucketName;//省略其他代碼...}
使用postman測試:
4.2 yml配置文件
前面我們一直使用springboot項目創建完畢后自帶的application.properties進行屬性的配置,那其實呢,在springboot項目當中是支持多種配置方式的,除了支持properties配置文件以外,還支持另外一種類型的配置文件,就是我們接下來要講解的yml格式的配置文件。
-
application.properties
server.port=8080 server.address=127.0.0.1
-
application.yml
server:port: 8080address: 127.0.0.1
-
application.yaml
server:port: 8080address: 127.0.0.1
yml 格式的配置文件,后綴名有兩種:
- yml (推薦)
- yaml
常見配置文件格式對比:
我們可以看到配置同樣的數據信息,yml格式的數據有以下特點:
- 容易閱讀
- 容易與腳本語言交互
- 以數據為核心,重數據輕格式
簡單的了解過springboot所支持的配置文件,以及不同類型配置文件之間的優缺點之后,接下來我們就來了解下yml配置文件的基本語法:
- 大小寫敏感
- 數值前邊必須有空格,作為分隔符
- 使用縮進表示層級關系,縮進時,不允許使用Tab鍵,只能用空格(idea中會自動將Tab轉換為空格)
- 縮進的空格數目不重要,只要相同層級的元素左側對齊即可
#
表示注釋,從這個字符一直到行尾,都會被解析器忽略
了解完yml格式配置文件的基本語法之后,接下來我們再來看下yml文件中常見的數據格式。在這里我們主要介紹最為常見的兩類:
- 定義對象或Map集合
- 定義數組、list或set集合
對象/Map集合
user:name: zhangsanage: 18password: 123456
數組/List/Set集合
hobby: - java- game- sport
熟悉完了yml文件的基本語法后,我們修改下之前案例中使用的配置文件,變更為application.yml配置方式:
- 修改application.properties名字為:
_application.properties
(名字隨便更換,只要加載不到即可) - 創建新的配置文件:
application.yml
原有application.properties文件:
新建的application.yml文件:
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/tliasusername: rootpassword: 1234servlet:multipart:max-file-size: 10MBmax-request-size: 100MBmybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: truealiyun:oss:endpoint: https://oss-cn-hangzhou.aliyuncs.comaccessKeyId: LTAI4GCH1vX6DKqJWxd6nEuWaccessKeySecret: yBshYweHOpqDuhCArrVHwIiBKpyqSLbucketName: web-397
4.3 @ConfigurationProperties
講解完了yml配置文件之后,最后再來介紹一個注解@ConfigurationProperties
。在介紹注解之前,我們先來看一個場景,分析下代碼當中可能存在的問題:
我們在application.properties或者application.yml中配置了阿里云OSS的四項參數之后,如果java程序中需要這四項參數數據,我們直接通過@Value注解來進行注入。這種方式本身沒有什么問題問題,但是如果說需要注入的屬性較多(例:需要20多個參數數據),我們寫起來就會比較繁瑣。
那么有沒有一種方式可以簡化這些配置參數的注入呢?答案是肯定有,在Spring中給我們提供了一種簡化方式,可以直接將配置文件中配置項的值自動的注入到對象的屬性中。
Spring提供的簡化方式套路:
-
需要創建一個實現類,且實體類中的屬性名和配置文件當中key的名字必須要一致
比如:配置文件當中叫endpoints,實體類當中的屬性也得叫endpoints,另外實體類當中的屬性還需要提供 getter / setter方法
-
需要將實體類交給Spring的IOC容器管理,成為IOC容器當中的bean對象
-
在實體類上添加
@ConfigurationProperties
注解,并通過perfect屬性來指定配置參數項的前綴
實體類:AliOSSProperties
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;/*阿里云OSS相關配置*/
@Data
@Component
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliOSSProperties {//區域private String endpoint;//身份IDprivate String accessKeyId ;//身份密鑰private String accessKeySecret ;//存儲空間private String bucketName;
}
AliOSSUtils工具類:
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;@Component //當前類對象由Spring創建和管理
public class AliOSSUtils {//注入配置參數實體類對象@Autowiredprivate AliOSSProperties aliOSSProperties;/*** 實現上傳圖片到OSS*/public String upload(MultipartFile multipartFile) throws IOException {// 獲取上傳的文件的輸入流InputStream inputStream = multipartFile.getInputStream();// 避免文件覆蓋String originalFilename = multipartFile.getOriginalFilename();String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));//上傳文件到 OSSOSS ossClient = new OSSClientBuilder().build(aliOSSProperties.getEndpoint(),aliOSSProperties.getAccessKeyId(), aliOSSProperties.getAccessKeySecret());ossClient.putObject(aliOSSProperties.getBucketName(), fileName, inputStream);//文件訪問路徑String url =aliOSSProperties.getEndpoint().split("//")[0] + "//" + aliOSSProperties.getBucketName() + "." + aliOSSProperties.getEndpoint().split("//")[1] + "/" + fileName;// 關閉ossClientossClient.shutdown();return url;// 把上傳到oss的路徑返回}
}
在我們添加上注解后,會發現idea窗口上面出現一個紅色警告:
idea2023.3.3沒有出現該報錯
這個警告提示是告知我們還需要引入一個依賴:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
當我們在pom.xml文件當中配置了這項依賴之后,我們重新啟動服務,大家就會看到在properties或者是yml配置文件當中,就會提示阿里云 OSS 相關的配置項。所以這項依賴它的作用就是會自動的識別被@Configuration Properties
注解標識的bean對象。
剛才的紅色警告,已經變成了一個灰色的提示,提示我們需要重新運行springboot服務
@ConfigurationProperties注解我們已經介紹完了,接下來我們就來區分一下@ConfigurationProperties注解以及我們前面所介紹的另外一個@Value注解:
相同點:都是用來注入外部配置的屬性的。
不同點:
-
@Value注解只能一個一個的進行外部屬性的注入。
-
@ConfigurationProperties可以批量的將外部的屬性配置注入到bean對象的屬性中。
如果要注入的屬性非常的多,并且還想做到復用,就可以定義這么一個bean對象。通過 configuration properties 批量的將外部的屬性配置直接注入到 bin 對象的屬性當中。在其他的類當中,我要想獲取到注入進來的屬性,我直接注入 bin 對象,然后調用 get 方法,就可以獲取到對應的屬性值了