基于SpringBoot的秒杀系统出现的BUG以及难点(总结)

最近完成了一个后端项目,过程中遇到了一些难点和第一次遇到的BUG
分享一下,做个记录 ~~在这里插入图片描述

1、通过注解判断参数vo是否为空,并做全局异常处理

在开发中,controller层需要经常对vo参数进行格式判断,这样就可能在多个请求方法中出现格式判断,所以为了降低代码冗余,通过在注解来进行格式判断。

实现思路:添加依赖,在需要进行格式判断的类中添加判断注解,如果我们需要实现一些自己的判断,可以实现自定义注解。这样基本就已经实现完了,但是。还要考虑的就是,如果入参的格式不符合我们的要求,validation就会抛出异常,为了友好的返回结果,我们就会做全局异常处理。

第一部分:格式验证

  1. 添加依赖

            <!--validation验证依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-validation</artifactId>
            </dependency>
    
  2. 在入参时需要进行格式判断的类中添加判断注解

    //我这里以一个模板入参类为样例(参考)
    //其中@IsMobile为自定义注解
    @Data
    public class LoginVo {
        @NotNull //不能为空
        @IsMobile //mobile格式判断(我们的自定义注解)
        private String mobile;
    
        @NotNull
        @Length(min = 32) //长度要求
        private String password;
    
    }
    
  3. 实现自定义注解并注册到validation组件中

    /**
     * 验证手机号
     *
     * @author thenie
     * @since 1.0.0
     */
    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
    @Retention(RUNTIME)
    @Documented
    @Constraint(validatedBy = {IsMobileValidator.class})
    public @interface IsMobile {
    
        boolean required() default true;
    
        String message() default "手机号码格式错误";
    
        Class<?>[] groups() default { };
    
        Class<? extends Payload>[] payload() default { };
    }
    

    IsMobileValidator.java

    /**
     * 手机号码校验规则
     *
     * @author thenie
     * @since 1.0.0
     */
    public class IsMobileValidator implements ConstraintValidator<IsMobile,String> {
    
        private boolean required = false;
    
        @Override
        public void initialize(IsMobile constraintAnnotation) {
            required = constraintAnnotation.required();
        }
    
        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
            if (required){
                return ValidatorUtil.isMobile(value);
            }else {
                if (StringUtils.isEmpty(value)){
                    return true;
                }else {
                    return ValidatorUtil.isMobile(value);
                }
            }
        }
    }
    

    ValidatorUtil.java

    /**
     * 校验工具类
     *
     * @author thenie
     * @since 1.0.0
     */
    public class ValidatorUtil {
        //电话号码验证的正则表达式
        private static final Pattern mobile_pattern = Pattern.compile("^1[3|4|5|7|8][0-9]{9}$");
    
        public static boolean isMobile(String mobile){
            if (StringUtils.isEmpty(mobile)) {
                return false;
            }
    
            Matcher matcher = mobile_pattern.matcher(mobile);
            return matcher.matches();
        }
    }
    

    最后在我们需要使用的地方,通过 @Valid开启就行。

    在这里插入图片描述

第二部分:全局异常处理

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public RespBean ExceptionHandler(Exception e) {
        //处理异常,友好返回
    }
}

2、根据cookie访问页面,参数中没有user信息,如何实现User自动映射?

这里也是为了降低代码冗余,实现User自动映射

实现思路:添加参数解析器,实现我们的自动注入逻辑

  1. 配置参数解析器

    WebMvcConfig配置类中重写

        /**
         * Controller 配置参数解析器
         * @param argumentResolvers
         */
        @Override
        public void addArgumentResolvers(List argumentResolvers) {
            argumentResolvers.add(new CurrentUserArgumentResolver());
        }
    
  2. 实现参数解析器逻辑

    /**
     * 参数解析器
     */
    @Slf4j
    public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {
    
        @Override
        public boolean supportsParameter(MethodParameter parameter) {
            //如果参数中存在注解CurrentUser
            return parameter.hasParameterAnnotation(CurrentUser.class);
        }
    
        @Override
        public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer modelAndView, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
            //进行
            Object currentUserInfo = webRequest.getAttribute("currentUser", RequestAttributes.SCOPE_REQUEST);
            if(null == currentUserInfo)
                log.warn("---------------用户未登录---------------");
            return currentUserInfo;
        }
    }
    
  3. 定义声明注解

    /**
     * 声明注解,从请求域中注入到参数中
     */
    @Target({ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CurrentUser {
    }
    

3、redis通过RedisTemplate实现分布式锁解决预减库存

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.concurrent.TimeUnit;

@Component
public class LockUtil {

    @Autowired
    private StringRedisTemplate redisTemplate;


    /**
     * 尝试获取锁 立即返回
     *
     * @param key
     * @param value
     * @param timeout
     * @return
     */
    public boolean lock(String key, String value, long timeout) {
        return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS);
    }

    /**
     * 实现原子性递减操作
     * @param key
     * @return
     */
    public Long executeDecrToZero(String key){
        //lua脚本自己定义,实现业务逻辑
        String luaScript=
                "if (redis.call('exists', KEYS[1]) == 1) then\n" +
                    "local stock = tonumber(redis.call('get', KEYS[1]));\n" +
                    "if (stock > 0) then\n" +
                        "redis.call('incrby', KEYS[1], -1);\n" +
                        "return stock;\n" +
                    "end;\n" +
                "return 0;\n" +
                "end;";
        RedisScript<Long> redisScript = new DefaultRedisScript<>(luaScript,Long.class);
        return (Long) 				     redisTemplate.execute(redisScript,Collections.singletonList(key),Collections.EMPTY_LIST);
    }

}

4、application.yml中格式问题

第一次遇到这种问题,我的application.ymlpassword类似为123456Niess这种密码,在本机启动时不会报错,但是我生成jar包部署到服务器上后,启动时会报错,排查了很久,才发现是password的格式错误。

解决方法:有特殊字符或者类似这种格式用引号括起来就好了,例如"123456Niess"

5、Maven中导包爆红

一般我们都会把maven仓库和idea相关的config设置好,然后基本上就不会出现问题。

但是不排除有时候我们将一个下载下来的项目,用idea打开后maven会爆红,不管怎么点击右上方刷新还是会存在

通用的解决方案:

  1. 将出问题的包找到maven仓库目录将相关包删除,然后重新导入
  2. 如果还是爆红,那就把pom.xml里面的内容剪切再粘贴就好

如果你是公司的项目的话,它有的包会依赖于公司私有服务器上,报红很正常,只需要找的mt要一份maven的config设置一下就好。



版权声明:本文为weixin_46804107原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。