Springboot Jwt

安装JWT依赖 1 2 3 4 5 <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>4.5.0</version> </dependency> 定义Jwt工具类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 package com.example.demo.utils; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.exceptions.SignatureVerificationException; import com.auth0.jwt.exceptions.TokenExpiredException; import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.JWTVerifier; import org.springframework.stereotype.Component; import java.util.Collections; import java.util.Date; import java.util.Map; @Component public class JwtUtils { private static final String SECRET_KEY = "Pxiv0oevyL8rT3an11QKL208vYlkvAmt"; private static final String ISSUER = "admin_service"; private static final long EXPIRATION_TIME = 3600000; public static String generateToken(Map<String, Object> claims) { // 定义密钥 Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); // 获取当前时间 Date now = new Date(); // 设置过期时间 Date expiration = new Date(now.getTime() + EXPIRATION_TIME); // 构建JWT并添加所有自定义claim com.auth0.jwt.JWTCreator.Builder tokenBuilder = JWT.create() .withIssuer(ISSUER) .withIssuedAt(now) .withExpiresAt(expiration); // 遍历添加每个自定义claim if (claims != null && !claims.isEmpty()) { for (Map.Entry<String, Object> entry : claims.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); // 根据值类型选择对应的 withClaim 方法重载 if (value instanceof String) { tokenBuilder.withClaim(key, (String) value); } else if (value instanceof Integer) { tokenBuilder.withClaim(key, (Integer) value); } else if (value instanceof Long) { tokenBuilder.withClaim(key, (Long) value); } else if (value instanceof Boolean) { tokenBuilder.withClaim(key, (Boolean) value); } else if (value instanceof Double) { tokenBuilder.withClaim(key, (Double) value); } else if (value instanceof Float) { tokenBuilder.withClaim(key, Collections.singletonList((Float) value)); } else if (value instanceof Short) { tokenBuilder.withClaim(key, Collections.singletonList((Short) value)); } else if (value instanceof Byte) { tokenBuilder.withClaim(key, Collections.singletonList((Byte) value)); } else if (value instanceof Enum<?>) { tokenBuilder.withClaim(key, (Map<String, ?>) value); } } } return tokenBuilder.sign(algorithm); } public static Object validateToken(String token) { try { Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); JWTVerifier verifier = JWT.require(algorithm) .withIssuer(ISSUER) .build(); DecodedJWT jwt = verifier.verify(token); return "success"; } catch (SignatureVerificationException e) { return "签名验证失败"; } catch (TokenExpiredException e) { return "Token已过期"; } catch (JWTVerificationException e) { return e.getLocalizedMessage(); } } } 定义JwtInterceptor 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.example.demo.config; import cn.hutool.json.JSONUtil; import com.example.demo.utils.JwtUtils; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); if (token == null || "".equals(token)) { response.setCharacterEncoding("UTF-8"); response.getWriter().write(JSONUtil.toJsonStr("未登录")); return false; } Object result = JwtUtils.validateToken(token); if ("success".equals(result)) { return true; } else { response.setCharacterEncoding("UTF-8"); response.getWriter().write(JSONUtil.toJsonStr(result)); return false; } } } 配置拦截器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.example.demo.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebAppConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new JwtInterceptor()) .addPathPatterns("/**") // 拦截所有请求,可根据实际需求调整路径模式 .excludePathPatterns("/login"); // 排除登录接口 } } 使用JWT生成Token 1 2 3 4 5 6 7 Map<String, Object> claims = new HashMap<>(); claims.put("userId", userId); claims.put("username", username); // 其他自定义claim... String token = JwtUtils.generateToken(claims); return JSONUtil.toJsonStr(token); 这样你就完成了Spring Boot项目中JWT的集成和使用。记得根据你的项目实际情况对代码进行适当修改和扩展。 ...

2025-04-30 · 3 min · 525 words

Springboot Response

1. 创建HTTP状态码枚举 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 package com.example.demo.utils; import lombok.Getter; @Getter public enum HttpStatusEnum { /** * 操作成功 */ SUCCESS(200, "操作成功"), /** * 对象创建成功 */ CREATED(201, "对象创建成功"), /** * 请求已经被接受 */ ACCEPTED(202, "请求已经被接受"), /** * 操作已经执行成功,但是没有返回数据 */ NO_CONTENT(204, "操作已经执行成功,但是没有返回数据"), /** * 资源已被移除 */ MOVED_PERM(301, "资源已被移除"), /** * 重定向 */ SEE_OTHER(303, "重定向"), /** * 资源没有被修改 */ NOT_MODIFIED(304, "资源没有被修改"), /** * 参数列表错误(缺少,格式不匹配) */ BAD_REQUEST(400, "参数列表错误(缺少,格式不匹配)"), /** * 未授权 */ UNAUTHORIZED(401, "未授权"), /** * 访问受限,授权过期 */ FORBIDDEN(403, "访问受限,授权过期"), /** * 资源,服务未找到 */ NOT_FOUND(404, "资源,服务未找!"), /** * 不允许的http方法 */ BAD_METHOD(405, "不允许的http方法"), /** * 资源冲突,或者资源被锁 */ CONFLICT(409, "资源冲突,或者资源被锁"), /** * 不支持的数据,媒体类型 */ UNSUPPORTED_TYPE(415, "不支持的数据,媒体类型"), /** * 系统内部错误 */ ERROR(500, "系统内部错误"), /** * 接口未实现 */ NOT_IMPLEMENTED(501, "接口未实现"), /** * 系统警告消息 */ WARN(601,"系统警告消息"); private final Integer code; private final String message; HttpStatusEnum(Integer code, String message) { this.code = code; this.message = message; } } 2. 创建通用返回类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 package com.example.demo.utils; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class ResponseResult<T> { /*状态码*/ private Integer code; /*状态信息*/ private Boolean status; /*返回信息*/ private String message; /*返回数据*/ private T data; /** * 全参数方法 * * @param code 状态码 * @param status 状态 * @param message 返回信息 * @param data 返回数据 * @param <T> 泛型 * @return {@link ResponseResult<T>} */ private static <T> ResponseResult<T> response(Integer code, Boolean status, String message, T data) { ResponseResult<T> responseResult = new ResponseResult<>(); responseResult.setCode(code); responseResult.setStatus(status); responseResult.setMessage(message); responseResult.setData(data); return responseResult; } /** * 全参数方法 * * @param code 状态码 * @param status 状态 * @param message 返回信息 * @param <T> 泛型 * @return {@link ResponseResult<T>} */ private static <T> ResponseResult<T> response(Integer code, Boolean status, String message) { ResponseResult<T> responseResult = new ResponseResult<>(); responseResult.setCode(code); responseResult.setStatus(status); responseResult.setMessage(message); return responseResult; } /** * 成功返回(无参) * * @param <T> 泛型 * @return {@link ResponseResult<T>} */ public static <T> ResponseResult<T> success() { return response(HttpStatusEnum.SUCCESS.getCode(), true, HttpStatusEnum.SUCCESS.getMessage(), null); } /** * 成功返回(枚举参数) * * @param httpResponseEnum 枚举参数 * @param <T> 泛型 * @return {@link ResponseResult<T>} */ public static <T> ResponseResult<T> success(HttpStatusEnum httpResponseEnum) { return response(httpResponseEnum.getCode(), true, httpResponseEnum.getMessage()); } /** * 成功返回(状态码+返回信息) * * @param code 状态码 * @param message 返回信息 * @param <T> 泛型 * @return {@link ResponseResult<T>} */ public static <T> ResponseResult<T> success(Integer code, String message) { return response(code, true, message); } /** * 成功返回(返回信息 + 数据) * * @param message 返回信息 * @param data 数据 * @param <T> 泛型 * @return {@link ResponseResult<T>} */ public static <T> ResponseResult<T> success(String message, T data) { return response(HttpStatusEnum.SUCCESS.getCode(), true, message, data); } /** * 成功返回(状态码+返回信息+数据) * * @param code 状态码 * @param message 返回信息 * @param data 数据 * @param <T> 泛型 * @return {@link ResponseResult<T>} */ public static <T> ResponseResult<T> success(Integer code, String message, T data) { return response(code, true, message, data); } /** * 成功返回(数据) * * @param data 数据 * @param <T> 泛型 * @return {@link ResponseResult<T>} */ public static <T> ResponseResult<T> success(T data) { return response(HttpStatusEnum.SUCCESS.getCode(), true, HttpStatusEnum.SUCCESS.getMessage(), data); } /** * 成功返回(返回信息) * * @param message 返回信息 * @param <T> 泛型 * @return {@link ResponseResult<T>} */ public static <T> ResponseResult<T> success(String message) { return response(HttpStatusEnum.SUCCESS.getCode(), true, message, null); } /** * 失败返回(无参) * * @param <T> 泛型 * @return {@link ResponseResult<T>} */ public static <T> ResponseResult<T> fail() { return response(HttpStatusEnum.ERROR.getCode(), false, HttpStatusEnum.ERROR.getMessage(), null); } /** * 失败返回(枚举) * * @param httpResponseEnum 枚举 * @param <T> 泛型 * @return {@link ResponseResult<T>} */ public static <T> ResponseResult<T> fail(HttpStatusEnum httpResponseEnum) { return response(httpResponseEnum.getCode(), false, httpResponseEnum.getMessage()); } /** * 失败返回(状态码+返回信息) * * @param code 状态码 * @param message 返回信息 * @param <T> 泛型 * @return {@link ResponseResult<T>} */ public static <T> ResponseResult<T> fail(Integer code, String message) { return response(code, false, message); } /** * 失败返回(返回信息+数据) * * @param message 返回信息 * @param data 数据 * @param <T> 泛型 * @return {@link ResponseResult<T>} */ public static <T> ResponseResult<T> fail(String message, T data) { return response(HttpStatusEnum.ERROR.getCode(), false, message, data); } /** * 失败返回(状态码+返回信息+数据) * * @param code 状态码 * @param message 返回消息 * @param data 数据 * @param <T> 泛型 * @return {@link ResponseResult<T>} */ public static <T> ResponseResult<T> fail(Integer code, String message, T data) { return response(code, false, message, data); } /** * 失败返回(数据) * * @param data 数据 * @param <T> 泛型 * @return {@link ResponseResult<T>} */ public static <T> ResponseResult<T> fail(T data) { return response(HttpStatusEnum.ERROR.getCode(), false, HttpStatusEnum.ERROR.getMessage(), data); } /** * 失败返回(返回信息) * * @param message 返回信息 * @param <T> 泛型 * @return {@link ResponseResult<T>} */ public static <T> ResponseResult<T> fail(String message) { return response(HttpStatusEnum.ERROR.getCode(), false, message, null); } } 3. 使用示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.example.demo.controller; import com.example.demo.utils.ResponseResult; @RequestMapping("") public class Hello { @GetMapping("/success") public ResponseResult<String> success() { return ResponseResult.success("操作成功"); } @GetMapping("/fail") public ResponseResult<String> fail() { return ResponseResult.fail(HttpStatusEnum.PARAM_ERROR); } }

2025-04-30 · 6 min · 1101 words

Springboot使用Sentinel实现流量控制与熔断降级

1、安装Sentinel 1 2 3 4 5 <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-core</artifactId> <version>1.8.8</version> </dependency>

2025-04-30 · 1 min · 11 words

Springboot上传图片

在 Spring Boot 3 中,要实现限制上传图片尺寸、格式以及对图片进行裁剪压缩处理,可以按照以下步骤进行。下面是一个完整的示例代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; @RestController public class ImageUploadController { private static final String UPLOAD_DIR = "uploads"; private static final String[] ALLOWED_TYPES = {"image/jpeg", "image/png", "image/gif"}; private static final int MAX_WIDTH = 1920; private static final int MAX_HEIGHT = 1080; private static final int TARGET_WIDTH = 800; private static final int TARGET_HEIGHT = 600; @PostMapping("/upload") public ResponseEntity<String> uploadImage(@RequestParam("image") MultipartFile file) { if (file.isEmpty()) { return new ResponseEntity<>("请选择要上传的图片", HttpStatus.BAD_REQUEST); } // 检查文件类型 String contentType = file.getContentType(); boolean isAllowed = false; for (String allowedType : ALLOWED_TYPES) { if (allowedType.equals(contentType)) { isAllowed = true; break; } } if (!isAllowed) { return new ResponseEntity<>("不支持的文件类型,仅支持 JPEG、PNG 和 GIF 格式", HttpStatus.BAD_REQUEST); } try { BufferedImage image = ImageIO.read(file.getInputStream()); int width = image.getWidth(); int height = image.getHeight(); // 检查图片尺寸 if (width > MAX_WIDTH || height > MAX_HEIGHT) { return new ResponseEntity<>("图片尺寸过大,最大宽度为 " + MAX_WIDTH + ",最大高度为 " + MAX_HEIGHT, HttpStatus.BAD_REQUEST); } // 裁剪和压缩图片 BufferedImage resizedImage = resizeImage(image, TARGET_WIDTH, TARGET_HEIGHT); // 创建上传目录(如果不存在) File uploadDir = new File(UPLOAD_DIR); if (!uploadDir.exists()) { uploadDir.mkdirs(); } // 获取文件名 String fileName = file.getOriginalFilename(); Path filePath = Paths.get(UPLOAD_DIR, fileName); // 保存处理后的图片 String format = contentType.split("/")[1]; ImageIO.write(resizedImage, format, filePath.toFile()); return new ResponseEntity<>("图片上传并处理成功", HttpStatus.OK); } catch (IOException e) { e.printStackTrace(); return new ResponseEntity<>("图片上传失败", HttpStatus.INTERNAL_SERVER_ERROR); } } private BufferedImage resizeImage(BufferedImage originalImage, int targetWidth, int targetHeight) { BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB); Graphics2D graphics2D = resizedImage.createGraphics(); graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); graphics2D.drawImage(originalImage, 0, 0, targetWidth, targetHeight, null); graphics2D.dispose(); return resizedImage; } } 代码说明 文件类型限制: ALLOWED_TYPES 数组定义了允许上传的图片格式,在 uploadImage 方法中,通过 file.getContentType() 获取文件的 MIME 类型,并与 ALLOWED_TYPES 进行比较,若不匹配则返回错误信息。 图片尺寸限制: MAX_WIDTH 和 MAX_HEIGHT 定义了允许上传的最大图片尺寸。在 uploadImage 方法中,使用 ImageIO.read 读取图片文件为 BufferedImage 对象,然后获取其宽度和高度,若超过最大尺寸则返回错误信息。 图片裁剪压缩处理: TARGET_WIDTH 和 TARGET_HEIGHT 定义了处理后图片的目标尺寸。resizeImage 方法用于将原始图片按照目标尺寸进行裁剪和压缩,使用 Graphics2D 进行绘制,最后返回处理后的 BufferedImage 对象。 在 uploadImage 方法中,调用 resizeImage 方法对图片进行处理,然后使用 ImageIO.write 将处理后的图片保存到指定目录。

2025-04-25 · 2 min · 393 words

Springboot使用Redisson分布式锁

在 Spring Boot 项目里,运用 Redisson 实现分布式锁来处理销售扣减库存和入库增加库存,且同时操作一个库存值,可按以下步骤完成: 1、添加依赖 在pom.xml文件里添加 Redisson 和 Spring Boot Data Redis 的依赖: 1 2 3 4 5 6 7 8 9 10 11 12 13 <dependencies> <!-- Redisson --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.16.2</version> </dependency> <!-- Spring Boot Data Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies> 2、配置 Redisson 在application.yml文件里配置 Redis: 1 2 3 4 spring: redis: host: localhost port: 6379 3、在service中使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; @Service public class InventoryService { @Autowired private RedissonClient redissonClient; // 库存键名 private static final String INVENTORY_KEY = "product:inventory:1"; /** * 销售扣减库存 * @param quantity 扣减数量 * @return 是否成功 */ public boolean deductInventory(int quantity) { // 获取分布式锁 RLock lock = redissonClient.getLock(INVENTORY_KEY); try { // 尝试加锁,等待10秒,自动释放锁时间为30秒 if (lock.tryLock(10, 30, TimeUnit.SECONDS)) { // 模拟从Redis获取当前库存 Integer currentInventory = getInventoryFromRedis(); if (currentInventory != null && currentInventory >= quantity) { // 扣减库存 int newInventory = currentInventory - quantity; // 更新Redis中的库存 updateInventoryInRedis(newInventory); return true; } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // 释放锁 if (lock.isHeldByCurrentThread()) { lock.unlock(); } } return false; } /** * 入库增加库存 * @param quantity 增加数量 */ public void addInventory(int quantity) { // 获取分布式锁 RLock lock = redissonClient.getLock(INVENTORY_KEY); try { // 尝试加锁,等待10秒,自动释放锁时间为30秒 if (lock.tryLock(10, 30, TimeUnit.SECONDS)) { // 模拟从Redis获取当前库存 Integer currentInventory = getInventoryFromRedis(); if (currentInventory != null) { // 增加库存 int newInventory = currentInventory + quantity; // 更新Redis中的库存 updateInventoryInRedis(newInventory); } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { // 释放锁 if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } /** * 从Redis获取当前库存 * @return 当前库存 */ private Integer getInventoryFromRedis() { // 这里需要根据实际情况实现从Redis获取库存的逻辑 return 100; } /** * 更新Redis中的库存 * @param newInventory 新的库存数量 */ private void updateInventoryInRedis(int newInventory) { // 这里需要根据实际情况实现更新Redis库存的逻辑 } } 代码解释 ...

2025-04-25 · 3 min · 446 words

Spring boot 限流

在 Spring Boot 3 中实现限流可以采用多种方式,下面为你介绍常见的三种: 1、使用 Sentinel 实现限流 Sentinel 是阿里巴巴开源的一款流量控制组件,具备实时监控、限流、熔断等功能。 步骤 添加依赖:在pom.xml里添加 Sentinel 的依赖。 1 2 3 4 5 <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-spring-boot-starter</artifactId> <version>1.8.6</version> </dependency> 配置规则:创建一个配置类来设置限流规则。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; @Configuration public class SentinelConfig { @PostConstruct public void initFlowRules() { List<FlowRule> rules = new ArrayList<>(); FlowRule rule = new FlowRule(); rule.setResource("yourResourceName"); rule.setCount(10); // 每秒最多允许10个请求 rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setLimitApp("default"); rules.add(rule); FlowRuleManager.loadRules(rules); } } 添加注解:在需要限流的方法上添加@SentinelResource注解。 ...

2025-04-25 · 2 min · 369 words

Springboot:Lombok

简介 Lombok 是一个 Java 库,它可以自动插入编辑器和构建工具中,简化 Java 代码的编写。 Lombok 提供了一系列注解,用于自动生成 getter、setter、构造函数、toString 方法等,从而减少了代码量,提高了代码的可读性和可维护性。 安装 1 2 3 4 5 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.28</version> </dependency> 常用注解 注解名 作用 @Data 自动生成 getter、setter、toString、equals、hashCode 方法 @Getter 自动生成 getter 方法 @Setter 自动生成 setter 方法 @ToString 自动生成 toString 方法 @EqualsAndHashCode 自动生成 equals 和 hashCode 方法 @NoArgsConstructor 自动生成无参构造函数 @AllArgsConstructor 自动生成全参构造函数 全部注解 @Data @Data注解是一个非常强大的综合注解,它等价于同时使用@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstructor。使用@Data可以大大简化代码,减少样板代码的编写。 1 2 3 4 5 6 import lombok.Data; @Data public class Person { private String name; private int age; } @Getter和@Setter 1. 放置位置的灵活性 ...

2025-03-12 · 8 min · 1640 words

Springboot:输出嵌套数据

实体类 1 2 3 4 5 6 7 8 9 10 11 12 @Getter @Setter @AllArgsConstructor @NoArgsConstructor @Entity @TableName("t_article") public class Article { private Long id; private String title; private String content; private Long parentId; } DTO类 1 2 3 4 5 6 7 @Data public class ArticleDTO { private Long id; private String title; private String content; private List<ArticleDTO> children;//这是重点 } Service类 1 2 3 public interface ArticleService { List<ArticleDTO> getArticleList(); } ServiceImpl类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 @Service public class ArticleServiceImpl implements ArticleService { @Autowired private ArticleMapper articleMapper; @Override public List<ArticleDTO> getArticleList() { // 查询所有文章 List<Article> articles = ArticleRepository.searchQuery(); Map<String, ArticleResDto> articleMap = new HashMap<>(); List<ArticleResDto> rootArticles = new ArrayList<>(); // 遍历所有文章,将其转换为ArticleResDto对象,并存储在articleMap中 // 这一步很重要,否则下一步for循环可能无法找到父节点导致数据丢失 for (Article article : articles) { ArticleResDto articleResDto = new ArticleResDto(); //将文章属性复制到文章DTO对象中对应属性中(两边属性字段必须相同,不相同的可以单独通过set方法赋值) BeanUtils.copyProperties(article, articleResDto); articleMap.put(article.getCode(), articleResDto); } // 遍历所有文章,将其转换为ArticleResDto对象,并存储在rootArticles中 for (Article article : articles) { // 从articleMap中获取对应的ArticleResDto对象 ArticleResDto articleResDto = articleMap.get(article.getCode()); // 如果该文章的parent为0,则将其作为根节点存储在rootArticles中 if (article.getParent().equals("0")) { rootArticles.add(articleResDto); } else { // 如果该文章的parent不为0,则将其作为子节点存储在对应的父节点的children中 ArticleResDto parentarticle = articleMap.get(article.getParent()); if (parentarticle != null) { // 如果父节点的children为null,则创建一个新的List对象 if (parentarticle.getChildren() == null) { parentarticle.setChildren(new ArrayList<>()); } parentarticle.getChildren().add(articleResDto); } } } return rootArticles; } // 返回顶级文章列表 /** * 输出结果: * [ * { * "id": 1, * "title": "文章1", * "content": "文章1的内容", * "children": [ * { * "id": 2, * "title": "文章2", * "content": "文章2的内容", * "children": [] * } * ] * } * ] */ return articleDTOs; } BeanUtils.copyProperties(article, articleDTO);将文章属性复制到文章DTO对象中对应属性中 ...

2025-03-11 · 2 min · 298 words

Spring Data JPA

第一步: maven中添加JPA依赖 1 2 3 4 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> 第二步(可选): 安装IDEA插件 JPA Buddy,可以自动生成Entity实体类和Repository仓库接口 第三步: 创建数据表,比如: 1 2 3 4 5 6 7 8 CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `age` int(11) DEFAULT NULL, `created_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; 第四步: 创建数据表对应实体类 项目下边创建entity软件包(文件夹) 方法1(推荐):通过JPA Buddy插件创建,在entity上右键→新建→来自数据库的JPA实体,下边选择对应的数据表和字段,点击确认,就可以自动在entity下边创建对应的实体类了 ...

2025-03-10 · 12 min · 2400 words

Springboot:Redis

摘要 Redis 主要支持以下几种数据类型: string(字符串): 基本的数据存储单元,可以存储字符串、整数或者浮点数。 hash(哈希):一个键值对集合,可以存储多个字段。 list(列表):一个简单的列表,可以存储一系列的字符串元素。 set(集合):一个无序集合,可以存储不重复的字符串元素。 zset(sorted set:有序集合): 类似于集合,但是每个元素都有一个分数(score)与之关联。 位图(Bitmaps):基于字符串类型,可以对每个位进行操作。 超日志(HyperLogLogs):用于基数统计,可以估算集合中的唯一元素数量。 地理空间(Geospatial):用于存储地理位置信息。 发布/订阅(Pub/Sub):一种消息通信模式,允许客户端订阅消息通道,并接收发布到该通道的消息。 流(Streams):用于消息队列和日志存储,支持消息的持久化和时间排序。 模块(Modules):Redis 支持动态加载模块,可以扩展 Redis 的功能。 String(字符串) string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据,比如jpg图片或者序列化的对象。string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。 key value name “zhangsan” ege “15” 常用命令: SET key value:设置键的值,重复设置会覆盖原来的值。 GET key:获取键的值。 INCR key:将键的值加 1。 DECR key:将键的值减 1。 APPEND key value:将值追加到键的值之后。 Hash(哈希) hash 类型是一个键值对集合,是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。比如存储用户的 id、姓名、年龄、性别等信息。文章的 id、标题、内容、作者等信息等。每个哈希最多可以存储 2^32 - 1 个键值对。 key field value user1 name “zhangsan” ege “15” 常用命令: ...

2025-03-06 · 9 min · 1913 words