最近完成了一个后端项目,过程中遇到了一些难点和第一次遇到的BUG
分享一下,做个记录 ~~
文章目录
1、通过注解判断参数vo是否为空,并做全局异常处理
在开发中,controller层需要经常对vo参数进行格式判断,这样就可能在多个请求方法中出现格式判断,所以为了降低代码冗余,通过在注解来进行格式判断。
实现思路:添加依赖,在需要进行格式判断的类中添加判断注解,如果我们需要实现一些自己的判断,可以实现自定义注解。这样基本就已经实现完了,但是。还要考虑的就是,如果入参的格式不符合我们的要求,validation
就会抛出异常,为了友好的返回结果,我们就会做全局异常处理。
第一部分:格式验证
添加依赖
<!--validation验证依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
在入参时需要进行格式判断的类中添加判断注解
//我这里以一个模板入参类为样例(参考) //其中@IsMobile为自定义注解 @Data public class LoginVo { @NotNull //不能为空 @IsMobile //mobile格式判断(我们的自定义注解) private String mobile; @NotNull @Length(min = 32) //长度要求 private String password; }
实现自定义注解并注册到
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
自动映射
实现思路:添加参数解析器,实现我们的自动注入逻辑
配置参数解析器
WebMvcConfig配置类中重写
/** * Controller 配置参数解析器 * @param argumentResolvers */ @Override public void addArgumentResolvers(List argumentResolvers) { argumentResolvers.add(new CurrentUserArgumentResolver()); }
实现参数解析器逻辑
/** * 参数解析器 */ @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; } }
定义声明注解
/** * 声明注解,从请求域中注入到参数中 */ @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.yml
中password
类似为123456Niess
这种密码,在本机启动时不会报错,但是我生成jar
包部署到服务器上后,启动时会报错,排查了很久,才发现是password
的格式错误。
解决方法:有特殊字符或者类似这种格式用引号括起来就好了,例如"123456Niess"
5、Maven中导包爆红
一般我们都会把maven仓库和idea相关的config设置好,然后基本上就不会出现问题。
但是不排除有时候我们将一个下载下来的项目,用idea
打开后maven
会爆红,不管怎么点击右上方刷新还是会存在
通用的解决方案:
- 将出问题的包找到
maven
仓库目录将相关包删除,然后重新导入 - 如果还是爆红,那就把
pom.xml
里面的内容剪切再粘贴就好
如果你是公司的项目的话,它有的包会依赖于公司私有服务器上,报红很正常,只需要找的mt要一份maven的config设置一下就好。