搭建SpringBoot多模块微服务项目脚手架(三)
搭建SpringBoot多模块微服务项目脚手架(三)
文章目录
- 搭建SpringBoot多模块微服务项目脚手架(三)
- 1.概述
- 项目结构
- 2.接口返回统一信息模板
- 2.1.封装返回统一信息思路介绍
- 2.2.封装json数据格式
- 1.导入依赖
- 2.封装code码
- 3.封装json格式模板
- 4.使用统一返回信息
- 3.接口统一请求信息模板
- 3.1.封装统一请求信息
- 1.导入依赖包
- 2.封装请求信息类
- 3.使用统一请求模板
- 4.统一异常信息模板
- 4.1.封装全局异常类
- 1.导入依赖
- 2.创建全局类封装异常
- 3.使用统一异常处理信息
- 4.2.封装特定异常
- 4.3.封装自定义异常信息
- 自定义异常信息分三个步骤
- 1.定义自定义异常信息属性结构
- 2.自定义异常处理返回的信息
- 3.使用自定义异常类返回自定义信息
- 5.封装统一日志模板
- 5.1.统一日志信息设置
- 1.封装日志格式
- 2.设置错误日志输出到文件
- 3.异常栈信息输出到错误日志文件
- 4.禁用mybatis日志
- 6.验收脚手架
- 6.1.验收Swagger
- 6.2.验收MybatisPlus
- 1.实体类添加MybatisPlus注解
- 2. 新增业务验证ID自增长、创建时间和修改时间自动填充功能
- 3.分页查询
- 4.逻辑删除
- 6.3.验收统一请求和返回信息模块
- 6.4.验收异常
- 1.特定异常
- 2.自定义异常
- 6.5.验收日志
- 7.总结
- 福利
1.概述
搭建SpringBoot多模块微服务项目脚手架 第三篇文章,在搭建本篇文章内容前,先查看
搭建SpringBoot多模块微服务项目脚手架一)
搭建SpringBoot多模块微服务项目脚手架二)
搭建基础环境。
第一篇文章
https://blog.csdn.net/m0_38039437/article/details/129408124
搭建SpringBoot多模块微服务框架
第二篇文章
https://blog.csdn.net/m0_38039437/article/details/129419467
封装《MybatisPlus》
封装《Swagger》
封装《自动生成代码工具》
第三篇文章
封装《接口返回统一信息模板》
封装《接口统一请求信息模板》
封装《统一异常信息模板》
封装《统一日志模板》
项目结构

