当前位置: 首页 > news >正文

搭建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.loglog_warn.loglog_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&#xff1a;将平常的一个对象转换成响应式对象。所谓的响应式对象就是当页面点击修改此对象时&#xff0c;页面无需刷新而在页面上的其他地方有用到这个对象的地方会自动同步修改过来例如&#xff1a; <template><div class"container"><di…...

C++ Primer Plus 第6版 读书笔记(6) 第 6 章 分支语句和逻辑运算符

第 6 章 分支语句和逻辑运算符 C是在 C 语言基础上开发的一种集面向对象编程、泛型编程和过程化编程于一体的编程语言&#xff0c;是C语言的超集。本书是根据2003年的ISO/ANSI C标准编写的&#xff0c;通过大量短小精悍的程序详细而全面地阐述了 C的基本概念和技术&#xff0c;…...

Java Class 加密工具 ClassFinal

Jar包加密工具 ClassFinal介绍环境依赖使用说明下载加密命令行示例maven插件方式无密码模式机器绑定启动加密后的jar启动参数给密码不加密码参数直接启动1. 密码文件获取2. 交互输入参考资料介绍 ClassFinal 是一款 java class 文件安全加密工具&#xff0c;支持直接加密jar包…...

【蓝桥杯集训·每日一题】AcWing 3555. 二叉树

文章目录一、题目1、原题链接2、题目描述二、解题报告1、思路分析2、时间复杂度3、代码详解三、知识风暴最近公共祖先一、题目 1、原题链接 3555. 二叉树 2、题目描述 给定一个 n 个结点&#xff08;编号 1∼n&#xff09;构成的二叉树&#xff0c;其根结点为 1 号点。 进行 m…...

【JavaScript运行原理之V8引擎】V8引擎解析JavaScript代码原理

1. 编程语言的执行 高级语言最终都需要编译为低级语言才能被硬件执行&#xff0c;越高级的语言中间的转换时间越长&#xff0c;效率越低&#xff0c;越低级的语言执行素的越快&#xff0c;但是由于缺少高级语言便捷的语法特性所以很难编写代码。 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&#xff08;Recurrent Neural Network&#xff09;中文循环神经网络&#xff0c;用于处理序列数据。它与传统人工神经网络和卷积神经网络的输入和输出相互独立…...

docker安装(linux)

安装需要的软件包 yum install -y yum-utils 设置stable镜像仓库&#xff08;使用阿里云镜像&#xff09; 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 事务及其作用

事务是一系列的数据库操作&#xff0c;是数据库应用程序的基本逻辑单元 10.1 事务的基本概念 1.事务 事务是用户定义的一个数据库操作序列&#xff0c;是一个具有原子性的操作&#xff0c;不可再分&#xff0c;一个事务内的操作要么全做、要么都不做。一般来说&#xff0c;一…...

通讯录(C++实现)

系统需求通讯录是一个可以记录亲人、好友信息的工具。本章主要利用C来实现一个通讯录管理系统系统中需要实现的功能如下:添加联系人:向通讯录中添加新人&#xff0c;信息包括&#xff08;姓名、性别、年龄、联系电话、家庭住址&#xff09;最多记录1000人显示联系人:显示通讯录…...

轻松掌握C++的模板与类模板,将Tamplate广泛运用于我们的编程生活

C提高编程 本阶段主要针对C泛型编程和STL技术做详细讲解&#xff0c;探讨C更深层的使用 泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。 模板 1.模板的概念 模板就是建立通用的模具&#xff0c;大大提高复用性 例如&#xff1a; 2.函数模板 C另一种编程思想称…...

pandas 数据预处理+数据概览 处理技巧整理(持续更新版)

这篇文章主要是整理下使用pandas的一些技巧&#xff0c;因为经常不用它&#xff0c;这些指令忘得真的很快。前段时间在数模美赛中已经栽过跟头了&#xff0c;不希望以后遇到相关问题的时候还去网上查&#xff08;主要是太杂了&#xff09;。可能读者跟我有一样的问题&#xff0…...

mmdetectionV2.x版本 训练自己的VOC数据集

mmdetection目录下创建data文件夹&#xff0c;路劲如图所示&#xff0c;不带yololabels 修改配置文件 mmdet/datasets/voc.py 配置图片格式 mmdet/datasets/xml_style.py 如果图片是jpg则改成jpg&#xff0c;是png格式就改成png&#xff0c;这里我不需要改&#xff0c;本…...

Shell - crontab 定时 git 拉取并执行 maven 打包

