【SpringBoot开发】之商城项目案例(实现登陆版)

 🎉🎉欢迎来到我的CSDN主页!🎉🎉

🏅我是君易--鑨,一个在CSDN分享笔记的博主。📚📚

🌟推荐给大家我的博客专栏《SpringBoot开发之商城项目系列》。🎯🎯

🎁如果感觉还不错的话请给我关注加三连吧!🎁🎁


前言

        在上一期的博客分享中我们初步的将项目搭建起来了,简单的实现了首页的数据数据库绑定查询显示,今天给大家带来的是登陆的功能实现,以及对其登陆功能的完善。

一、登陆功能实现代码编写

1. 导入数据回显的响应封装类

        我们导入的是前面几期的博客中分享到的响应封装类,用户数据回显以及数据查询的状态。

 JsonResponseBody.java

package com.yx.yxshop.resp;import lombok.Data;@Data
public class JsonResponseBody<T> {private Integer code;private String msg;private T data;private Long total;private JsonResponseBody(JsonResponseStatus jsonResponseStatus, T data) {this.code = jsonResponseStatus.getCode();this.msg = jsonResponseStatus.getMsg();this.data = data;}private JsonResponseBody(JsonResponseStatus jsonResponseStatus, T data, Long total) {this.code = jsonResponseStatus.getCode();this.msg = jsonResponseStatus.getMsg();this.data = data;this.total = total;}public static <T> JsonResponseBody<T> success() {return new JsonResponseBody<T>(JsonResponseStatus.OK, null);}public static <T> JsonResponseBody<T> success(T data) {return new JsonResponseBody<T>(JsonResponseStatus.OK, data);}public static <T> JsonResponseBody<T> success(T data, Long total) {return new JsonResponseBody<T>(JsonResponseStatus.OK, data, total);}public static <T> JsonResponseBody<T> unknown() {return new JsonResponseBody<T>(JsonResponseStatus.UN_KNOWN, null);}public static <T> JsonResponseBody<T> other(JsonResponseStatus jsonResponseStatus) {return new JsonResponseBody<T>(jsonResponseStatus, null);}}

 JsonResponseStatus.java

package com.yx.yxshop.resp;import lombok.Getter;@Getter
public enum JsonResponseStatus {OK(200, "OK"),UN_KNOWN(500, "未知错误"),LOGIN_MOBILE_INFO(5001, "未携带手机号或手机号格式有误"),LOGIN_PASSWORD_INFO(5002, "未携带密码或不满足格式"),LOGIN_NO_EQUALS(5003, "登录信息不一致"),LOGIN_MOBILE_NOT_FOUND(5004, "登录手机号未找到"),;private final Integer code;private final String msg;JsonResponseStatus(Integer code, String msg) {this.code = code;this.msg = msg;}public String getName(){return this.name();}}

2. 控制层编写请求

        在编写控制层的请求方法之前首先新建一个文件夹存放我们的vo视图类,vo实体类用于操作数据库的传参设置,避免改动对应表的实体对象类

UserVo.java
package com.yx.yxshop.vo;import lombok.Data;/*** com.yx.yxshop.vo** @author 君易--鑨* @site www.yangxin.com* @company 木易* @create 2023/12/28* 确保实体类的正确性,用于请求操作*/
@Data  //提供set/get方法
public class UserVo {//    定义所需的属性private String phone;//手机号码private String password;//用户密码}

        借助我们的JsonResponseBody响应封装了进行编写登陆请求以及登陆的方法、接口以及接口实现类

