• SpringBoot 注解开发


    利用自定义注解,解决问题

    例1 自定义注解限制请求

    场景:前端发起的频繁的请求,导致服务器压力过大。需要对后端接口进行限流处理,每个接口都需要做限流处理的话就会导致代码冗余,此时就可以利用注解进行解决

    非注解形式

    这里是用于 某个用户点击的次数(结合radis)进行判断,也可以用请求的ip进行判断

    核心代码:

    1. //限流,60秒5次
    2. ValueOperations valueOperations = redisTemplate.opsForValue();
    3. String uri = request.getRequestURI();
    4. //captcha = "0"; 这个用于测试
    5. Integer count =(Integer) valueOperations.get(uri+":"+user.getId());
    6. if(count == null){
    7. valueOperations.set(uri+":"+user.getId(),1,60,TimeUnit.SECONDS);
    8. } else if (count<5) {
    9. valueOperations.increment(uri+":"+user.getId());
    10. }else {
    11. return RespBean.error(RespBeanEnum.ACCESS_LIMIT_REAHCED);
    12. }

    完整代码:

    1. @RequestMapping(value = "/path",method = RequestMethod.GET)
    2. @ResponseBody
    3. public RespBean getPath(User user,Long goodsId,String captcha,HttpServletRequest request){
    4. if(user == null){
    5. return RespBean.error(RespBeanEnum.SESSION_ERROR);
    6. }
    7. //限流,60秒5次
    8. ValueOperations valueOperations = redisTemplate.opsForValue();
    9. String uri = request.getRequestURI();
    10. log.info("uri====:{}",uri);
    11. log.info("qequestIp===={}",request.getRemoteAddr());
    12. log.info("LocalIp===={}",request.getLocalAddr());
    13. //captcha = "0";
    14. Integer count =(Integer) valueOperations.get(uri+":"+user.getId());
    15. if(count == null){
    16. valueOperations.set(uri+":"+user.getId(),1,60,TimeUnit.SECONDS);
    17. } else if (count<5) {
    18. valueOperations.increment(uri+":"+user.getId());
    19. }else {
    20. return RespBean.error(RespBeanEnum.ACCESS_LIMIT_REAHCED);
    21. }
    22. boolean check= orderService.checkCaptcha(user,goodsId,captcha);
    23. if(!check){
    24. return RespBean.error(RespBeanEnum.ERROR_CAPTCHA);
    25. }
    26. String str = orderService.createPath(user,goodsId);
    27. return RespBean.success(str);
    28. }

    注解形式

    注解定义方式,以AcessLimit为例

    1. 定义注解  AccessLimit

    2. 创建拦截器 AccessLimitInterceptor

    3. 在WebConfig 中添加拦截器 addInterceptors

    定义注解AccessLimit
    1. @Retention(RetentionPolicy.RUNTIME)
    2. @Target(ElementType.METHOD)
    3. public @interface AccessLimit {
    4. //时间
    5. int second();
    6. //次数
    7. int maxCount();
    8. //这个用于判断是否需要登录,后续可用SpringSecurity + JWT 实现。
    9. boolean needLogin() default true;
    10. }
    创建拦截器 AccessLimitInterceptor

    核心代码:

    1. @Component
    2. public class AccessLimitInterceptor implements HandlerInterceptor {
    3. @Autowired
    4. private RedisTemplate redisTemplate;
    5. @Override
    6. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    7. if(handler instanceof HandlerMethod){
    8. HandlerMethod hm = (HandlerMethod) handler;
    9. AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
    10. if( accessLimit ==null){
    11. return true;
    12. }
    13. int second = accessLimit.second();
    14. int maxCount = accessLimit.maxCount();
    15. boolean needLogin = accessLimit.needLogin();
    16. String key = request.getRequestURI();
    17. ValueOperations valueOperations =redisTemplate.opsForValue();
    18. Integer count = (Integer) valueOperations.get(key);
    19. if(count == null){
    20. valueOperations.set(key,1,second, TimeUnit.SECONDS);
    21. } else if (count
    22. valueOperations.increment(key);
    23. } else {
    24. render(response,RespBeanEnum.ACCESS_LIMIT_REAHCED);
    25. return false;
    26. }
    27. }
    28. return true;
    29. }

    完整代码:

    1. package com.xxx.seckilldemo.annotation;
    2. import com.fasterxml.jackson.databind.ObjectMapper;
    3. import com.xxx.seckilldemo.config.UserContext;
    4. import com.xxx.seckilldemo.pojo.User;
    5. import com.xxx.seckilldemo.service.IUserService;
    6. import com.xxx.seckilldemo.utils.CookieUtil;
    7. import com.xxx.seckilldemo.vo.RespBean;
    8. import com.xxx.seckilldemo.vo.RespBeanEnum;
    9. import org.springframework.beans.factory.annotation.Autowired;
    10. import org.springframework.data.redis.core.RedisTemplate;
    11. import org.springframework.data.redis.core.ValueOperations;
    12. import org.springframework.stereotype.Component;
    13. import org.springframework.util.StringUtils;
    14. import org.springframework.web.method.HandlerMethod;
    15. import org.springframework.web.servlet.HandlerInterceptor;
    16. import javax.servlet.http.HttpServletRequest;
    17. import javax.servlet.http.HttpServletResponse;
    18. import java.io.IOException;
    19. import java.io.ObjectInput;
    20. import java.io.PrintWriter;
    21. import java.util.concurrent.TimeUnit;
    22. /** 接口限流拦截器
    23. * @author Adzc
    24. * @date 2023/11/15 15:40
    25. */
    26. @Component
    27. public class AccessLimitInterceptor implements HandlerInterceptor {
    28. @Autowired
    29. private IUserService userService;
    30. @Autowired
    31. private RedisTemplate redisTemplate;
    32. @Override
    33. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    34. if(handler instanceof HandlerMethod){
    35. User user = getUser(request,response);
    36. UserContext.setUser(user);
    37. HandlerMethod hm = (HandlerMethod) handler;
    38. AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
    39. if( accessLimit ==null){
    40. return true;
    41. }
    42. int second = accessLimit.second();
    43. int maxCount = accessLimit.maxCount();
    44. //此处是登录相关
    45. boolean needLogin = accessLimit.needLogin();
    46. String key = request.getRequestURI();
    47. if(needLogin){
    48. if (user == null){
    49. render(response, RespBeanEnum.SESSION_ERROR);
    50. return false;
    51. }
    52. key+=":"+user.getId();
    53. }
    54. //在有效时间内,次数的判断
    55. ValueOperations valueOperations =redisTemplate.opsForValue();
    56. Integer count = (Integer) valueOperations.get(key);
    57. if(count == null){
    58. valueOperations.set(key,1,second, TimeUnit.SECONDS);
    59. } else if (count
    60. valueOperations.increment(key);
    61. } else {
    62. //返回对象的定义
    63. render(response,RespBeanEnum.ACCESS_LIMIT_REAHCED);
    64. return false;
    65. }
    66. }
    67. return true;
    68. }
    69. /**
    70. * 获取当前用户
    71. * @param request
    72. * @param response
    73. * @return
    74. */
    75. private User getUser(HttpServletRequest request,HttpServletResponse response){
    76. String ticket = CookieUtil.getCookieValue(request,"userTicket");
    77. if (StringUtils.isEmpty(ticket)){
    78. System.out.println("没有获取到用户");
    79. return null;
    80. }
    81. return userService.getUserByCookie(ticket,request,response);
    82. }
    83. /**
    84. * 构建返回对象
    85. * @param response
    86. * @param respBeanEnum
    87. */
    88. private void render(HttpServletResponse response,RespBeanEnum respBeanEnum) throws IOException {
    89. response.setContentType("application/json");
    90. response.setCharacterEncoding("utf-8");
    91. PrintWriter out = response.getWriter();
    92. RespBean respBean = RespBean.error(respBeanEnum);
    93. out.write(new ObjectMapper().writeValueAsString(respBean));
    94. out.flush();
    95. out.close();
    96. }
    97. }

    返回对象用到了枚举

    1. /**
    2. * 公共返回对象
    3. * @author Adzc
    4. * @date 2023/9/25 23:19
    5. */
    6. @Data
    7. @AllArgsConstructor
    8. @NoArgsConstructor
    9. @ToString
    10. public class RespBean {
    11. private long code;
    12. private String message;
    13. private Object obj;
    14. /**
    15. * 成功返回结果
    16. * @return
    17. */
    18. public static RespBean success(){
    19. return new RespBean(RespBeanEnum.SUCCESS.getCode(), RespBeanEnum.SUCCESS.getMessage(),null);
    20. }
    21. public static RespBean success(Object obj){
    22. return new RespBean(RespBeanEnum.SUCCESS.getCode(), RespBeanEnum.SUCCESS.getMessage(),obj);
    23. }
    24. /**
    25. * 失败返回结果
    26. * @param respBeanEnum
    27. * @return
    28. */
    29. public static RespBean error(RespBeanEnum respBeanEnum){
    30. return new RespBean(respBeanEnum.getCode(), respBeanEnum.getMessage(),null);
    31. }
    32. public static RespBean error(RespBeanEnum respBeanEnum,Object obj){
    33. return new RespBean(respBeanEnum.getCode(), respBeanEnum.getMessage(),obj);
    34. }
    35. }
    1. @Getter
    2. @ToString
    3. @AllArgsConstructor
    4. public enum RespBeanEnum {
    5. SUCCESS(200,"SUCCESS"),
    6. ERROR(500,"服务端异常"),
    7. //登录模块
    8. LOGIN_ERROR(500210,"用户名或密码不正确"),
    9. MOBILE_ERROR(500211,"非法手机号"),
    10. BIND_ERROR(500212,"参数校验异常"),
    11. MOBILE_NOT_EXIST(500213, "手机号不存在"),
    12. PASSWORD_UPDATE_FAIL(500214,"密码更新失败"),
    13. SESSION_ERROR(500215,"用户不存在"),
    14. ACCESS_LIMIT_REAHCED(500504,"访问过于频繁请稍后在尝试");
    15. private final Integer code;
    16. private final String message;
    17. }
    在WebConfig 中添加拦截器 addInterceptors

    核心代码

    1. @Override
    2. public void addInterceptors(InterceptorRegistry registry) {
    3. registry.addInterceptor(accessLimitInterceptor);
    4. }
    5. }

    完整代码

    1. @Configuration
    2. @EnableWebMvc
    3. public class WebConfig implements WebMvcConfigurer {
    4. @Autowired
    5. private AccessLimitInterceptor accessLimitInterceptor;
    6. @Override
    7. public void addInterceptors(InterceptorRegistry registry) {
    8. registry.addInterceptor(accessLimitInterceptor);
    9. }
    10. }

    例2 利用@JsonSerialize注解+自定义类进行手机号脱敏处理

    场景:很多手机号之类的信息需要在前端页面需要进行脱敏处理

    1. public class PhoneJsonSerializer extends JsonSerializer {
    2. @Override
    3. public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    4. if (value != null && value instanceof String){
    5. if (!Objects.equals(value,"") && value.toString().length() > 5){
    6. String phone = value.toString();
    7. phone = phone.substring(0, 3) + "******" + phone.substring(phone.length() - 2);
    8. gen.writeString(phone);
    9. } else {
    10. gen.writeObject(value);
    11. }
    12. } else {
    13. gen.writeObject(value);
    14. }
    15. }
    16. }

    在返回的对象中使用

    1. @JsonSerialize(using = PhoneJsonSerializer.class)
    2. private String phone;

  • 相关阅读:
    有什么可替代问卷星的好用的问卷工具?
    2022年9月19日--9月25日(ue4热更新视频教程为主,)
    PeLK:101 x 101 的超大卷积网络,同参数量下反超 ViT | CVPR 2024
    通过 SingleFlight 模式学习 Go 并发编程
    防火墙技术:一种保护网络安全的安全模型及其编程实现
    【无标题】
    L61.linux命令每日一练 -- 第九章 Linux进程管理命令 -- renice和nohup
    基于NIFI工具搭建生产级别的IIOT系统
    P1006 [NOIP2008 提高组] 传纸条,棋盘型dp,路径dp
    使用 AJAX 提升网页数据的动态交互
  • 原文地址:https://blog.csdn.net/weixin_53630942/article/details/134496894