目录 一.引言 二.踩坑与实践 1.原始代码 2.mvn package 未执行与解决 [导入环境变量] 3.git pull 未执行与解决 [添加绝对路径] 三.总结 一.引言 git 任务部署在通道机&#xff0c;每天6点需要定时更新 jar 包并打包上线&#xff0c;所以需要在 linux 服务器上&#xff…...

408考研计算机之计算机组成与设计——知识点及其做题经验篇目3:指令的寻址方式

上篇文章我们讲到&#xff0c;指令的基本格式&#xff0c;一条指令通常包括操作码字段和地址码字段两部分&#xff1a; 操作码字段地址码字段并且我们还讲到根据操作数地址码的数目不同&#xff0c;可将指令分为零一二三四地址指令。感兴趣的小伙伴们可以看看小编的上一篇文章…...

前端包管理工具:npm,yarn、cnpm、npx、pnpm

包管理工具npm Node Package Manager&#xff0c;也就是Node包管理器&#xff1b; 但是目前已经不仅仅是Node包管理器了&#xff0c;在前端项目中我们也在使用它来管理依赖的包&#xff1b; 比如vue、vue-router、vuex、express、koa、react、react-dom、axios、babel、webpack…...

推荐系统 FM因式分解

reference&#xff1a;知乎 FM算法解析 LR算法没有二阶交叉 如果是id类特征&#xff0c;这里的x是0/1&#xff0c;raw的特征输入就是float&#xff0c;当然&#xff0c;在我的理解里&#xff0c;一般会把raw的特征进行分桶&#xff0c;还是映射到0/1特征&#xff0c;不然这个w…...

Maven基础入门

文章目录Maven简介Maven 工作模式1.仓库2.坐标Maven的基本使用1.常用命令2.生命周期依赖管理1.依赖配置2.依赖传递3.可选依赖4.排除依赖5.依赖范围IDEA配置MavenMaven简介 Apache Maven 是一个项目管理和构建工具&#xff0c;它基于项目对象模型(POM)的概念&#xff0c;通过一…...

传输层协议 TCP UDP

目录 协议前菜 端口号 ​编辑端口号范围划分 认识知名端口号(Well-Know Port Number) netstat pidof 传输层协议 UDP协议 UDP协议端格式 UDP的特点 面向数据报 UDP的缓冲区 UDP使用注意事项 基于UDP的应用层协议 TCP协议 TCP协议概念 TCP协议段格式 标志…...

一点就分享系列(实践篇6——上篇)【迟到补发】Yolo-High_level系列算法开源项目融入V8 旨在研究和兼容使用【持续更新】

一点就分享系列&#xff08;实践篇5-补更篇&#xff09;[迟到补发]—Yolo系列算法开源项目融入V8旨在研究和兼容使用[持续更新] 题外话 去年我一直复读机式强调High-level在工业界已经饱和的情况&#xff0c;目的是呼吁更多人看准自己&#xff0c;不管是数字孪生交叉领域&#…...

buu RSA 1 (Crypto 第一页)

题目描述&#xff1a; 两个文件&#xff0c;都用记事本打开&#xff0c;记住用记事本打开 pub.key: -----BEGIN PUBLIC KEY----- MDwwDQYJKoZIhvcNAQEBBQADKwAwKAIhAMAzLFxkrkcYL2wch21CM2kQVFpY97 /AvKr1rzQczdAgMBAAE -----END PUBLIC KEY-----flag.enc: A柪YJ^ 柛x秥?y…...

Python 二分查找:bisect库的使用

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…...

性能优化之HBase性能调优

HBase是Hadoop生态系统中的一个组件&#xff0c;是一个分布式、面向列存储的内存型开源数据库&#xff0c;可以支持数百万列&#xff08;MySQL4张表在HBase中对应1个表&#xff0c;4个列&#xff09;、超过10亿行的数据存储。可用作&#xff1a;冷热数据分离HBase适合作为冷数据…...

图像金字塔,原理、实现及应用

什么是图像金字塔 图像金字塔是对图像的一种多尺度表达&#xff0c;将各个尺度的图像按照分辨率从小到大&#xff0c;依次从上到下排列&#xff0c;就会形成类似金字塔的结构&#xff0c;因此称为图像金字塔。 常见的图像金字塔有两类&#xff0c;一种是高斯金字塔&#xff0…...

08-Oracle游标管理(定义,打开、获取数据及关闭游标)

目标 1.确定何时需要显示游标2.声明、打开和关闭显示游标3.从显示游标中提取数据4.了解与游标有关的属性5.使用游标FOR循环检索游标中的数据6.在游标FOR循环的子查询中声明游标7.评估使用逻辑运算符结合在一起的布尔条件游标 1、在使用一个PL/SQL块来执行DML语句或只返回一行结…...