 IUserService.java
package com.yx.yxshop.service;import com.yx.yxshop.pojo.User;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yx.yxshop.resp.JsonResponseBody;
import com.yx.yxshop.vo.UserVo;/*** <p>* 用户信息表 服务类* </p>** @author yangxin* @since 2023-12-27*/
public interface IUserService extends IService<User> {JsonResponseBody<?> login(UserVo vo);}
UserServiceImpl.java 
package com.yx.yxshop.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yx.yxshop.pojo.User;
import com.yx.yxshop.mapper.UserMapper;
import com.yx.yxshop.resp.JsonResponseBody;
import com.yx.yxshop.resp.JsonResponseStatus;
import com.yx.yxshop.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yx.yxshop.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;/*** <p>* 用户信息表 服务实现类* </p>** @author yangxin* @since 2023-12-27*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic JsonResponseBody<?> login(UserVo vo) {//        对其带来的参数对象进行判断
//        电话号码if (vo.getPhone()==null){
//                  返回没有手机号码的异常状态return JsonResponseBody.other(JsonResponseStatus.LOGIN_MOBILE_INFO);}
//        密码if (vo.getPassword()==null){
//            返回没有密码的异常状态return JsonResponseBody.other(JsonResponseStatus.LOGIN_PASSWORD_INFO);}
//     从数据库中查询用户信息User one = getOne(new QueryWrapper<User>().lambda()                       //false表示有多条数据不报错.eq(User::getId, vo.getPhone()).eq(User::getPassword, vo.getPassword()), false);if (one==null){//为查询到指定数据return JsonResponseBody.other(JsonResponseStatus.LOGIN_NO_EQUALS);//返回登录信息不一致的}
//        登录成功   (查询到了指定的数据)return JsonResponseBody.success(one);}
}
 UserController.java
package com.yx.yxshop.controller;import com.yx.yxshop.pojo.User;
import com.yx.yxshop.resp.JsonResponseBody;
import com.yx.yxshop.service.IUserService;
import com.yx.yxshop.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** <p>* 用户信息表 前端控制器* </p>** @author yangxin* @since 2023-12-27*/
@Controller
@RequestMapping("/user")
public class UserController {// 引入用户接口类@Autowiredprivate IUserService userService;//    编写一个用户登陆的请求public JsonResponseBody<?> userLogin(UserVo vo){return userService.login(vo);}}

        代码编写到这里我们的登陆的基本功能已经基本的实现了,但是我们接下来会对其进行完善。

二、完善登陆之全局异常处理

        我们直接对其抛出异常,但是我们的系统也会对其抛出异常。因此我们新建一个包,用于管理我们的全局异常。

 
BusinessException.java
package com.yx.yxshop.exp;import com.yx.yxshop.resp.JsonResponseStatus;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;/*** 错误异常处理类*/
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
@Data
public class BusinessException extends RuntimeException {private JsonResponseStatus jsonResponseStatus;}

        全局异常处理类,用于处理全局的异常捕捉。

 GlobalExceptionHandler.java
package com.yx.yxshop.exp;import com.yx.yxshop.resp.JsonResponseBody;
import com.yx.yxshop.resp.JsonResponseStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice//增强类返回json数据
//@ControllerAdvice//增强类返回页面
@Slf4j
public class GlobalExceptionHandler {@ExceptionHandler(BusinessException.class)public JsonResponseBody<?> BusinessExceptionHandler(BusinessException e){
//        获取异常状态JsonResponseStatus status = e.getJsonResponseStatus();
//        返回json数据return JsonResponseBody.other(status);}//    捕捉所有的异常@ExceptionHandler(Throwable.class)public JsonResponseBody<?> BusinessExceptionHandler(Throwable e){
//        返回json数据return JsonResponseBody.unknown();}
}

        在接口实现类进行稍微的调整

         我们这里直接运行项目,然后直接访问登陆的请求

三、完善登陆之jsr303验证

        集成jsr303验证首先要导入对应的pom文件依赖

<!--   导入jsr303依赖  --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>

         集成了之后我们在实体类进行对应的编写修改,在属性上打上注释进行标记

         在控制类进行标记使用

         在全局异常类中添加一个方法进行捕捉异常

         我们在网页上直接进行发送登陆请求

四、完善登陆之密码加密

1. 前端进行加密

        首先在我们的登陆界面编写登陆的点击事件

<script>
<#--			设置登录的点击事件-->$("#login").click(()=>{// 获取到密码与手机号码let phone = $("#mobile").val();let password = $("#password").val();// 发送登陆的post请求$.post('${springMacroRequestContext.contextPath}/user/login',{// 传递参数phone,password},resq=>{//接受对象类型},"json")})</script>

        接下来我们进行加密,首先我们先导入我们的js,选择的函数MD5加密

    <script src="http://www.gongjuji.net/Content/files/jquery.md5.js" type="text/javascript"></script>

         进行数据对其加密

 

         然后我们进入登陆,在控制天查看可以查看加密后的密码

         我们导入MD5的工具类,进行后台加密