2.接口返回统一信息模板
2.1.封装返回统一信息思路介绍
我们返回的数据使用Json格式,返回的内容包含两个部分固定内容和可变内容,根据这个需求介绍下设计一个返回数据的模板思路。
1.首先设计json数据格式结构,例如下面的结构包含
- success:是否成功
- code:状态码
- message:提示信息
- data:返回数据
2.设计json结构实现思路,下面会用实例介绍
{"success": true,"code": 20000,"message": "成功","data": {"items": [{"id": "1","name": "刘德华","intro": "毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余"}]}
}
2.2.封装json数据格式
在common公共模块下common_utils 模块中封装返回数据json格式,因为它是一个公共的功能并且不依赖业务,封装好之后在每个项目中都能够使用。
1.导入依赖
封装返回统一信息类中需要使用lombok提供get和set方法,因此我们在common模块中导入依赖。
<!--lombok-->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>
2.封装code码
状态码是一个常量值,因此设计状态码使用枚举类和接口两种方式都可以,这里使用接口设计状态码
- 在
common_utils模块的java文件夹下新建包名com.bruce.commonutils - 在
com.bruce.commonutils包下新建ResultCode接口设计状态码
这里只列举了几个状态码,在开发过程中会根据业务定义很多个失败的状态码,通过不同的状态码确认是哪个业务模块抛出的问题。
package com.bruce.commonutils;/*** @author bruce* @create 2023/3/9 18:58*/
public interface ResultCode {//返回状态成功public static Integer SUCCESS = 200;//返回用户模块没有登录权限状态码public static Integer ERROR = 10000;//返回用户模块没有登录权限状态码public static Integer ERROR_USER_LOGIN = 10000;//返回用户模块没有注册异常状态码public static Integer ERROR_USER_REGISTER = 10001;//返回课程模块没有购买课程状态码public static Integer ERROR_COURSE_PAY = 20000;
}
3.封装json格式模板
在commonutils包下新建R类设计统一返回结果信息
- json格式中每个字段都将他们定义为类的属性,然后构造一个JavaBean类,通过get和set方法赋值和取值。
- 创建几个链式方法,向调用者提供方法操作属性的值,实现统一返回信息格式内容。
package com.bruce.commonutils;/*** @author bruce* @create 2023/3/9 19:11*/import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.util.HashMap;
import java.util.Map;//lombok为属性提供get和set方法
@Data
public class R {//将json格式中的字段定义为类属性//Swagger注解,如果没有集成Swagger可以不用,建议使用@ApiModelProperty(value = "是否成功")private Boolean success;@ApiModelProperty(value = "返回码")private Integer code;@ApiModelProperty(value = "返回消息")private String message;@ApiModelProperty(value = "返回数据")private Map<String, Object> data = new HashMap<String, Object>();//私有化构造器,使其他类只能调用提供的方法private R() {}//结果返回成功静态方法public static R ok(){R r = new R();r.setSuccess(true);r.setCode(ResultCode.SUCCESS);r.setMessage("成功");return r;}//结果返回失败静态方法public static R error(){R resultMessage = new R();resultMessage.setSuccess(false);resultMessage.setCode(ResultCode.ERROR);resultMessage.setMessage("失败");return resultMessage;}public R success(Boolean success){this.setSuccess(success);return this;}// 提供自定义信息的方法,由调用者传入public R message(String message){this.setMessage(message);return this;}public R code(Integer code){this.setCode(code);return this;}public R data(String key, Object value){this.data.put(key, value);return this;}public R data(Map<String, Object> map){this.setData(map);return this;}}
4.使用统一返回信息
统一返回结果格式放在了公共的包中,业务模块使用该功能是跨模块调用,因此需要在业务模块中加上common_utils模块依赖。
例如在service 模块的pom.xml 文件中导入common_utils依赖
<!--统一返回结果格式-->
<dependency><groupId>com.bruce</groupId><artifactId>common_utils</artifactId><version>0.0.1-SNAPSHOT</version>
</dependency>
3.接口统一请求信息模板
前端请求接口时通常将数据封装到JSON对象中传递给接口,在封装json数据时可以定义一个规范的数据模板,所有请求这都使用同一个数据模板
3.1.封装统一请求信息
1.导入依赖包
封装请求信息类时会对每个请求字段信息做校验,验证前端传来的信息是否符合规则,如果不符合则直接返回。因此我们需要使用jakarta.validation依赖提供的参数校验功能。
这个校验功能只在请求信息中使用,而封装统一请求信息只在common_utils模块中,因此就将此依赖添加到该默认pom.xml文件中
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>common</artifactId><groupId>com.bruce</groupId><version>0.0.1-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>common_utils</artifactId><dependencies><dependency><groupId>jakarta.validation</groupId><artifactId>jakarta.validation-api</artifactId><version>2.0.2</version></dependency></dependencies></project>
2.封装请求信息类
统一请求数据模板被各个业务模块调用,它属于公共模块因此将它在common模块common_utils下,和统一返回数据格式在同一个目录下。
在commonutils包下新建B类设计统一请求信息格式,根据封装json结构设计如下
- 除了data属性,所有的属性都是固定不变的,请求每个接口都要携带这些属性。
- data属性定义为泛型,用来接收前端请求接口传来的数据。
package com.bruce.commonutils;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.io.Serializable;/*** @author bruce* @create 2023/3/9 19:41*/
@Data
@ApiModel("基础请求")
public class B<T> implements Serializable {@NotNull(message = "渠道参数错误")@ApiModelProperty(value = "渠道", example = "AppStore")private String channelId;@NotNull(message = "设备ID参数错误")@ApiModelProperty(value = "设备ID", example = "C3C1A3EE-6B32-4C06-A83C-DFCC0A31A331")private String deviceNo;@NotNull(message = "请求端设备种类参数错误")@ApiModelProperty(value = "请求端设备种类", example = "Android iOS PC")private String deviceType;@NotNull(message = "请求端版本号参数错误")@ApiModelProperty(value = "请求端版本号", example = "1.3.2")private String version;@NotNull(message = "请求端版本参数错误")@ApiModelProperty(value = "请求端版本", example = "001003002")private Integer versionCode;@ApiModelProperty(value = "请求端产品", example = "wenyizhongguo")private String clientType;@ApiModelProperty(value = "分辨率", example = "1280*720")private String resolution;@ApiModelProperty(value = "系统版本", example = "8.0")private String osVersion;@ApiModelProperty(value = "厂商", example = "iPhone 8 Plus")private String deviceName;@ApiModelProperty(value = "定位信息")private String adCode;@ApiModelProperty(value = "网络类型", example = "5G, wifi")private String networkAccess;@Valid@ApiModelProperty("请求内容")private T data;
}
3.使用统一请求模板
1.统一请求放在了公共的包中,业务模块使用该功能是跨模块调用,因此需要在业务模块中加上common_utils模块依赖。
例如在service 模块的pom.xml 文件中导入common_utils依赖,如果已存在则忽略。
<!--统一返回结果格式-->
<dependency><groupId>com.bruce</groupId><artifactId>common_utils</artifactId><version>0.0.1-SNAPSHOT</version>
</dependency>
2.在业务模块的启动类上使用@ComponentScan(basePackages = {“com.bruce”})注解设置包扫描路径,扫描到common包就可以。
4.统一异常信息模板
在请求接口遇到异常时会返回一个异常信息,这个异常信息通常都是java异常类返回的信息。用户都是看了之后不知道是什么意思,因此我们将这些返回的的异常信息统一进行处理,返回用户能够看懂的信息。
封装统一异常模板包含三个类型,之所以将他们分类就是为了在众多的异常中能够快速定位出问题出现在代码的位置和原因。
返回全局异常信息
全局异常类型是一个通用类型,他没有特殊含义,只要是异常信息都可以通过它返回给调用者。
特定异常处理
创建一些特别异常类型,用来突出显示某些问题,例如空指针异常类型、除数不能为0异常类型等等
自定义异常处理
自定义一些与业务相关的异常信息,例如登录没有权限异常、下单失败异常等等
4.1.封装全局异常类
1.导入依赖
封装全局异常类时需要依赖统一返回信息对象,所以需要在service_base模块中引入commonutils依赖
<dependency><groupId>com.bruce</groupId><artifactId>common_utils</artifactId><version>0.0.1-SNAPSHOT</version>
</dependency>
2.创建全局类封装异常
1.在common公共模块的service_base公共模块下封装异常信息模板
2.在该模块的com.bruce.servicebase包下新建exceptionhandler包
3.在该包下新建GlobalExceptionHandler 类封装全局异常类
package com.bruce.servicebase.exceptionhandler;import com.bruce.commonutils.R;
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.ResponseBody;/*** @author bruce* @create 2023/3/9 20:25*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {//设置哪些异常调用这个统一异常方法@ExceptionHandler(Exception.class)//返回数据@ResponseBodypublic R error(Exception e) {e.printStackTrace();return R.error().message("执行了全局异常处理");}
}
3.使用统一异常处理信息
1.统一异常放在了公共的包中,业务模块使用该功能是跨模块调用,因此需要在业务模块中加上service_base公共模块依赖。
例如在service 模块的pom.xml 文件中导入service_base依赖,如果已存在则忽略。
<dependency><groupId>com.bruce</groupId><artifactId>service_base</artifactId><version>0.0.1-SNAPSHOT</version>
</dependency>
2.在业务启动类中使用@ComponentScan注解扫描common的包,以便能够将统一异常处理类进行注册。
4.2.封装特定异常
特定异常处理和全局异常处理原理相同,唯一的区别就是给某个异常类设置一个专用的返回信息。例如被除数为零时会调用ArithmeticException异常类,我们可以专为该异常类添加一个异常信息。
在全局异常类GlobalExceptionHandler 封装特定异常类信息
package com.bruce.servicebase.exceptionhandler;import com.bruce.commonutils.R;
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.ResponseBody;/*** @author bruce* @create 2023/3/9 20:25*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {//设置哪些异常调用这个统一异常方法@ExceptionHandler(Exception.class)//返回数据@ResponseBodypublic R error(Exception e) {e.printStackTrace();return R.error().message("执行了全局异常处理");}//设置特定异常信息@ExceptionHandler(ArithmeticException.class)@ResponseBodypublic R error(ArithmeticException e) {e.printStackTrace();return R.error().message("特定异常:被除数不能为零");}
}
4.3.封装自定义异常信息
当全局异常中没有我们想要的异常类型时,我们可以自己创造一个异常类型,用到我们的业务中。在不同的业务中返回不同的自定义信息。
自定义异常信息分三个步骤
- 定义自定义异常信息属性结构
- 自定义异常处理返回的信息
- 使用自定义异常返回信息
1.定义自定义异常信息属性结构
在exceptionhandler包新建一个自定义异常类BruceException类,继承RuntimeException异常类。该类定义异常信息的属性。
package com.bruce.servicebase.exceptionhandler;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @author bruce* @create 2023/3/9 20:48*/
@Data
//生成有参构造器
@AllArgsConstructor
//生成无餐构造器
@NoArgsConstructor
public class BruceException extends RuntimeException {//定义状态码private Integer code;//定义异常信息private String msg;
}
2.自定义异常处理返回的信息
在全局异常类GlobalExceptionHandler 封装自定义异常类信息
package com.bruce.servicebase.exceptionhandler;import com.bruce.commonutils.R;
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.ResponseBody;/*** @author bruce* @create 2023/3/9 20:25*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {//设置哪些异常调用这个统一异常方法@ExceptionHandler(Exception.class)//返回数据@ResponseBodypublic R error(Exception e) {e.printStackTrace();return R.error().message("执行了全局异常处理");}//设置特定异常信息@ExceptionHandler(ArithmeticException.class)@ResponseBodypublic R error(ArithmeticException e) {//将日志输出到error文件中log.error(ExceptionUtil.getMessage(e));e.printStackTrace();return R.error().message("特定异常:被除数不能为零");}//自定义异常类型@ExceptionHandler(BruceException.class)@ResponseBodypublic R error(BruceException e) {e.printStackTrace();//动态返回异常状态码和异常信息return R.error().code(e.getCode()).message(e.getMsg());}
}
3.使用自定义异常类返回自定义信息
在业务中需要抛出异常的位置,手动调用自定义的异常类,抛出自定义异常信息。
try {int a = 10/0;
}catch(Exception e) {//给自定义异常类传入状态码和异常信息throw new BruceException(20001,"出现自定义异常");
}
5.封装统一日志模板
介绍SpringBoot项目如何对日志进行统一处理,比如将info、error、warn级别的日志输出到文件,设置日志格式等。
封装日志模板包含如下几个内容
- 创建日志配置文件设置日志输出格式
- 错误日志输出到文件
- 堆栈信息输出到文件
5.1.统一日志信息设置
1.封装日志格式
在业务模块中创建配置文件封装日志,例如在 service_user 用户模块的resources文件夹下新建logback-spring.xml配置文件,配置日志内容。
配置文件分为两个部分
- springProfile标签以上的内容都是设置日志输出格式和日志输出到文件路径。
- springProfile标签设置日志应用到不同环境日志的级别。
日志级别设置介绍
- 日志的级别是在springProfile标签中设置,root标签设置的是全局日志级别。
- logger标签是设置某个类或者某个包输出日志的级别,例如只将sql输出设置为debug级别,就可以指定mapper包输出的日志为debug。
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds"><!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 --><!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true --><!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 --><!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --><contextName>logback</contextName><!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。这里value的值将作为日志文件路径--><property name="log.path" value="/Users/edy/Documents/javawork/parent_spring"/><!-- 彩色日志 --><!-- 配置格式变量:CONSOLE_LOG_PATTERN 彩色日志格式 --><!-- magenta:洋红 --><!-- boldMagenta:粗红--><!-- cyan:青色 --><!-- white:白色 --><!-- magenta:洋红 --><property name="CONSOLE_LOG_PATTERN"value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/><!--输出到控制台--><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><!--这里设置的日志级别优先级高于配置文件底部springProfile标签的logger和root子标签设置的日志级别如果这里设置了日志级别,会覆盖下面的设置的日志级别,下面设置的日志级别不会生效。--><!--<level>WARN</level>--></filter><encoder><Pattern>${CONSOLE_LOG_PATTERN}</Pattern><!-- 设置字符集 --><charset>UTF-8</charset></encoder></appender><!--输出到文件--><!-- 时间滚动输出 level为 INFO 日志 --><appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在记录的日志文件的路径及文件名 --><file>${log.path}/log_info.log</file><!--日志文件输出格式--><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern><charset>UTF-8</charset></encoder><!-- 日志记录器的滚动策略,按日期,按大小记录 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 每天日志归档路径以及格式 --><fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>100MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy><!--日志文件保留天数--><maxHistory>15</maxHistory></rollingPolicy><!-- 此日志文件只记录info级别的 --><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>INFO</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!-- 时间滚动输出 level为 WARN 日志 --><appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在记录的日志文件的路径及文件名 --><file>${log.path}/log_warn.log</file><!--日志文件输出格式--><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern><charset>UTF-8</charset> <!-- 此处设置字符集 --></encoder><!-- 日志记录器的滚动策略,按日期,按大小记录 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>100MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy><!--日志文件保留天数--><maxHistory>15</maxHistory></rollingPolicy><!-- 此日志文件只记录warn级别的 --><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>warn</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!-- 时间滚动输出 level为 ERROR 日志 --><appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在记录的日志文件的路径及文件名 --><file>${log.path}/log_error.log</file><!--日志文件输出格式--><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern><charset>UTF-8</charset> <!-- 此处设置字符集 --></encoder><!-- 日志记录器的滚动策略,按日期,按大小记录 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>100MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy><!--日志文件保留天数--><maxHistory>15</maxHistory></rollingPolicy><!-- 此日志文件只记录ERROR级别的 --><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><!--<logger>用来设置某一个包或者具体的某一个类的日志打印级别、以及指定<appender>。<logger>仅有一个name属性,一个可选的level和一个可选的addtivity属性。name:用来指定受此logger约束的某一个包或者具体的某一个类。level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,如果未设置此属性,那么当前logger将会继承上级的级别。--><!--使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:第一种把<root level="INFO">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息第二种就是单独给mapper下目录配置DEBUG模式,代码如下,这样配置sql语句会打印,其他还是正常DEBUG级别:--><!--开发环境:打印控制台--><springProfile name="dev"><!--可以输出项目中的debug日志,包括mybatis的sql日志--><logger name="com.bruce.service_user.mapper" level="DEBUG" /><!--root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,默认是DEBUG可以包含零个或多个appender元素,ref属性值是引用上面appender标签定义的名称,用来设置上面这些属性的日志级别。--><root level="INFO"><appender-ref ref="CONSOLE" /><appender-ref ref="INFO_FILE" /><appender-ref ref="WARN_FILE" /><appender-ref ref="ERROR_FILE" /></root></springProfile><!--生产环境:输出到文件--><springProfile name="pro"><root level="INFO"><appender-ref ref="CONSOLE" /><appender-ref ref="DEBUG_FILE" /><appender-ref ref="INFO_FILE" /><appender-ref ref="ERROR_FILE" /><appender-ref ref="WARN_FILE" /></root></springProfile></configuration>
2.设置错误日志输出到文件
上面的配置文件已经配置好了日志输出的格式,运行项目后就会自动在配置文件中指定的路径下创建info.log、error.log、warn.log三个日志文件。
通常代码报错都会自动调用异常类,抛出异常信息。因此我们在异常类上添加注解,就可以将异常信息都输出到error.log文件中。
此处会用到统一异常信息处理知识,因此需要先配置好统一异常信息的内容。
在异常类上配置日志
- 在
service_base公共模块中打开封装全局异常类GlobalExceptionHandler,在该类上添加@Slf4j注解。 - 在该类处理异常的方法里使用log.error()方法将异常信息输出到error文件。
- 输出日志的方法还有log.info、log.warn、log.debug不同的日志级别输出到不同级别的文件。
package com.bruce.servicebase.exceptionhandler;import com.bruce.commonutils.R;
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.ResponseBody;/*** @author bruce* @create 2023/3/9 20:25*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {//设置哪些异常调用这个统一异常方法@ExceptionHandler(Exception.class)//返回数据@ResponseBodypublic R error(Exception e) {e.printStackTrace();return R.error().message("执行了全局异常处理");}//设置特定异常信息@ExceptionHandler(ArithmeticException.class)@ResponseBodypublic R error(ArithmeticException e) {//将异常日志输出到error文件中,只有错误信息没有上下文栈信息log.error(e.getMessage());e.printStackTrace();return R.error().message("特定异常:被除数不能为零");}//自定义异常类型@ExceptionHandler(BruceException.class)@ResponseBodypublic R error(BruceException e) {e.printStackTrace();//动态返回异常状态码和异常信息return R.error().code(e.getCode()).message(e.getMsg());}
}
3.异常栈信息输出到错误日志文件
在service_base 公共模块的 exceptionhandler包新建ExceptionUtil类,这个类的作用是将异常栈信息输出到错误日志文件中
package com.bruce.servicebase.exceptionhandler;import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;/*** @author bruce* @create 2023/3/9 20:43*/
public class ExceptionUtil {public static String getMessage(Exception e) {StringWriter sw = null;PrintWriter pw = null;try {sw = new StringWriter();pw = new PrintWriter(sw);// 将出错的栈信息输出到printWriter中e.printStackTrace(pw);pw.flush();sw.flush();} finally {if (sw != null) {try {sw.close();} catch (IOException e1) {e1.printStackTrace();}}if (pw != null) {pw.close();}}return sw.toString();}
}
在全局异常类GlobalExceptionHandler 通过log.err方法将异常输出到错误日志
//设置特定异常信息@ExceptionHandler(ArithmeticException.class)@ResponseBodypublic R error(ArithmeticException e) {//调用ExceptionUtil工具路,将异常栈信息输出到错误日志error文件中log.error(ExceptionUtil.getMessage(e));// 控制台输出异常信息e.printStackTrace();return R.error().message("特定异常:被除数不能为零");}
4.禁用mybatis日志
在每个业务模块的application.properties文件中将mybatis日志删掉或禁用,否则日志插件会有冲突。
例如 service_user 用户模块的application.properties文件中将下面的日志内容删除。
#mybatis日志
#logging.level.root=INFO
#mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
6.验收脚手架
我们通过开发一个用户模块业务验收搭建的《搭建SpringBoot多模块微服务项目脚手架》各个功能是否可以正常使用。
6.1.验收Swagger
1.启动service_user 模块服务,点击UserApplication 启动类启动服务。
2.在浏览器输入http://localhost:8001/swagger-ui/index.html 查看Swagger
6.2.验收MybatisPlus
MybatisPlus功能都在service_user模块上验证,通过mybatisplus操作增删改查业务,验收ID自增长、创建时间和修改时间自动填充、分页查询、逻辑删除功能。
1.实体类添加MybatisPlus注解
在service_user模块的entity 包中打开User实体类
- 在
id属性上添加 @TableId 注解,自动生成ID。 - 在
gmtCreate属性 和gmtModified属性上添加@TableField注解,自动填充内容 - 在
isDeleted属性上添加 @TableLogic 注解实现逻辑删除
package com.bruce.service_user.entity;import com.baomidou.mybatisplus.annotation.*;import java.util.Date;import java.io.Serializable;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;/*** <p>* * </p>** @author bruce* @since 2023-03-09*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="User对象", description="")
public class User implements Serializable {private static final long serialVersionUID = 1L;@ApiModelProperty(value = "用户ID")@TableId(value = "id", type = IdType.ID_WORKER_STR)private String id;@ApiModelProperty(value = "姓名")private String name;@ApiModelProperty(value = "年龄")private Integer age;@ApiModelProperty(value = "邮箱")private String email;@ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除")@TableLogicprivate Boolean isDeleted;@ApiModelProperty(value = "创建时间")@TableField(fill = FieldFill.INSERT)private Date gmtCreate;@ApiModelProperty(value = "更新时间")@TableField(fill = FieldFill.INSERT_UPDATE)private Date gmtModified;}
2. 新增业务验证ID自增长、创建时间和修改时间自动填充功能
新增业务用来验证ID自增长、创建时间和修改时间自动填充功能。
在service_user模块,controller包中UserController类中开发新增用户接口
controller层开发新增用户接口
package com.bruce.service_user.controller;import com.bruce.commonutils.B;
import com.bruce.commonutils.R;
import com.bruce.service_user.entity.User;
import com.bruce.service_user.service.UserService;
import com.bruce.servicebase.exceptionhandler.BruceException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/*** <p>* 前端控制器* </p>** @author bruce* @since 2023-03-09*/
@Api(tags = "用户管理")
@RestController
@RequestMapping("/service_user/user")
public class UserController {@AutowiredUserService userService;@ApiOperation(value = "新增用户")@PostMapping("addUser")public R addUser(@RequestBody B<User> b) {int userId = userService.addUser(b);if (userId < 1) {throw new BruceException(20003, "新增用户失败");}return R.ok();}
}
service层实现新增用户业务逻辑
package com.bruce.service_user.service.impl;import com.bruce.commonutils.B;
import com.bruce.service_user.entity.User;
import com.bruce.service_user.mapper.UserMapper;
import com.bruce.service_user.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;/*** <p>* 服务实现类* </p>** @author bruce* @since 2023-03-09*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {@Overridepublic int addUser(B<User> b) {User user = b.getData();int insert = baseMapper.insert(user);return insert;}
}
调用新增用户接口
在Swagger上点击新增用户接口,填写如下请求内容。并执行Execute。
{"adCode": "string","channelId": "AppStore","clientType": "wenyizhongguo","data": {"age": 10,"email": "user@163.com","name": "张三"},"deviceName": "iPhone 8 Plus","deviceNo": "C3C1A3EE-6B32-4C06-A83C-DFCC0A31A331","deviceType": "Android iOS PC","networkAccess": "5G, wifi","osVersion": "8.0","resolution": "1280*720","version": "1.3.2","versionCode": 1003002
}
查看控制台输出了新增成功的日志,查看mysql数据库新增了一条数据,从数据中查看id为自动生成,创建时间和修改时间自动填充。删除状态填充了默认值。
+---------------------+--------+------+--------------+------------+---------------------+---------------------+
| id | name | age | email | is_deleted | gmt_create | gmt_modified |
+---------------------+--------+------+--------------+------------+---------------------+---------------------+
| 1634037440222609409 | 张三 | 10 | user@163.com | 0 | 2023-03-10 11:44:19 | 2023-03-10 11:44:19 |
+---------------------+--------+------+--------------+------------+---------------------+---------------------+
3.分页查询
通过分页查询用户列表,验证封装的分页查询功能。
controller层开发分页查询接口
在service_user模块,controller包中UserController类中开发分页查询用户接口
@ApiOperation(value = "无条件分页查询用户")@GetMapping("pageUser/{page}/{limit}")public R queryUserList(@ApiParam(name = "page", value = "当前页码", required = true)@PathVariable long page,@ApiParam(name = "limit", value = "每页记录数", required = true)@PathVariable long limit) {//创建分页查询对象Page<User> userPage = new Page<>(page, limit);//调用分页查询对象,传入分页对象和查询条件,结果封装到userPage对象userService.page(userPage, null);//获取查询结果List<User> records = userPage.getRecords();//获取查询总条数long size = userPage.getSize();//封装返回结果Map<String, Object> map = new HashMap<>();map.put("total", size);map.put("data", records);return R.ok().message("成功").data(map);}
调用接口查询用户
在Swagger上点击查询用户接口,填写当前页和每页条数。并执行Execute。返回查询结果
{"success": true,"code": 200,"message": "成功","data": {"total": 10,"data": [{"id": "1634037440222609409","name": "张三","age": 10,"email": "user@163.com","isDeleted": false,"gmtCreate": "2023-03-10T03:44:19.000+00:00","gmtModified": "2023-03-10T03:44:19.000+00:00"}]}
}
4.逻辑删除
通过删除用户,验证封装的逻辑删除功能。
controller层开发删除用户接口
在service_user模块,controller包中UserController类中开发删除用户接口
@ApiOperation(value = "根据ID删除用户", notes = "逻辑删除用户")@DeleteMapping("{id}")public R removeById(@PathVariable String id) {boolean flag = userService.removeById(id);if (flag) {return R.ok();} else {return R.error();}}
调用接口删除用户
在Swagger上点击删除用户接口,填写用户id。并执行Execute。通过mysql查询用户信息,is_deleted字段变为1.
+---------------------+--------+------+--------------+------------+---------------------+---------------------+
| id | name | age | email | is_deleted | gmt_create | gmt_modified |
+---------------------+--------+------+--------------+------------+---------------------+---------------------+
| 1634037440222609409 | 张三 | 10 | user@163.com | 1 | 2023-03-10 11:44:19 | 2023-03-10 11:44:19 |
+---------------------+--------+------+--------------+------------+---------------------+---------------------+
1 row in set (0.00 sec)
6.3.验收统一请求和返回信息模块
上面示例中开发的接口返回类型使用我们封装的R类对象,请求参数类型使用我们封装的B类型对象。
@ApiOperation(value = "新增用户")@PostMapping("addUser")public R addUser(@RequestBody B<User> b) {int userId = userService.addUser(b);if (userId < 1) {throw new BruceException(20003, "新增用户失败");}return R.ok();}
6.4.验收异常
1.特定异常
通过一个被除数为0验证下发生异常后调用我们封装好的ArithmeticException 异常,返回被除数不能为0信息。
controller层开发特定异常接口
在service_user模块,controller包中UserController类中开发特定异常接口
注意:在接口中只定义了成功返回的信息,没有判断失败时返回的信息,是因为它发生异常后会走封装好的特定异常方法,在该方法中定义好了返回信息格式。
@ApiOperation(value = "特定异常", notes = "被除数为0异常 ArithmeticExceptio")@GetMapping("exception/{first}/{secont}")public R myException(@ApiParam(name = "first", value = "除数", required = true)@PathVariable long first,@ApiParam(name = "secont", value = "被除数", required = true)@PathVariable long secont) {long l = first / secont;return R.ok().message("成功").data("data", l);}
调用特定异常接口
在Swagger上点击特定异常接口,除数填写1,被除数填写0。并执行Execute。返回信息是特定异常封装的返回信息。
{"success": false,"code": 60000,"message": "特定异常:被除数不能为零","data": {}
}
2.自定义异常
通过一个被除数为0验证下发生异常后调用我们封装好的BruceException 自定义异常类,返回传入的code和message信息。
controller层开发特定异常接口
在service_user模块,controller包中UserController类中开发特定异常接口
@ApiOperation(value = "自定义异常", notes = "被除数为0异常 自定义异常信息")@GetMapping("exception/{first}/{secont}")public R myException(@ApiParam(name = "first", value = "除数", required = true)@PathVariable long first,@ApiParam(name = "secont", value = "被除数", required = true)@PathVariable long secont) {try {long l = first / secont;return R.ok().message("成功").data("data", l);} catch (Exception e) {//实例化自定义异常类对象,传入code和messagethrow new BruceException(3001, "自定义异常");}}
调用自定义异常接口
在Swagger上点击自定义异常接口,除数填写1,被除数填写0。并执行Execute。返回信息是自定义异常封装的返回信息。
{"success": false,"code": 3001,"message": "自定义异常","data": {}
}
6.5.验收日志
我们将日志封装后根据日志类型分别输出到 log_info.log 、 log_warn.log 、 log_error.log 三个文件中,上面操作业务有成功和和异常的,因此我们只要查看下日志文件就可以确认它是否可用。

7.总结
每次使用SpringBoot开发微服务项目时都要搭建一次完整的开发环境过程都很长,配置也很复杂繁琐。但是我们将这个过程做成一个模板并命名为脚手架,那么就不一样了。
将《搭建SpringBoot多模块微服务项目脚手架》上传到GitHub上,每次需要的时候只要下载下来,简单的修改下包名就可以开发业务,这个体验就很舒服了。
福利
《搭建SpringBoot多模块微服务项目脚手架》 源代码已在Gitee仓库开源,每个想学习的朋友都可以下载练手。
只做为学习,勿做他用!
Gitee仓库项目地址:https://gitee.com/brucexiaogui/spring-boot-hand-ladder
相关文章:
搭建SpringBoot多模块微服务项目脚手架(三)
搭建SpringBoot多模块微服务项目脚手架(三) 文章目录搭建SpringBoot多模块微服务项目脚手架(三)1.概述项目结构2.接口返回统一信息模板2.1.封装返回统一信息思路介绍2.2.封装json数据格式1.导入依赖2.封装code码3.封装json格式模板4.使用统一返回信息3.接口统一请求信息模板3.1…...
对vue3中reactive、toref、torefs、ref的详细理解
reactive:将平常的一个对象转换成响应式对象。所谓的响应式对象就是当页面点击修改此对象时,页面无需刷新而在页面上的其他地方有用到这个对象的地方会自动同步修改过来例如: <template><div class"container"><di…...
C++ Primer Plus 第6版 读书笔记(6) 第 6 章 分支语句和逻辑运算符
第 6 章 分支语句和逻辑运算符 C是在 C 语言基础上开发的一种集面向对象编程、泛型编程和过程化编程于一体的编程语言,是C语言的超集。本书是根据2003年的ISO/ANSI C标准编写的,通过大量短小精悍的程序详细而全面地阐述了 C的基本概念和技术,…...
Java Class 加密工具 ClassFinal
Jar包加密工具 ClassFinal介绍环境依赖使用说明下载加密命令行示例maven插件方式无密码模式机器绑定启动加密后的jar启动参数给密码不加密码参数直接启动1. 密码文件获取2. 交互输入参考资料介绍 ClassFinal 是一款 java class 文件安全加密工具,支持直接加密jar包…...
【蓝桥杯集训·每日一题】AcWing 3555. 二叉树
文章目录一、题目1、原题链接2、题目描述二、解题报告1、思路分析2、时间复杂度3、代码详解三、知识风暴最近公共祖先一、题目 1、原题链接 3555. 二叉树 2、题目描述 给定一个 n 个结点(编号 1∼n)构成的二叉树,其根结点为 1 号点。 进行 m…...
【JavaScript运行原理之V8引擎】V8引擎解析JavaScript代码原理
1. 编程语言的执行 高级语言最终都需要编译为低级语言才能被硬件执行,越高级的语言中间的转换时间越长,效率越低,越低级的语言执行素的越快,但是由于缺少高级语言便捷的语法特性所以很难编写代码。 2. 大杂烩JS 它是作者在1995…...
C++11:智能指针
文章目录1. 介绍1.1 动态内存与智能指针2. 使用2.1 创建2.2 使用3. 原理3.1 RAII3.2 像指针一样使用3.3 支持智能指针对象拷贝auto_ptrRAII4. 标准库中的智能指针4.1 unique_ptr模拟实现4.2 shared_ptr引用计数模拟实现定制删除器4.3 weak_ptrshared_ptr造成的循环引用问题与sh…...
ccc-pytorch-RNN(7)
文章目录一、RNN简介二、RNN关键结构三、RNN的训练方式四、时间序列预测五、梯度弥散和梯度爆炸问题一、RNN简介 RNN(Recurrent Neural Network)中文循环神经网络,用于处理序列数据。它与传统人工神经网络和卷积神经网络的输入和输出相互独立…...
docker安装(linux)
安装需要的软件包 yum install -y yum-utils 设置stable镜像仓库(使用阿里云镜像) yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 更新yum软件包索引 yum makecache fast 安装DOCKER 引擎 yum -y…...
【数据库概论】10.1 事务及其作用
事务是一系列的数据库操作,是数据库应用程序的基本逻辑单元 10.1 事务的基本概念 1.事务 事务是用户定义的一个数据库操作序列,是一个具有原子性的操作,不可再分,一个事务内的操作要么全做、要么都不做。一般来说,一…...
通讯录(C++实现)
系统需求通讯录是一个可以记录亲人、好友信息的工具。本章主要利用C来实现一个通讯录管理系统系统中需要实现的功能如下:添加联系人:向通讯录中添加新人,信息包括(姓名、性别、年龄、联系电话、家庭住址)最多记录1000人显示联系人:显示通讯录…...
轻松掌握C++的模板与类模板,将Tamplate广泛运用于我们的编程生活
C提高编程 本阶段主要针对C泛型编程和STL技术做详细讲解,探讨C更深层的使用 泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。 模板 1.模板的概念 模板就是建立通用的模具,大大提高复用性 例如: 2.函数模板 C另一种编程思想称…...
pandas 数据预处理+数据概览 处理技巧整理(持续更新版)
这篇文章主要是整理下使用pandas的一些技巧,因为经常不用它,这些指令忘得真的很快。前段时间在数模美赛中已经栽过跟头了,不希望以后遇到相关问题的时候还去网上查(主要是太杂了)。可能读者跟我有一样的问题࿰…...
mmdetectionV2.x版本 训练自己的VOC数据集
mmdetection目录下创建data文件夹,路劲如图所示,不带yololabels 修改配置文件 mmdet/datasets/voc.py 配置图片格式 mmdet/datasets/xml_style.py 如果图片是jpg则改成jpg,是png格式就改成png,这里我不需要改,本…...
Shell - crontab 定时 git 拉取并执行 maven 打包
目录 一.引言 二.踩坑与实践 1.原始代码 2.mvn package 未执行与解决 [导入环境变量] 3.git pull 未执行与解决 [添加绝对路径] 三.总结 一.引言 git 任务部署在通道机,每天6点需要定时更新 jar 包并打包上线,所以需要在 linux 服务器上ÿ…...
408考研计算机之计算机组成与设计——知识点及其做题经验篇目3:指令的寻址方式
上篇文章我们讲到,指令的基本格式,一条指令通常包括操作码字段和地址码字段两部分: 操作码字段地址码字段并且我们还讲到根据操作数地址码的数目不同,可将指令分为零一二三四地址指令。感兴趣的小伙伴们可以看看小编的上一篇文章…...
前端包管理工具:npm,yarn、cnpm、npx、pnpm
包管理工具npm Node Package Manager,也就是Node包管理器; 但是目前已经不仅仅是Node包管理器了,在前端项目中我们也在使用它来管理依赖的包; 比如vue、vue-router、vuex、express、koa、react、react-dom、axios、babel、webpack…...
推荐系统 FM因式分解
reference:知乎 FM算法解析 LR算法没有二阶交叉 如果是id类特征,这里的x是0/1,raw的特征输入就是float,当然,在我的理解里,一般会把raw的特征进行分桶,还是映射到0/1特征,不然这个w…...
Maven基础入门
文章目录Maven简介Maven 工作模式1.仓库2.坐标Maven的基本使用1.常用命令2.生命周期依赖管理1.依赖配置2.依赖传递3.可选依赖4.排除依赖5.依赖范围IDEA配置MavenMaven简介 Apache Maven 是一个项目管理和构建工具,它基于项目对象模型(POM)的概念,通过一…...
传输层协议 TCP UDP
目录 协议前菜 端口号 编辑端口号范围划分 认识知名端口号(Well-Know Port Number) netstat pidof 传输层协议 UDP协议 UDP协议端格式 UDP的特点 面向数据报 UDP的缓冲区 UDP使用注意事项 基于UDP的应用层协议 TCP协议 TCP协议概念 TCP协议段格式 标志…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...