Python判断字符串是否包含特定子串的7种方法

目录1、使用 in 和 not in2、使用 find 方法3、使用 index 方法4、使用 count 方法5、通过魔法方法6、借助 operator7、使用正则匹配转自&#xff1a;https://cloud.tencent.com/developer/article/1699719我们经常会遇这样一个需求&#xff1a;判断字符串中是否包含某个关键词…...

aop实现接口访问频率限制

引言 项目开发中我们有时会用到一些第三方付费的接口&#xff0c;这些接口的每次调用都会产生一些费用&#xff0c;有时会有别有用心之人恶意调用我们的接口&#xff0c;造成经济损失&#xff1b;或者有时需要对一些执行时间比较长的的接口进行频率限制&#xff0c;这里我就简…...

Hive---窗口函数

Hive窗口函数 其他函数: Hive—Hive函数 文章目录Hive窗口函数开窗数据准备建表导入数据聚合函数window子句LAG(col,n,default_val) 往前第 n 行数据LEAD(col,n, default_val) 往后第 n 行数据ROW_NUMBER() 会根据顺序计算RANK() 排序相同时会重复&#xff0c;总数不会变DENSE…...

JavaSe第7次笔记

1. C语言里面&#xff0c;NULL是0地址。Java中null和0地址没关系。 2.数组可以做方法的返回值。 3.可以使用变量作为数组的个数开辟空间。 4.断言assert&#xff0c;需要设置。 5.排序&#xff1a;Arrays. sort(array); 6.查找&#xff1a; int index Arrays. binarySea…...

什么是 Service 以及描述下它的生命周期。Service 有哪些启动方法,有 什么区别,怎样停用 Service?

在 Service 的生命周期中,被回调的方法比 Activity 少一些,只有 onCreate, onStart, onDestroy, onBind 和 onUnbind。 通常有两种方式启动一个 Service,他们对 Service 生命周期的影响是不一样的。 1. 通过 startService Service 会经历 onCreate 到 onStart,然后处于运行…...

Redis部署

JAVA安装 mkdir /usr/local/javacd /usr/local/java/wget --no-check-certificate --no-cookies --header "Cookie: oraclelicenseaccept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u131-b11/d54c1d3a095b4ff2b6607d096fa80163/jdk-8u13…...

AT32F437制作Bootloader然后实现Http OTA升级

首先创建一个AT32F437的工程&#xff0c;然后发现调试工程配置这里的型号和创建工程选的型号不一致&#xff0c;手动更改一下&#xff0c;使用PW Link下载程序的话还要配置一下pyocd.exe的路径。 打开drv_clk.c文件的调试功能看下系统时钟频率。 项目使用的是AT32F437VMT7芯片&…...

Springboot项目启动初始化数据缓存

1.从Java EE5规范开始&#xff0c;Servlet中增加了两个影响Servlet生命周期的注解&#xff0c; PostConstruct和PreDestroy&#xff0c;这两个注解被用来修饰一个非静态的void&#xff08;&#xff09;方法&#xff0c;被PostConstruct修饰的方法会在服务器加载Servlet的时候运…...

深度学习必备知识——模型数据集Yolo与Voc格式文件相互转化

在深度学习中&#xff0c;第一步要做的往往就是处理数据集,尤其是学习百度飞桨PaddlePaddle的小伙伴&#xff0c;数据集经常要用Voc格式的&#xff0c;比如性能突出的ppyolo等模型。所以学会数据集转化的本领是十分必要的。这篇博客就带你一起进行Yolo与Voc格式的相互转化&…...

数据、数据资源及数据资产管理的区别

整理不易&#xff0c;转发请注明出处&#xff0c;请勿直接剽窃&#xff01; 点赞、关注、不迷路&#xff01; 摘要&#xff1a;数据、数据资源、数据资产 数据、数据资源及数据资产的区别 举例 CRM系统建设完成后会有很多数据&#xff0c;这些数据就是原始数据&#xff0c;业务…...

标度不变性(scale invariance)与无标度(scale-free)概念辨析

文章目录标度标度种类名义标度序级标度等距标度比率标度常用标度方法不足标度不变性标度不变&#xff08;Scale-invariant&#xff09;曲线和自相似性&#xff08;self-similarity&#xff09;射影几何分形随机过程中的标度不变性标度不变的 Tweedie distribution普适性&#x…...

WMS仓库管理系统解决方案,实现仓库管理一体化