 MD5Utils.java
package com.yx.yxshop.utils;import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;import java.nio.charset.StandardCharsets;
import java.util.UUID;@Component
public class MD5Utils {//加密盐,与前端一致private static final String salt = "f1g2h3j4";public static String md5(String src) {return DigestUtils.md5DigestAsHex(src.getBytes(StandardCharsets.UTF_8));}public static String createSalt() {return UUID.randomUUID().toString().replace("-", "");}/*** 将前端的明文密码通过MD5加密方式加密成后端服务所需密码,混淆固定盐salt,安全性更可靠*/public static String inputPassToFormPass(String inputPass) {String str = salt.charAt(1) + String.valueOf(salt.charAt(5)) + inputPass + salt.charAt(0) + salt.charAt(3);return md5(str);}/*** 将后端密文密码+随机salt生成数据库的密码,混淆固定盐salt,安全性更可靠*/public static String formPassToDbPass(String formPass, String salt) {String str = salt.charAt(7) + String.valueOf(salt.charAt(9)) + formPass + salt.charAt(1) + salt.charAt(5);return md5(str);}public static void main(String[] args) {String formPass = inputPassToFormPass("123456");System.out.println("前端加密密码:" + formPass);String salt = createSalt();System.out.println("后端加密随机盐:" + salt);String dbPass = formPassToDbPass(formPass, salt);System.out.println("后端加密密码:" + dbPass);}}

