詳解SpringBoot項目整合Vue做一個完整的用戶注冊功能
前言
用戶注冊功能是每一個系統(tǒng)的入口門面功能,很多人可能會以為很簡單,不就是一個簡單的CRUD嗎?其實不然,要把前后端功能都做出來,頁面跳轉(zhuǎn)也沒問題,還真不簡單。這次筆者做這么一個看似簡單的用戶注冊功能就花了足足兩天多時間,中間調(diào)試和解決Bug也花了好長時間。這次我就把自己做出的完整功能的實現(xiàn)過程作了一個提煉分享到我的公眾號上來。希望有需要了解如何實現(xiàn)用戶注冊完整過程的讀者朋友能夠仔細(xì)看一看。
說明:本文前后端代碼的實現(xiàn)分別在本人之前二次開發(fā)的開源項目vue-element-admin和vueblog兩個項目的基礎(chǔ)上進(jìn)行
1 實現(xiàn)用戶注冊流程
1.1 用戶注冊完整流程

1.2 用戶注冊信息及校驗

2 后臺接口設(shè)計
2.1 上傳頭像接口
2.1.1 接口url
http://localhost:8081/blog/upload/user/avatar
2.1.2 請求類型
POST
2.1.3 接口入?yún)?/p>
| 參數(shù)名稱 | 參數(shù)類型 | 是否必傳 | 備注 |
|---|---|---|---|
| file | MultipartFile | 是 | 多媒體圖片文件 |
2.1.4 接口出參
| 參數(shù)名稱 | 參數(shù)類型 | 示例值 | 備注 |
|---|---|---|---|
| status | Integer | 200 | 狀態(tài)碼:200-成功; 500-失敗 |
| msg | String | “success” | 響應(yīng)信息:“success”-上傳頭像成功; "upload file failed"-上傳頭像失敗 |
| data | String | vueblog2022.oss-cn-shenzhen.aliyuncs.com/avatar/63be… | 上傳頭像成功后的下載地址 |
2.2 用戶注冊接口
2.2.1 接口url
http://localhost:8081/blog/user/reg
2.2.2 請求類型
POST
2.2.3 接口入?yún)?/p>
| 參數(shù)名稱 | 參數(shù)類型 | 是否必填 | 備注 |
|---|---|---|---|
| username | String | 是 | 用戶賬號 |
| nickname | String | 是 | 用戶昵稱 |
| password | String | 是 | 用戶登錄密碼 |
| userface | String | 否 | 用戶頭像鏈接地址 |
| phoneNum | Long | 是 | 用戶手機(jī)號碼 |
| String | 否 | 用戶郵箱地址 |
2.2.3 接口出參
| 參數(shù)名稱 | 參數(shù)類型 | 示例值 | 備注 |
|---|---|---|---|
| status | Integer | 200 | 響應(yīng)碼: 200-成功;500-失敗 |
| msg | String | 注冊成功 | 響應(yīng)消息 |
| data | Integer | 0 | 注冊成功標(biāo)識:0-注冊成功;1-用戶名重復(fù); null-內(nèi)部服務(wù)異常 |
3 后端代碼實現(xiàn)
3.1 用戶頭像上傳接口編碼實現(xiàn)
文件上傳,這里選用了阿里云的對象存儲,需要先開通阿里云對象存儲服務(wù),關(guān)于如何開通阿里云短信服務(wù)并將阿里云對象存儲服務(wù)集成到SpringBoot項目中,請參考我之前發(fā)布的文章SpringBoot項目集成阿里云對象存儲服務(wù)實現(xiàn)文件上傳
3.1.1 服務(wù)層編碼
新建OssClientService類繼承阿里云對象存儲服務(wù)SDK完成圖片上傳功能
@Service
public class OssClientService {
@Resource
private OssProperties ossProperties;
private static final Logger logger = LoggerFactory.getLogger(OssClientService.class);
public String uploadFile(MultipartFile file){
// 創(chuàng)建OSSClient實例。
OSS ossClient = new OSSClientBuilder().build(ossProperties.getEndPoint(), ossProperties.getAccessKey(),
ossProperties.getSecretKey());
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
String objectName = "avatar/" + uuid + ".png";
String imageUrl = null;
try {
InputStream inputStream = file.getInputStream();
ossClient.putObject(ossProperties.getBucketName(), objectName, inputStream);
imageUrl = "https://" + ossProperties.getBucketName() + "." + ossProperties.getEndPoint() + "/" + objectName;
} catch (OSSException oe) {
logger.error("Caught an OSSException, which means your request made it to OSS, but was rejected with an error response for some reason.");
logger.error("Error Message:" + oe.getErrorMessage());
logger.error("Error Code:" + oe.getErrorCode());
logger.error("RequestId: " + oe.getRequestId());
logger.error("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
logger.error("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");
logger.error("Error Message:" + ce.getErrorMessage());
} catch (FileNotFoundException fe) {
logger.error("file not found exception");
logger.error("Error Message:" + fe.getMessage(), fe);
} catch (IOException exception){
logger.error("file get input stream error, caused by " + exception.getMessage(), exception);
}
finally {
if (ossClient!=null) {
ossClient.shutdown();
}
}
return imageUrl;
}
}注意:升級到3.9.1版本后的aliyun-sdk-oss需要在每次上傳文件時新建一個OSS實例, 上傳完文件之后再調(diào)用shutdown方法關(guān)閉這個實例
3.1.2 控制器層編碼
新建UploadFileController類完成從前端接收附件參數(shù),并調(diào)用OssClientService服務(wù)實現(xiàn)圖片上傳
@RestController
@RequestMapping("/upload")
public class UploadFileController {
@Resource
private OssClientService ossClientService;
@PostMapping("/user/avatar")
@ApiOperation(value = "userAvatar", notes = "用戶上傳頭像接口",
produces = "application/octet-stream", consumes = "application/json")
public RespBean uploadUserAvatar(HttpServletRequest request){
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
// 獲取上傳文件對象
MultipartFile file = multipartRequest.getFile("file");
RespBean respBean = new RespBean();
String downloadUrl = ossClientService.uploadFile(file);
if (!StringUtils.isEmpty(downloadUrl)) {
respBean.setStatus(200);
respBean.setMsg("success");
respBean.setData(downloadUrl);
} else {
respBean.setStatus(500);
respBean.setMsg("upload file failed");
}
return respBean;
}
}3.2 用戶注冊接口
3.2.1 數(shù)據(jù)庫訪問層編碼
在UserMapper接口類中新增注冊用戶抽象方法
int registerUser(UserDTO user);
然后在UserMapper.xml文件中完成用戶數(shù)據(jù)入庫sql編寫
<insert id="registerUser" useGeneratedKeys="true" keyProperty="id" parameterType="org.sang.pojo.dto.UserDTO">
INSERT INTO user(username, nickname, password, phoneNum,email, userface, regTime,enabled)
values(#{username,jdbcType=VARCHAR},#{nickname,jdbcType=VARCHAR},
#{password,jdbcType=VARCHAR}, #{phoneNum,jdbcType=BIGINT}, #{email,jdbcType=VARCHAR},
#{userface,jdbcType=VARCHAR},now(),1)
</insert>3.2.2 服務(wù)層編碼
在CustomUserDetailsService接口類中添加注冊用戶抽象方法
int registerUser(UserDTO user);
然后在 CustomUserDetailsService接口類的實現(xiàn)類UserService類中完成用戶注冊邏輯
@Override
public int registerUser(UserDTO user) {
// 判斷用戶是否重復(fù)注冊
UserDTO userDTO = userMapper.loadUserByUsername(user.getUsername());
if (userDTO != null) {
return 1;
}
//插入用戶, 插入之前先對密碼進(jìn)行加密
user.setPassword(passwordEncoder.encode(user.getPassword()));
user.setEnabled(1);//用戶可用
int result = userMapper.registerUser(user);
//配置用戶的角色,默認(rèn)都是普通用戶
List<Integer> roleIds = Arrays.asList(2);
int i = rolesMapper.setUserRoles(roleIds, user.getId());
boolean b = i == roleIds.size() && result == 1;
if (b) {
// 注冊成功
return 0;
} else {
// 注冊失敗
return 2;
}
}3.2.3 控制器層編碼
在LoginRegController類中完成用戶登錄接口從前端接收參數(shù)到調(diào)用UserService服務(wù)類完成用戶注冊業(yè)務(wù)
@RequestMapping(value = "/login_page", method = RequestMethod.GET)
@ApiOperation(value = "loginPage", notes = "尚未登錄跳轉(zhuǎn)", produces = "application/json",
consumes = "application/json", response = RespBean.class)
public RespBean loginPage() {
return new RespBean(ResponseStateConstant.UN_AUTHORIZED, "尚未登錄,請登錄!");
}
@PostMapping("/user/reg")
@ApiOperation(value = "reg", notes = "用戶注冊", produces = "application/json",
consumes = "application/json", response = RespBean.class)
public RespBean reg(@RequestBody UserDTO user) {
int result = userService.registerUser(user);
if (result == 0) {
//成功
return new RespBean(ResponseStateConstant.SERVER_SUCCESS, "注冊成功!");
} else if (result == 1) {
return new RespBean(ResponseStateConstant.DUPLICATE_ERROR, "用戶名重復(fù),注冊失敗!");
} else {
//失敗
return new RespBean(ResponseStateConstant.SERVER_ERROR, "注冊失敗!");
}
}由于以上兩個接口都是需要放開權(quán)限控制的,因此完成以上兩個接口的編碼后還需要在security配置類WebSecurityConfig類中支持匿名訪問
只需要在configure(HttpSecurity http)方法中添加如下幾行代碼即可
http.authorizeRequests()
.antMatchers("/user/reg").anonymous()
.antMatchers("/upload/user/avatar").anonymous()完成后端編碼后可以啟動Mysql服務(wù)和redis服務(wù),然后運行BlogserverApplication類中的Main方法成功后就可以通過postman工具測試接口了
4 前端代碼實現(xiàn)
4.1 完成用戶注冊界面vue組件編碼
在src/views目錄下新建register文件夾,然后在register目錄下新建index.vue文件
完成用戶注冊組件編碼
這里的文件上傳選擇了element-ui組件庫中的upload組件
<template>
<div class="register-container">
<el-form :model="registerModel" :rules="rules" ref="registerForm" label-width="100px" class="register-form">
<el-form-item label="用戶賬號" prop="userAccount" required>
<el-input
v-model="registerModel.userAccount"
placeholder="請輸入用戶名"/>
</el-form-item>
<el-form-item label="用戶昵稱" prop="nickName" required>
<el-input
v-model="registerModel.nickName"
type="text"
placeholder="請輸入用戶昵稱"/>
</el-form-item>
<el-form-item label="登錄密碼" prop="password" required>
<el-input
v-model="registerModel.password"
type="password"
placeholder="請輸入密碼"
suffix-icon="el-icon-lock"/>
</el-form-item>
<el-form-item label="確認(rèn)密碼" prop="password2" required>
<el-input
v-model="registerModel.password2"
type="password"
:show-password="false"
placeholder="請再次輸入密碼"
suffix-icon="el-icon-lock" />
</el-form-item>
<el-form-item label="頭像">
<el-upload class="avatar-uploader"
:show-file-list="false"
accept="image"
:action="uploadAvatarUrl"
:on-preview="previewAvatar"
:before-upload="beforeAvartarUpload"
:on-success="handleSuccessAvatar"
>
<img v-if="avatarUrl" :src="avatarUrl" class="avatar" />
<div v-else class="upload-btn" >
<el-button>點擊上傳頭像</el-button>
<div slot="tip" class="el-upload__tip">只能上傳jpg/png文件,且不超過10M</div>
</div>
</el-upload>
</el-form-item>
<el-form-item label="手機(jī)號" prop="phoneNum" required>
<el-input type="tel"
v-model="registerModel.phoneNum"
placeholder="請輸入手機(jī)號"
/>
</el-form-item>
<el-form-item label="郵箱" prop="email">
<el-input type="email"
v-model="registerModel.email"
placeholder="請輸入你的郵箱" />
</el-form-item>
<el-form-item class="btn-area">
<el-button class="submit-btn" type="primary" :loading="onLoading" @click="handleRegister('registerForm')">提交</el-button>
<el-button class="reset-btn" type="info" @click="resetForm('registerForm')">重置</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { Message } from 'element-ui'
import { isNumber, validatePhoneNum, validatePassword, validEmail } from '@/utils/validate'
export default {
name: 'register',
data(){
// 密碼校驗器
const passwordValidator = (rule,value, callback) =>{
console.log(rule)
if(!validatePassword(value)){
callback('密碼強(qiáng)度不滿足要求,密碼必須同時包含字母、數(shù)字和特殊字符,請重新輸入')
} else {
callback()
}
}
// 二次密碼校驗器
const password2Validator = (rule, value, callback) => {
console.log(rule)
const password = this.registerModel.password
if(password!=value){
callback(new Error('兩次輸入的密碼不一致'))
} else {
callback()
}
}
// 手機(jī)號碼校驗器
const phoneNumValidator = (rule, value, callback)=> {
console.log(rule)
if(!(value.length==11 && isNumber(value))){
callback(new Error('手機(jī)號碼必須是11位數(shù)字'))
} else if(!validatePhoneNum(parseInt(value))){
callback(new Error('手機(jī)號碼不合法'))
} else {
callback()
}
}
// 郵件地址校驗器
const emailValidator = (rule, value, callback) => {
console.log(rule)
if(value!='' && !validEmail(value)){
callback(new Error('郵箱地址不合法'))
} else {
callback()
}
}
// 區(qū)分本地開發(fā)環(huán)境和生產(chǎn)環(huán)境
let uploadAvatarUrl = ''
if(window.location.host='localhost'){
uploadAvatarUrl = 'http://localhost:8081/blog/upload/user/avatar'
} else {
uploadAvatarUrl = 'http://www.javahsf.club:8081/blog/upload/user/avatar'
}
return {
uploadAvatarUrl: uploadAvatarUrl,
registerModel: {
userAccount: '',
nickName: '',
password: '',
password2: '',
avatarSize: 32,
uploadUrl: uploadUrl,
phoneNum: '',
email: ''
},
onLoading: false,
avatarUrl: '',
password2Style: {
dispaly: 'none',
color: 'red'
},
// 表單校驗規(guī)則
rules: {
userAccount: [
{ required: true, message: '請輸入用戶賬號', trigger: 'blur' },
{ min: 2, max: 64, message: '2-64個字符', trigger: 'blur' }
],
nickName: [
{ required: true, message: '請輸入昵稱', trigger: 'blur' },
{ min: 2, max: 64, message: '長度控制在2-64個字符',trigger: 'blur' }
],
password: [
{ required: true, message: '請輸入密碼', trigger: 'blur' },
{ min: 6, max: 18, message: '長度控制在6-18個字符', trigger: 'blur' },
{ validator: passwordValidator, trigger: 'blur' }
],
password2: [
{ required: true, message: '請再次輸入密碼', trigger: 'blur' },
{ min: 6, max: 18, message: '長度控制在6-18個字符', trigger: 'blur' },
{ validator: password2Validator, trigger: 'blur' }
],
phoneNum: [
{ required: true, message: '請輸入手機(jī)號', trigger: 'blur'},
{ validator: phoneNumValidator, trigger: 'blur' }
],
email: [
{ min: 0, max: 64, message: '長度控制在64個字符'},
{ validator: emailValidator, trigger: 'blur' }
]
},
redirect: undefined
}
},
watch: {
$route: {
handler: function(route) {
const query = route.query
if (query) {
this.redirect = query.redirect
this.otherQuery = this.getOtherQuery(query)
}
},
immediate: true
}
},
methods: {
// 圖片上傳之前校驗圖片格式和附件大小
beforeAvartarUpload(file) {
console.log(file)
if(!(file.type=='image/jpeg' ||file.type=='image/png')){
Message.error('頭像圖片必須是jpg或png格式')
}else if(file.size/(1024*1024)>10){
Message.error('圖片大小不能超過10M')
}
},
// 上傳圖片預(yù)覽
previewAvatar(file){
console.log(file)
},
// 圖片上傳成功回調(diào)
handleSuccessAvatar(response){
console.log(response.data)
this.avatarUrl = response.data
},
// 提交注冊
handleRegister(formName){
this.$refs[formName].validate((valid=>{
if(valid){ // 表單校驗通過
const params = {
username: this.registerModel.userAccount,
nickname: this.registerModel.nickName,
password: this.registerModel.password,
phoneNum: this.registerModel.phoneNum,
email: this.registerModel.email,
userface: this.avatarUrl
}
this.onLoading = true
this.$store.dispatch('user/register', params).then(res=>{
this.onLoading = true
if(res.status===200){
Message.success('恭喜注冊成功,現(xiàn)在就可以登錄系統(tǒng)了!')
// 跳轉(zhuǎn)到登錄界面
this.$router.push({ path: '/login', query: this.otherQuery })
} else {
Message.error(res.msg)
}
})
}else{ // 表單校驗不通過,拒絕提交注冊
this.onLoading = true
Message.error('用戶注冊信息校驗不通過,請重新填寫注冊信息')
return false
}
}))
},
// 表單重置
resetForm(formName) {
this.$refs[formName].resetFields()
},
getOtherQuery(query) {
return Object.keys(query).reduce((acc, cur) => {
if (cur !== 'redirect') {
acc[cur] = query[cur]
}
return acc
}, {})
}
}
}
</script>
<!--頁面樣式-->
<style lang="scss" scoped>
.register-container{
margin-top: 100px;
margin-left: 10%;
.el-input{
width: 60%;
}
.avatar-uploader .avatar{
width: 240px;
height: 240px;
}
.el-button.submit-btn{
width: 10%;
height: 40px;
margin-left: 150px;
margin-right: 25px;
}
.el-button.reset-btn{
width: 10%;
height: 40px;
}
}
</style>4.2 工具類中增加校驗方法
src/utils/validate.js中增加校驗密碼和手機(jī)號碼的方法
export function validatePhoneNum(phoneNum) {
const reg = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/
return reg.test(phoneNum)
}
export function validatePassword(password) {
// 強(qiáng)密碼:字母+數(shù)字+特殊字符
const reg = /^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^&*]+$)(?![a-zA-z\d]+$)(?![a-zA-z!@#$%^&*]+$)(?![\d!@#$%^&*]+$)[a-zA-Z\d!@#$%^&*]+$/
return reg.test(password)
}以上校驗均使用正則表達(dá)式校驗
4.3 API文件中添加用戶注冊方法
src/api/user.js文件中新增用戶注冊接口方法
export function register(data) {
return request({
url: '/user/reg',
method: 'post',
data
})
}4.4 全局方法中添加用戶注冊方法
src/store/modules/user.js 文件中的actions對象中增加用戶注冊行為方法
const actions = {
// user register
register({ commit }, registerInfo) {
return new Promise((resolve, reject) => {
register(registerInfo).then(response => {
if (response.status === 200 && response.data.status === 200) {
const resInfo = { status: response.status, msg: '注冊成功' }
resolve(resInfo)
} else {
const resInfo = { status: response.status, msg: response.data.msg }
resolve(resInfo)
}
}).catch(error => {
console.error(error)
reject(error)
})
})
},
// ......省略其他已有方法
}
因為用戶注冊完之后需要跳轉(zhuǎn)到登錄界面,直接在注冊頁面調(diào)用后臺用戶注冊接口成功后調(diào)用this.$router.push方法發(fā)現(xiàn)無法實現(xiàn)頁面的跳轉(zhuǎn)效果, 因此改為在vuex的全局dispatch中調(diào)用注冊接口
4.5 路由列表中添加用戶注冊組件
在src/router/index.js文件的固定路由列表中添加注冊組件的路由
import Register from '@/views/register/index'
export const constantRoutes = [
{
id: '0',
path: '/register',
component: Register,
hidden: true
},
//...... 省略其他路由
]4.6 登錄組件中添加用戶注冊的跳轉(zhuǎn)鏈接
在src/views/login/index.vue文件中的模板代碼部分的登錄按鈕標(biāo)簽下面添加如下兩行代碼
<div> <router-link to="/resetPass" class="forget-password">忘記密碼</router-link> <router-link class="register" to="/register">注冊賬號</router-link> </div>
同時對忘記密碼和注冊賬號兩個鏈接添加樣式(忘記密碼功能尚待實現(xiàn))
<style lang="scss" scoped>
.register, .forget-password{
width: 20%;
height: 35px;
color: blue;
margin-right: 20px;
cursor: pointer;
}
</style>4.7 路由跳轉(zhuǎn)控制中添加白名單
在路由跳轉(zhuǎn)控制文件src/permission.js文件中將注冊用戶的路由添加到白名單中
const whiteList = ['/login', '/register', '/auth-redirect'] // no redirect whitelist
如果不在白名單中加上用戶注冊的路由,你會發(fā)現(xiàn)在用戶登錄界面壓根無法跳轉(zhuǎn)到用戶注冊界面的
5 效果體驗
在啟動后端服務(wù)后,在vue-element-admin項目下通過 鼠標(biāo)右鍵->git bash進(jìn)入命令控制臺
然后輸入npm run dev 項目啟動前端服務(wù)
然后在谷歌瀏覽器中輸入:http://localhost:3000/回車進(jìn)入登錄界面

點擊下面的【注冊賬號】鏈接就能跳轉(zhuǎn)到用【用戶注冊】頁面

填寫好用戶注冊信息后就可以點擊下面的【提交】按鈕提交注冊了,注冊成功后系統(tǒng)會彈框提示用戶中注冊成功,并重新跳轉(zhuǎn)到【用戶登錄】界面
6 寫在最后
本文演示了在spring-boot項目中繼承阿里云對象存儲sdk實現(xiàn)了圖片上傳和用戶提交登錄兩個接口的詳細(xì)實現(xiàn),同時前端使用element-ui庫中的upload組件調(diào)用后端圖片上傳接口實現(xiàn)了附件上傳功能,實現(xiàn)了一個完整的用戶登錄信息的校驗和提交注冊及注冊成功后的頁面跳轉(zhuǎn)等功能。
相信對想要了解一個系統(tǒng)的用戶模塊是如何實現(xiàn)用戶的注冊以及注冊成功后的頁面跳轉(zhuǎn)的完整功能的是如何實現(xiàn)的讀者朋友一定會有所幫助的!
本文前后端項目代碼git倉庫地址如下,對源碼感興趣的讀者朋友可以克隆到本地參考
到此這篇關(guān)于SpringBoot項目整合Vue做一個完整的用戶注冊功能的文章就介紹到這了,更多相關(guān)SpringBoot項目整合Vue做一個完整的用戶注冊功能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 詳解springboot?springsecuroty中的注銷和權(quán)限控制問題
- SpringBoot使用Spring Security實現(xiàn)登錄注銷功能
- SpringBoot--- SpringSecurity進(jìn)行注銷權(quán)限控制的配置方法
- Vue+springboot批量刪除功能實現(xiàn)代碼
- springboot和vue前后端交互的實現(xiàn)示例
- SpringBoot3結(jié)合Vue3實現(xiàn)用戶登錄功能
- 基于SpringBoot和Vue3的博客平臺的用戶注冊與登錄功能實現(xiàn)
- SpringBoot和Vue.js實現(xiàn)的前后端分離的用戶權(quán)限管理系統(tǒng)
- Vue結(jié)合Springboot實現(xiàn)用戶列表單頁面(前后端分離)
- vue+springboot用戶注銷功能實現(xiàn)代碼
相關(guān)文章
SpringBoot調(diào)用公共模塊的自定義注解失效的解決
這篇文章主要介紹了SpringBoot調(diào)用公共模塊的自定義注解失效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02
Java內(nèi)存模型(JMM)及happens-before原理
這篇文章主要介紹了java內(nèi)存模型(JMM)及happens-before原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-04-04
如何為Spring Cloud Gateway加上全局過濾器
這篇文章主要介紹了如何為Spring Cloud Gateway加上全局過濾器,幫助大家更好得理解和學(xué)習(xí)使用Gateway,感興趣的朋友可以了解下2021-03-03
Springboot如何使用mybatis實現(xiàn)攔截SQL分頁
這篇文章主要介紹了Springboot使用mybatis實現(xiàn)攔截SQL分頁,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06
淺談如何優(yōu)雅地停止Spring Boot應(yīng)用
這篇文章主要介紹了淺談如何優(yōu)雅地停止Spring Boot應(yīng)用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05
Spring中ApplicationListener的使用解析
這篇文章主要介紹了Spring中ApplicationListener的使用解析,ApplicationContext事件機(jī)制是觀察者設(shè)計模式的實現(xiàn),通過ApplicationEvent類和ApplicationListener接口,需要的朋友可以參考下2023-12-12
解決mybatis-plus3.1.1版本使用lambda表達(dá)式查詢報錯的方法
這篇文章主要介紹了解決mybatis-plus3.1.1版本使用lambda表達(dá)式查詢報錯的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08