仓库是企业的核心环节&#xff0c;若没有对库存的合理控制和送货&#xff0c;将会造成成本的上升&#xff0c;服务品质的难以得到保证&#xff0c;进而降低企业的竞争能力。WMS仓库管理系统包括基本信息&#xff0c;标签&#xff0c;入库&#xff0c;上架&#xff0c;领料&…...

css常见定位、居中方案_css定位居中

一、 定位分类 1、静态定位 position:static;&#xff08;默认&#xff0c;具备标准流条件&#xff09; 2、相对定位 position:relative; 通过 top 或者 bottom 来设置 Y 轴位置 通过 left 或者 right 来设置 X 轴位置 特点&#xff1a; 相对定位不会脱离文档流相对于自…...

【微信小程序】-- 自定义组件 -- 创建与引用 样式(三十二)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…...

ArangoDB——AQL编辑器

AQL 编辑器 ArangoDB 的查询语言称为 AQL。AQL与关系数据库管理系统 (RDBMS)区别在于其更像一种编程语言&#xff0c;更自然地适合无模式模型&#xff0c;并使查询语言非常强大&#xff0c;同时保持易于读写。数据建模概念 数据库是集合的集合。集合存储记录&#xff0c;称为文…...

Lesson 9.1 集成学习的三大关键领域、Bagging 方法的基本思想和 RandomForestRegressor 的实现

文章目录一、 集成学习的三大关键领域二、Bagging 方法的基本思想三、RandomForestRegressor 的实现在开始学习之前&#xff0c;先导入我们需要的库&#xff0c;并查看库的版本。 import numpy as np import pandas as pd import sklearn import matplotlib as mlp import sea…...

basic1.0链码部署(基于test-network 环境ubuntu20.04腾讯云)

解决了官方示例指令需要科学上网才能运行的问题&#xff08;通过手动下载二进制文件和拉取官方fabric-samples&#xff09;。具体的将bootstrap.sh脚本解读了一遍 具体可以参照我的博客 fabric中bootstrap.sh到底帮助我们干了什么&#xff1f;&#xff08;curl -sSL https://bi…...

Android---系统启动流程

目录 Android 系统启动流程 init 进程分析 init.rc 解析 Zygote 概叙 Zygote 触发过程 Zygote 启动过程 什么时Runtime&#xff1f; System Server 启动流程 Fork 函数 总结 面试题 Android 是 google 公司开发的一款基于 Linux 的开源操作系统。 Android 系统启动…...

【网络】http协议

&#x1f941;作者&#xff1a; 华丞臧. &#x1f4d5;​​​​专栏&#xff1a;【网络】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449; LeetCode刷题网站 文章…...

Thread::interrupted() 什么意思? 如何中断线程?

1、答&#xff1a; Thread::interrupted() 是一个静态方法&#xff0c;用于判断当前线程是否被中断&#xff0c;并清除中断标志位。 具体来说&#xff0c;当一个线程被中断后&#xff0c;它的中断状态将被设置为 true。如果在接下来的某个时间点内调用了该线程的 interrupted…...

Oracle OCP 19c 考试(1Z0-083)中关于Oracle不完全恢复的考点(文末附录像)

欢迎试看博主的专著《MySQL 8.0运维与优化》 下面是Oracle 19c OCP考试&#xff08;1Z0-083&#xff09;中关于Oracle不完全恢复的题目: A database is configured in ARCHIVELOG mode A full RMAN backup exists but no control file backup to trace has been taken A media…...

一起来学习配置Combo接口吧!

Combo接口是一个光电复用的逻辑接口&#xff0c;一个Combo接口对应设备面板上一个GE电接口和一个GE光接口。电接口与其对应的光接口是光电复用关系&#xff0c;两者不能同时工作&#xff08;当激活其中一个接口时&#xff0c;另一个接口就自动处于禁用状态&#xff09;&#xf…...

C++模拟实现红黑树

目录 介绍----什么是红黑树 甲鱼的臀部----规定 分析思考 绘图解析代码实现 节点部分 插入部分分步解析 ●父亲在祖父的左&#xff0c;叔叔在祖父的右&#xff1a; ●父亲在祖父的右&#xff0c;叔叔在祖父的左&#xff1a; 测试部分 整体代码 介绍----什么是红黑树 红…...

HTTPS协议之SSL/TLS详解(下)

目录 前言&#xff1a; SSL/TLS详解 HTTP协议传输安全性分析 对称加密 非对称加密 证书 小结&#xff1a; 前言&#xff1a; 在网络世界中&#xff0c;存在着运营商劫持和一些黑客的攻击。如果明文传输数据是很危险的操作&#xff0c;因为我们不清楚中间传输过程中就被哪…...