        然后对其前台传来的密码进行二次加密,在service层进行加密

package com.yx.yxshop.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yx.yxshop.exp.BusinessException;
import com.yx.yxshop.pojo.User;
import com.yx.yxshop.mapper.UserMapper;
import com.yx.yxshop.resp.JsonResponseBody;
import com.yx.yxshop.resp.JsonResponseStatus;
import com.yx.yxshop.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yx.yxshop.vo.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;import java.nio.charset.StandardCharsets;import static com.yx.yxshop.resp.JsonResponseStatus.LOGIN_NO_EQUALS;/*** <p>* 用户信息表 服务实现类* </p>** @author yangxin* @since 2023-12-27*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {@Overridepublic JsonResponseBody<?> login(UserVo vo) {//     从数据库中查询用户信息User one = getOne(new QueryWrapper<User>().lambda()                       //false表示有多条数据不报错.eq(User::getId, vo.getPhone()), false);//不带密码查询if (one==null){//为查询到指定数据throw new BusinessException(LOGIN_NO_EQUALS);//抛出登录信息不一致的}
//     加前端的密码+数据库的盐加密String str =vo.getPassword()+one.getSalt();str = DigestUtils.md5DigestAsHex(str.getBytes(StandardCharsets.UTF_8));
//         判断if (!str.equals(one.getPassword())){//判断加密的和数据库对比throw new BusinessException(LOGIN_NO_EQUALS);//抛出异常}
//        登录成功   (查询到了指定的数据)return JsonResponseBody.success();}
}

         然后我们运行进行登陆测试

        我们最后将登陆的请求方法完善在前端的JS中 

五、完善登陆之Redis集成使用

        当我们登陆完成之后我们在后续的很多地方需要获取到用户信息进行操作,例如:添加购物车、下单、主页显示登陆用户,因此我们使用的是Redis来存储我们的登陆用户信息。

1. 导入Redis的pom依赖

<!--       导入Redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

         当然我们集成了redis之后还要将我们的Redis服务打开才能正常使用

2. 配置yml连接

        在yml文件中进行我们redis相关的文件配置

    redis:host: 127.0.0.1port: 6379password: 123456database: 0

3. 集成使用 

         接下来就是对其集成Redis到我们的项目之中进行使用,在UserSeriviceImpl中进行使用

         考虑到后续存放我们的用于信息时我们的键重复时,因此我们借用使用雪花ID进行生成键。对其的导入其pom文件的依赖。

<dependency><groupId>com.github.yitter</groupId><artifactId>yitter-idgenerator</artifactId><version>1.0.6</version></dependency>

         接下来就是雪花ID与Redis的集成使用

         我们现在的项目并不是前后端分离的项目,所以前端要获取到用户的信息需要我们使用到Cookie存储,因此需要借助一些工具类

CookieUtils.java
package com.yx.yxshop.utils;import lombok.extern.slf4j.Slf4j;import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;@Slf4j
public class CookieUtils {/*** @Description: 得到Cookie的值, 不编码*/public static String getCookieValue(HttpServletRequest request, String cookieName) {return getCookieValue(request, cookieName, false);}/*** @Description: 得到Cookie的值*/public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {Cookie[] cookieList = request.getCookies();if (cookieList == null || cookieName == null) {return null;}String retValue = null;try {for (int i = 0; i < cookieList.length; i++) {if (cookieList[i].getName().equals(cookieName)) {if (isDecoder) {retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");} else {retValue = cookieList[i].getValue();}break;}}} catch (UnsupportedEncodingException e) {e.printStackTrace();}return retValue;}/*** @Description: 得到Cookie的值*/public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {Cookie[] cookieList = request.getCookies();if (cookieList == null || cookieName == null) {return null;}String retValue = null;try {for (int i = 0; i < cookieList.length; i++) {if (cookieList[i].getName().equals(cookieName)) {retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);break;}}} catch (UnsupportedEncodingException e) {e.printStackTrace();}return retValue;}/*** @Description: 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue) {setCookie(request, response, cookieName, cookieValue, -1);}/*** @param request* @param response* @param cookieName* @param cookieValue* @param cookieMaxage* @Description: 设置Cookie的值 在指定时间内生效,但不编码*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, int cookieMaxage) {setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);}/*** @Description: 设置Cookie的值 不设置生效时间,但编码* 在服务器被创建,返回给客户端,并且保存客户端* 如果设置了SETMAXAGE(int seconds),会把cookie保存在客户端的硬盘中* 如果没有设置,会默认把cookie保存在浏览器的内存中* 一旦设置setPath():只能通过设置的路径才能获取到当前的cookie信息*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, boolean isEncode) {setCookie(request, response, cookieName, cookieValue, -1, isEncode);}/*** @Description: 设置Cookie的值 在指定时间内生效, 编码参数*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, int cookieMaxage, boolean isEncode) {doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);}/*** @Description: 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, int cookieMaxage, String encodeString) {doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);}/*** @Description: 删除Cookie带cookie域名*/public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,String cookieName) {doSetCookie(request, response, cookieName, null, -1, false);}/*** @Description: 设置Cookie的值,并使其在指定时间内生效*/private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {try {if (cookieValue == null) {cookieValue = "";} else if (isEncode) {cookieValue = URLEncoder.encode(cookieValue, "utf-8");}Cookie cookie = new Cookie(cookieName, cookieValue);if (cookieMaxage > 0)cookie.setMaxAge(cookieMaxage);if (null != request) {// 设置域名的cookieString domainName = getDomainName(request);log.info("========== domainName: {} ==========", domainName);if (!"localhost".equals(domainName)) {cookie.setDomain(domainName);}}cookie.setPath("/");response.addCookie(cookie);} catch (Exception e) {e.printStackTrace();}}/*** @Description: 设置Cookie的值,并使其在指定时间内生效*/private static void doSetCookie(HttpServletRequest request, HttpServletResponse response,String cookieName, String cookieValue, int cookieMaxage, String encodeString) {try {if (cookieValue == null) {cookieValue = "";} else {cookieValue = URLEncoder.encode(cookieValue, encodeString);}Cookie cookie = new Cookie(cookieName, cookieValue);if (cookieMaxage > 0)cookie.setMaxAge(cookieMaxage);if (null != request) {// 设置域名的cookieString domainName = getDomainName(request);log.info("========== domainName: {} ==========", domainName);if (!"localhost".equals(domainName)) {cookie.setDomain(domainName);}}cookie.setPath("/");response.addCookie(cookie);} catch (Exception e) {e.printStackTrace();}}/*** @Description: 得到cookie的域名*/private static String getDomainName(HttpServletRequest request) {String domainName = null;String serverName = request.getRequestURL().toString();if (serverName == null || serverName.equals("")) {domainName = "";} else {serverName = serverName.toLowerCase();serverName = serverName.substring(7);final int end = serverName.indexOf("/");serverName = serverName.substring(0, end);if (serverName.indexOf(":") > 0) {String[] ary = serverName.split("\\:");serverName = ary[0];}final String[] domains = serverName.split("\\.");int len = domains.length;if (len > 3 && !isIp(serverName)) {// www.xxx.com.cndomainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];} else if (len <= 3 && len > 1) {// xxx.com or xxx.cndomainName = "." + domains[len - 2] + "." + domains[len - 1];} else {domainName = serverName;}}return domainName;}public static String trimSpaces(String IP) {//去掉IP字符串前后所有的空格while (IP.startsWith(" ")) {IP = IP.substring(1, IP.length()).trim();}while (IP.endsWith(" ")) {IP = IP.substring(0, IP.length() - 1).trim();}return IP;}public static boolean isIp(String IP) {//判断是否是一个IPboolean b = false;IP = trimSpaces(IP);if (IP.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")) {String s[] = IP.split("\\.");if (Integer.parseInt(s[0]) < 255)if (Integer.parseInt(s[1]) < 255)if (Integer.parseInt(s[2]) < 255)if (Integer.parseInt(s[3]) < 255)b = true;}return b;}}

        导入完其帮助类时我们在UserServiceImpl进行运用使用,但是需要请求响应,在控制层进行带入。 

        后面就是其运用了。 

        在进行测试之前必须在实体类中要进行一个修改 

         导入一个我们Redis的配置类利于我们存储数据及数据查看

 RedisConfig.java
package com.yx.yxshop.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setConnectionFactory(connectionFactory);redisTemplate.afterPropertiesSet();return redisTemplate;}}

        然后进行运行测试项目

 

         我们运行之后在网页进行访问页面,进行登陆的操作在网页的应用程序生成了一个Cookie和在Redis缓存中生成对应的用户信息。但是以后在完善代码的过程中要多次获取Cookie中的用户信息,我们把所涉及到的代码给他封装成一个类。

 IRedisService.java
package com.yx.yxshop.service;import com.yx.yxshop.pojo.User;/*** com.yx.yxshop.service** @author 君易--鑨* @site www.yangxin.com* @company 木易* @create 2023/12/30* reids封装类,用于封装redis操作*/
public interface IRedisService {/*** 保存用户*/void saveUser(String key, User user);/*** 读取用户*/User loadUser(String key);
}
 IRedisServiceImpl.java
package com.yx.yxshop.service.impl;import com.yx.yxshop.pojo.User;
import com.yx.yxshop.service.IRedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;/*** com.yx.yxshop.service.impl** @author 君易--鑨* @site www.yangxin.com* @company 木易* @create 2023/12/30* 接口实现类*/
@Service
public class IRedisServiceImpl implements IRedisService {@Autowiredprivate RedisTemplate redisTemplate;@Overridepublic void saveUser(String token, User user) {//        将个人信息存放到我们的缓存中redisTemplate.opsForValue().set("user:"+token,user);}@Overridepublic User loadUser(String token) {return (User) redisTemplate.opsForValue().get("user:"+token);}
}

        控制层引用封装类

 

           在代码当中尽量不要出现"user:",尽量使用常量,我们新建一个类进行定义我们常用的常量。

Constans.java
package com.yx.yxshop.core;/*** com.yx.yxshop.core** @author 君易--鑨* @site www.yangxin.com* @company 木易* @create 2023/12/30* 常量类*/
public class Constans {public static final String REDIS_USER_PREFIX = "user:";//缓存前缀类public static final String REDIS_CAR_PREFIX = "car:";//购物车常量public static final String REDIS_ORDER_PREFIX = "order:";//订单常量}

         在方法中进行引用

        为了登陆成功在首页显示登陆用户信息的姓名是,我们在后台进行一个Cookie存值。

         运行测试

 ​​​​​​​

六、自定义验证注解

        写一个自定义注解类

 IsTrue.java
package com.yx.yxshop.core;import javax.validation.Payload;
import java.lang.annotation.*;/*** com.yx.yxshop.core** @author 君易--鑨* @site www.yangxin.com* @company 木易* @create 2023/12/30* 自定义注解类*/@Retention(RetentionPolicy.RUNTIME)//该注解表示项目运行时有效
@Documented//生成文档注解
@Target(ElementType.FIELD)//代表该注解可以用于属性字段
public @interface IsTrue {/**** 这个字段是否必须*/boolean required() default false;/**** 字段的正则*/String expr() default "";//    错误提交信息String message() default "{javax.validation.constraints.NotBlank.message}";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};}

        我们这个自定义注解类的使用范围比较广,只需要填写两个参数,一个数是否启用,一个是正则。 我们还需要配置一个解析器

 IsTrueConstraintValidator.java
package com.yx.yxshop.core;import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.Data;import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;@Data
public class IsTrueConstraintValidator implements ConstraintValidator<IsTrue, String> {private boolean require;private String expr;@Overridepublic void initialize(IsTrue isTrue) {expr = isTrue.expr();require = isTrue.require();}@Overridepublic boolean isValid(String value, ConstraintValidatorContext context) {if (!require) return true;if (StringUtils.isEmpty(value)) return false;return value.matches(expr);}}

         有了解析器之后我们还需要在自定义注解类中指定

         后面对其进行使用

 ​​​​​​​

        最后运行项目进行登陆测试 


 

 🎉🎉本期的博客分享到此结束🎉🎉

📚📚各位老铁慢慢消化📚📚

🎯🎯下期博客博主会带来新货🎯🎯

🎁三连加关注,阅读不迷路 !🎁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/589936.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

msvcp140_1.dll丢失怎样修复,缺失msvcp140_1.dll是什么原因

在日常使用电脑的过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcp140_1.dll丢失”。那么&#xff0c;msvcp140_1.dll究竟是什么文件&#xff1f;为什么会出现丢失的情况&#xff1f;又该如何解决这个问题呢&#xff1f;本文将详细介绍msvcp140_1…

力扣-206. 反转链表

文章目录 力扣题目代码 力扣题目 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#x…

SQLSERVER排查CPU占用高

操作系统是Windows2008R2 ,数据库是SQL2008R2 64位 64G内存,16核CPU 硬件配置还是比较高的,他说服务器运行的是金蝶K3软件,数据库实例里有多个数据库 现象 他说是这几天才出现的,而且在每天的某一个时间段才会出现CPU占用高的情况 内存占用不太高,只占用了30个G CPU…

Android 跨进程之间通信(IPC)方式之ContentProvider

Android 跨进程之间通信 Android 跨进程之间通信(IPC)方式之BroadcastReceiverAndroid 跨进程之间通信(IPC)方式之ContentProvider 文章目录 Android 跨进程之间通信前言一、ContentProvider 是什么&#xff1f;二、如何利用ContentProvider跨进程通信1.创建自定义ContentProv…

关于发展模式加入变量的问题解决

问题1描述&#xff1a; 编译的时候不报错&#xff0c;但是在运行的时候出错输出 FLDLST: ustar in fincl( 17 ) not found ENDRUN: called without a message string 问题1解决&#xff1a; 这是因为在cas-esm 的atm_in 中写入了某个变量&#xff0c;但是在F90 代码里面没…

python使用动态规划解决不同路径问题

针对二维动态规划&#xff0c;还有一个问题就是关于求不同路径的实例&#xff0c;主要是说明在实际应用的场景中&#xff0c;要理解透彻实际问题的真正目的&#xff0c;就可以灵活实现代码编写。 对于求不同路径问题描述&#xff0c;对于一个机器人&#xff0c;处在一个mxn的网…

【Java 进阶篇】Maven 使用详解:打造便捷高效的项目构建利器

在软件开发的道路上&#xff0c;项目构建是一个不可避免的过程。而Maven&#xff0c;作为一个强大的项目管理和构建工具&#xff0c;为开发者提供了一套标准化的项目结构和构建流程。本文将围绕Maven的使用详解&#xff0c;手把手地带你探索Maven的世界&#xff0c;让你在项目构…

XTU-OJ-1452-完全平方数-笔记

参考博客 XTU-OJ 1452-完全平方数 题意 输入一个奇数&#xff0c;使得 n*(2*an-1)/2是一个完全平方数&#xff0c;求满足条件的最小的a 1<n<1e9 先输入样例数&#xff0c;再输入n 输入 2 1 3 输出 0 2 代码 #include<stdio.h>#define N 1000000010int a…

【UnityShader入门精要学习笔记】(1)了解渲染流水线

本系列为作者学习UnityShader入门精要而作的笔记&#xff0c;内容将包括&#xff1a; 书本中句子照抄 个人批注项目源码一堆新手会犯的错误潜在的太监断更&#xff0c;有始无终 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 文章目录 渲染流水线什么是流水线什么…

【教3妹学编程-算法题】经营摩天轮的最大利润

3妹&#xff1a;“打个中国结&#xff0c;再系个红腰带&#xff0c; 愿善良的人们天天好运来, 你勤劳生活美, 你健康春常在, 你一生的忙碌为了笑逐颜开。” 2哥 : 3妹&#xff0c;元旦快乐啊。 3妹&#xff1a;2哥元旦快乐~。 2哥&#xff1a;祝新的一年&#xff0c;3妹技术突飞…

SPI通信

SPI通信 1、SPI通信概述 SPI(Serial peripheral interface)是一种同步、串行、全双工、总线制、主从工作方式。 有四线控制&#xff1a; SDO——主设备数据输出&#xff0c;从设备数据输入&#xff0c;对于MOSI output slave inputSDI——主设备数据输入&#xff0c;从事设备…

Linux之进程管理

什么是进程 在linux中每个执行的程序都称为一个进程&#xff0c;每个进程都分配一个ID号&#xff08;pid进程号&#xff09;。每个进程都可能以两种方式存在&#xff0c;即前台和后天。前台进程就是用户目前的屏幕上可以进行操作的。后台进程则是实际在操作&#xff0c;但屏幕…

计算机网络【Google的TCP BBR拥塞控制算法深度解析】

Google的TCP BBR拥塞控制算法深度解析 宏观背景下的BBR 慢启动、拥塞避免、快速重传、快速恢复&#xff1a; 说实话&#xff0c;这些机制完美适应了1980年代的网络特征&#xff0c;低带宽&#xff0c;浅缓存队列&#xff0c;美好持续到了2000年代。 随后互联网大爆发&#x…

滴水逆向三期笔记与作业——02C语言——09 字节对齐_结构体数组

09 字节对齐_结构体数组 一、sizeof关键字1.1 基本类型的sizeof1.2 数组类型的sizeof1.3 结构体类型的sizeof 二、字节对齐--结构体对齐2.1 结构体对齐2.2 对齐规则2.2.1对其参数规则2.2.2 数据成员对齐规则2.2.2.3 结构体的总大小2.2.2.4 结构体嵌套 三、typedef关键字3.1 对已…

Python如何把类当做字典来访问及浅谈Python类命名空间

Python如何把类当做字典来访问 Python把类当做字典来访问 定义一个类将它实例化&#xff0c;我们可以通过obj.属性来访问类的属性&#xff0c;如果想获取类的所有实例变量&#xff0c;我们可以使用obj.__dict__来访问&#xff0c;如下&#xff1a; class A:def __init__(self)…

mount -a 出错任然重启问题

问题来源 在磁盘分区挂载过后&#xff0c;为了创建的新分区的能够永久挂载&#xff0c;我们常常会在/etc/fstab下写下配置文件&#xff0c;使其永久挂载。但是该配置一旦写错&#xff0c;就面临这死机问题&#xff0c;为此&#xff0c;以下操作针对该问题进行 解决方案&#x…

[每周一更]-(第43期):Golang版本的升级历程

从1.13接触go语言开始更新我们公司内第一个Go项目&#xff0c;直至现在go版本已经发展到1.20&#xff08;20230428&#xff09;&#xff0c;我们从go发版开始认识go语言&#xff0c;有利于我们更深入 了解这门语言&#xff0c;洞悉一些深层方式&#xff0c;加深我们学习的动力&…

3294 李白的酒

#include<bits/stdc.h> using namespace std; int main(){int n;double ans;scanf("%d",&n);for(int i1;i<n;i)ans1,ans/2;printf("%.5f",ans); }

基于蚁狮算法优化的Elman神经网络数据预测 - 附代码

基于蚁狮算法优化的Elman神经网络数据预测 - 附代码 文章目录 基于蚁狮算法优化的Elman神经网络数据预测 - 附代码1.Elman 神经网络结构2.Elman 神经用络学习过程3.电力负荷预测概述3.1 模型建立 4.基于蚁狮优化的Elman网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针…

CMake入门教程【基础篇】CMake+Minggw构建项目

文章目录 Minggw是什么Minggw下载CMake下载安装第1步&#xff1a;下载CMake第2步&#xff1a;安装CMake 如何构建和编译项目&#xff1a;使用CMake和MinGW总结 Minggw是什么 MinGW&#xff08;Minimalist GNU for Windows&#xff09;是一个免费的软件开发环境&#xff0c;旨在…