练题模块环境搭建

文章目录

    • 1.数据库表设计
        • 1.practice_set 套卷
        • 2.practice_set_detail 套卷细节
        • 3.practice_info 练习信息
        • 4.practice_detail 练习详情
        • 5.E-R图
    • 2.架构设计(三层架构)
    • 3.练题微服务架构搭建
        • 1.创建一个练题微服务模块
          • 1.创建一个maven项目
          • 2.把src删除,只留pom.xml
        • 2.微服务父模块的pom.xml配置
          • 1.配置packaging为pom,指定编译版本,并统一指定SpringBoot版本,统一配置阿里云仓库,使子模块继承
          • 2.type和scope解释
            • 1.type
            • 2.scope
        • 3.创建一个练题微服务的api模块
          • 1.创建一个maven项目,删除resource目录和test目录
          • 2.创建一个common包存放Result和Page相关的
            • 1.目录结构
            • 2.PageInfo.java
            • 3.PageResult.java
            • 4.Result.java
            • 5.ResultCodeEnum.java
            • 6.引入lombok的依赖
          • 3.创建一个通用的枚举包
            • 1.结构
            • 2.IsDeleteFlagEnum.java
          • 4.创建一个req和vo分别存放入参和出参实体
            • 结构
        • 4.创建一个server子模块
          • 1.创建一个maven项目
          • 2.创建一个config包,暂时先存放mybatis的东西
            • 1.结构
            • 2.SqlStatementInterceptor.java sql状态拦截器
            • 3.MybatisPlusAllSqlLog.java sql转换器
            • 4.MybatisConfiguration.java 注册两个拦截器
          • 3.引入基本依赖
          • 4.config下创建一个redis包,存放redis配置和工具类
            • 1.结构
            • 2.RedisConfig.java
            • 3.RedisUtil.java
          • 5.config下创建登录拦截器和上下文将从Header中获取logId放到ThreadLocal中
            • 1.结构
            • 2.GlobalConfig.java mvc的全局处理,空值不返回,存放自定义拦截器
            • 3.LoginContextHolder.java ThreadLocal工具类
            • 4.LoginInterceptor.java 登录拦截器,从Header中获取logId放到ThreadLocal
          • 6.创建controller包
            • 1.结构
            • 2.DemoController.java 测试
          • 7.创建其余的包
            • 1.结构
            • 2.DruidEncryptUtil.java 用于对yaml中的东西加解密
          • 8.创建启动类
            • 1.PracticeApplication.java 注意写MapperScan和ComponentScan,还有启动类注解
          • 9.创建配置文件
            • 1.resource创建一个mapper文件夹
            • 2.application.yml
            • 3.bootstrap.yml
            • 4.log4j2-spring.xml
          • 10.启动测试,一次成功!

1.数据库表设计

1.practice_set 套卷
create table practice_set
(id                  bigint auto_increment comment '主键'primary key,set_name            varchar(255)             null comment '套题名称',set_type            int                      null comment '套题类型 1实时生成 2预设套题',set_heat            int                      null comment '热度',set_desc            varchar(255)             null comment '套题描述',primary_category_id bigint                   null comment '大类id',created_by          varchar(32) charset utf8 null comment '创建人',created_time        datetime                 null comment '创建时间',update_by           varchar(32) charset utf8 null comment '更新人',update_time         datetime                 null comment '更新时间',is_deleted          int default 0            null comment '是否被删除 0为删除 1已删除'
)comment '套题信息表' collate = utf8mb4_bin;
2.practice_set_detail 套卷细节
create table practice_set_detail
(id           bigint auto_increment comment '主键'primary key,set_id       bigint                   not null comment '套题id',subject_id   bigint                   null comment '题目id',subject_type int                      null comment '题目类型',created_by   varchar(32) charset utf8 null comment '创建人',created_time datetime                 null comment '创建时间',update_by    varchar(32) charset utf8 null comment '更新人',update_time  datetime                 null comment '更新时间',is_deleted   int default 0            null comment '是否被删除 0为删除 1已删除'
)comment '套题内容表' collate = utf8mb4_bin;
3.practice_info 练习信息
create table practice_info
(id              bigint auto_increment comment '主键'primary key,set_id          bigint                   null comment '套题id',complete_status int                      null comment '是否完成 1完成 0未完成',time_use        varchar(32)              null comment '用时',submit_time     datetime                 null comment '交卷时间',correct_rate    decimal(10, 2)           null comment '正确率',created_by      varchar(32) charset utf8 null comment '创建人',created_time    datetime                 null comment '创建时间',update_by       varchar(32) charset utf8 null comment '更新人',update_time     datetime                 null comment '更新时间',is_deleted      int default 0            null comment '是否被删除 0为删除 1已删除'
)comment '练习表' collate = utf8mb4_bin;
4.practice_detail 练习详情
create table practice_detail
(id             bigint auto_increment comment '主键'primary key,practice_id    bigint                   null comment '练题id',subject_id     bigint                   null comment '题目id',subject_type   int                      null comment '题目类型',answer_status  int                      null comment '回答状态',answer_content varchar(64)              null comment '回答内容',created_by     varchar(32) charset utf8 null comment '创建人',created_time   datetime                 null comment '创建时间',update_by      varchar(32) charset utf8 null comment '更新人',update_time    datetime                 null comment '更新时间',is_deleted     int default 0            null comment '是否被删除 0为删除 1已删除'
)comment '练习详情表' collate = utf8mb4_bin;
5.E-R图

image-20240625135009775

2.架构设计(三层架构)

img

3.练题微服务架构搭建

1.创建一个练题微服务模块
1.创建一个maven项目

image-20240625140734223

image-20240625140829256

2.把src删除,只留pom.xml

image-20240625141000578

2.微服务父模块的pom.xml配置
1.配置packaging为pom,指定编译版本,并统一指定SpringBoot版本,统一配置阿里云仓库,使子模块继承
<?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"><modelVersion>4.0.0</modelVersion><groupId>com.sun.club</groupId><artifactId>sun-club-practice</artifactId><version>1.0-SNAPSHOT</version><!-- 父模块需要配置这个pom --><packaging>pom</packaging><properties><!-- 指定编译版本 --><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><!-- 父模块统一指定SpringBoot版本 --><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.4.2</version><!-- 下面两个配置表示导入spring-boot-dependencies的dependencyManagement的版本 --><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><!-- 阿里云仓库,在父模块中配置仓库,可以使得所有子模块自动继承这个配置,这样在多模块项目中,每个模块无需单独配置仓库信息,便于管理和维护。 --><repositories><repository><id>central</id><name>aliyun maven</name><url>http://maven.aliyun.com/nexus/content/groups/public/</url><layout>default</layout><releases><enabled>true</enabled></releases><snapshots><enabled>true</enabled></snapshots></repository></repositories></project>
2.type和scope解释
1.type

<type>: 在Maven中,type元素指定了依赖项的包装类型。默认情况下,如果不指定type,Maven会假定它是一个jar文件。在你提供的示例中,type被设置为pom。这意味着被引入的依赖是一个POM类型的项目,通常用于依赖管理而非包含实际的代码库。这种类型的依赖通常用于声明一组库的版本管理,而不是作为代码库直接参与构建。

2.scope

<scope>: scope元素定义了依赖的使用范围。不同的scope值决定了依赖在项目的不同构建阶段以及不同模块间的可见性。常见的scope包括:

  • compile:默认值,表示依赖在编译阶段和运行阶段都是必需的,且会被传递到依赖的项目。
  • runtime:表示依赖不需要在编译阶段,但在运行时需要。
  • provided:表示依赖在编译和测试时需要,但在运行时不需要,因为运行环境已提供该依赖。
  • test:表示依赖仅在测试阶段需要,用于编译和运行测试代码。
  • import(正如你的例子中所用):这是一个特殊的scope,用于只在<dependencyManagement>中有效。它表示当前POM是从其他POM中导入依赖管理信息,通常用于继承和共享一组依赖定义。通过import,可以将其他项目的依赖版本管理集成到自己的项目中,从而保持依赖版本的一致性和可管理性。
3.创建一个练题微服务的api模块
1.创建一个maven项目,删除resource目录和test目录

image-20240625143011964

2.创建一个common包存放Result和Page相关的
1.目录结构

image-20240625143640667

2.PageInfo.java
package com.sunxiansheng.practice.api.common;import java.util.Objects;/*** Description: 分页请求的入参* @Author sun* @Create 2024/5/28 16:25* @Version 1.1*/
public class PageInfo {private Integer pageNo = 1;private Integer pageSize = 20;public Integer getPageNo() {return (pageNo == null || pageNo < 1) ? 1 : pageNo;}public Integer getPageSize() {return (pageSize == null || pageSize < 1) ? 20 : pageSize;}public PageInfo setPageNo(Integer pageNo) {this.pageNo = pageNo;return this;}public PageInfo setPageSize(Integer pageSize) {this.pageSize = pageSize;return this;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;PageInfo pageInfo = (PageInfo) o;return Objects.equals(pageNo, pageInfo.pageNo) &&Objects.equals(pageSize, pageInfo.pageSize);}@Overridepublic int hashCode() {return Objects.hash(pageNo, pageSize);}@Overridepublic String toString() {return "PageInfo{" +"pageNo=" + pageNo +", pageSize=" + pageSize +'}';}
}
3.PageResult.java
package com.sunxiansheng.practice.api.common;import java.util.Collections;
import java.util.List;
import java.util.Objects;/*** Description: 分页返回的实体* @Author sun* @Create 2024/5/28 16:36* @Version 1.1*/
public class PageResult<T> {// 当前页码,默认为1private Integer pageNo = 1;// 每页显示的记录数,默认为20private Integer pageSize = 20;// 总记录条数private Integer total = 0;// 总页数private Integer totalPages = 0;// 当前页的记录列表private List<T> result = Collections.emptyList();// 表示当前页是从分页查询结果的第几条记录开始,下标从1开始private Integer start = 1;// 表示当前页是从分页查询结果的第几条记录结束,下标从1开始private Integer end = 0;// ==================== 分页查询只需要设置这几个值即可 ====================// 设置当前页码,并重新计算起始和结束位置public PageResult<T> setPageNo(Integer pageNo) {this.pageNo = Objects.requireNonNull(pageNo, "Page number cannot be null");calculateStartAndEnd();return this;}// 设置每页记录数,并重新计算起始和结束位置public PageResult<T> setPageSize(Integer pageSize) {this.pageSize = Objects.requireNonNull(pageSize, "Page size cannot be null");calculateStartAndEnd();return this;}// 设置当前页的记录列表public PageResult<T> setRecords(List<T> result) {this.result = Objects.requireNonNull(result, "Result list cannot be null");return this;}// 设置总记录条数,并重新计算总页数和起始结束位置public PageResult<T> setTotal(Integer total) {this.total = Objects.requireNonNull(total, "Total count cannot be null");calculateTotalPages();calculateStartAndEnd();return this;}// ==================== 分页查询只需要设置这几个值即可 ====================// 计算总页数private void calculateTotalPages() {if (this.pageSize > 0) {this.totalPages = (this.total / this.pageSize) + (this.total % this.pageSize == 0 ? 0 : 1);} else {this.totalPages = 0;}}// 计算起始和结束位置private void calculateStartAndEnd() {if (this.pageSize > 0) {this.start = (this.pageNo - 1) * this.pageSize + 1;this.end = Math.min(this.pageNo * this.pageSize, this.total);} else {this.start = 1;this.end = this.total;}}public Integer getStart() {return start;}// 获取每页记录数public Integer getPageSize() {return pageSize;}public Integer getPageNo() {return pageNo;}public Integer getTotal() {return total;}public Integer getTotalPages() {return totalPages;}public List<T> getResult() {return result;}public Integer getEnd() {return end;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;PageResult<?> that = (PageResult<?>) o;return Objects.equals(pageNo, that.pageNo) &&Objects.equals(pageSize, that.pageSize) &&Objects.equals(total, that.total) &&Objects.equals(totalPages, that.totalPages) &&Objects.equals(result, that.result) &&Objects.equals(start, that.start) &&Objects.equals(end, that.end);}@Overridepublic int hashCode() {return Objects.hash(pageNo, pageSize, total, totalPages, result, start, end);}@Overridepublic String toString() {return "PageResult{" +"pageNo=" + pageNo +", pageSize=" + pageSize +", total=" + total +", totalPages=" + totalPages +", result=" + result +", start=" + start +", end=" + end +'}';}
}
4.Result.java
package com.sunxiansheng.practice.api.common;import lombok.Data;/*** Description:* @Author sun* @Create 2024/5/24 9:48* @Version 1.0*/
@Data
public class Result<T> {private Boolean success;private Integer code;private String message;private T data;/*** 成功返回结果* @return*/public static Result ok() {Result result = new Result();result.setSuccess(true);result.setCode(ResultCodeEnum.SUCCESS.getCode());result.setMessage(ResultCodeEnum.SUCCESS.getDesc());return result;}/*** 成功返回结果,携带数据* @param data* @return* @param <T>*/public static <T> Result ok(T data) {Result result = new Result();result.setSuccess(true);result.setCode(ResultCodeEnum.SUCCESS.getCode());result.setMessage(ResultCodeEnum.SUCCESS.getDesc());result.setData(data);return result;}/*** 失败返回结果* @return*/public static Result fail() {Result result = new Result();result.setSuccess(false);result.setCode(ResultCodeEnum.FAIL.getCode());result.setMessage(ResultCodeEnum.FAIL.getDesc());return result;}/*** 失败,携带数据* @param data* @return* @param <T>*/public static <T> Result fail(T data) {Result result = new Result();result.setSuccess(false);result.setCode(ResultCodeEnum.FAIL.getCode());result.setMessage(ResultCodeEnum.FAIL.getDesc());result.setData(data);return result;}}
5.ResultCodeEnum.java
package com.sunxiansheng.practice.api.common;import lombok.Getter;/*** Description: 返回结果枚举* @Author sun* @Create 2024/5/24 9:53* @Version 1.0*/
@Getter
public enum ResultCodeEnum {SUCCESS(200, "成功"),FAIL(500, "失败");public int code;public String desc;ResultCodeEnum(int code, String desc) {this.code = code;this.desc = desc;}/*** 根据code获取枚举* @param code* @return*/public static ResultCodeEnum getByCode(int code) {for (ResultCodeEnum value : values()) {if (value.code == code) {return value;}}return null;}
}
6.引入lombok的依赖
<dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.16</version></dependency>
</dependencies>
3.创建一个通用的枚举包
1.结构

image-20240625144145222

2.IsDeleteFlagEnum.java
package com.sunxiansheng.practice.api.enums;import lombok.Getter;/*** Description: 删除标识枚举* @Author sun* @Create 2024/5/24 9:53* @Version 1.0*/
@Getter
public enum IsDeleteFlagEnum {DELETED(1, "已删除"),UN_DELETED(0, "未删除");public int code;public String desc;IsDeleteFlagEnum(int code, String desc) {this.code = code;this.desc = desc;}/*** 根据code获取枚举* @param code* @return*/public static IsDeleteFlagEnum getByCode(int code) {for (IsDeleteFlagEnum value : values()) {if (value.code == code) {return value;}}return null;}
}
4.创建一个req和vo分别存放入参和出参实体
结构

image-20240625144432191

4.创建一个server子模块
1.创建一个maven项目

image-20240625144711871

2.创建一个config包,暂时先存放mybatis的东西
1.结构

image-20240625145214132

2.SqlStatementInterceptor.java sql状态拦截器
package com.sunxiansheng.practice.server.config.mybatis;import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.Properties;@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class,Object.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})})
public class SqlStatementInterceptor implements Interceptor {public static final Logger log = LoggerFactory.getLogger("sys-sql");@Overridepublic Object intercept(Invocation invocation) throws Throwable {long startTime = System.currentTimeMillis();try {return invocation.proceed();} finally {long timeConsuming = System.currentTimeMillis() - startTime;log.info("执行SQL:{}ms", timeConsuming);if (timeConsuming > 999 && timeConsuming < 5000) {log.info("执行SQL大于1s:{}ms", timeConsuming);} else if (timeConsuming >= 5000 && timeConsuming < 10000) {log.info("执行SQL大于5s:{}ms", timeConsuming);} else if (timeConsuming >= 10000) {log.info("执行SQL大于10s:{}ms", timeConsuming);}}}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}
3.MybatisPlusAllSqlLog.java sql转换器
package com.sunxiansheng.practice.server.config.mybatis;import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;import java.sql.SQLException;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;public class MybatisPlusAllSqlLog implements InnerInterceptor {public static final Logger log = LoggerFactory.getLogger("sys-sql");@Overridepublic void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {logInfo(boundSql, ms, parameter);}@Overridepublic void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {BoundSql boundSql = ms.getBoundSql(parameter);logInfo(boundSql, ms, parameter);}private static void logInfo(BoundSql boundSql, MappedStatement ms, Object parameter) {try {log.info("parameter = " + parameter);// 获取到节点的id,即sql语句的idString sqlId = ms.getId();log.info("sqlId = " + sqlId);// 获取节点的配置Configuration configuration = ms.getConfiguration();// 获取到最终的sql语句String sql = getSql(configuration, boundSql, sqlId);log.info("完整的sql:{}", sql);} catch (Exception e) {log.error("异常:{}", e.getLocalizedMessage(), e);}}// 封装了一下sql语句,使得结果返回完整xml路径下的sql语句节点id + sql语句public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId) {return sqlId + ":" + showSql(configuration, boundSql);}// 进行?的替换public static String showSql(Configuration configuration, BoundSql boundSql) {// 获取参数Object parameterObject = boundSql.getParameterObject();List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();// sql语句中多个空格都用一个空格代替String sql = boundSql.getSql().replaceAll("[\\s]+", " ");if (!CollectionUtils.isEmpty(parameterMappings) && parameterObject != null) {// 获取类型处理器注册器,类型处理器的功能是进行java类型和数据库类型的转换TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();// 如果根据parameterObject.getClass()可以找到对应的类型,则替换if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {sql = sql.replaceFirst("\\?",Matcher.quoteReplacement(getParameterValue(parameterObject)));} else {// MetaObject主要是封装了originalObject对象,提供了get和set的方法用于获取和设置originalObject的属性值,主要支持对JavaBean、Collection、Map三种类型对象的操作MetaObject metaObject = configuration.newMetaObject(parameterObject);for (ParameterMapping parameterMapping : parameterMappings) {String propertyName = parameterMapping.getProperty();if (metaObject.hasGetter(propertyName)) {Object obj = metaObject.getValue(propertyName);sql = sql.replaceFirst("\\?",Matcher.quoteReplacement(getParameterValue(obj)));} else if (boundSql.hasAdditionalParameter(propertyName)) {// 该分支是动态sqlObject obj = boundSql.getAdditionalParameter(propertyName);sql = sql.replaceFirst("\\?",Matcher.quoteReplacement(getParameterValue(obj)));} else {// 打印出缺失,提醒该参数缺失并防止错位sql = sql.replaceFirst("\\?", "缺失");}}}}return sql;}// 如果参数是String,则添加单引号, 如果是日期,则转换为时间格式器并加单引号; 对参数是null和不是null的情况作了处理private static String getParameterValue(Object obj) {String value;if (obj instanceof String) {value = "'" + obj.toString() + "'";} else if (obj instanceof Date) {DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT,DateFormat.DEFAULT, Locale.CHINA);value = "'" + formatter.format(new Date()) + "'";} else {if (obj != null) {value = obj.toString();} else {value = "";}}return value;}}
4.MybatisConfiguration.java 注册两个拦截器
package com.sunxiansheng.practice.server.config.mybatis;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MybatisConfiguration {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();mybatisPlusInterceptor.addInnerInterceptor(new MybatisPlusAllSqlLog());return mybatisPlusInterceptor;}}
3.引入基本依赖
<?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"><modelVersion>4.0.0</modelVersion><parent><groupId>com.sun.club</groupId><artifactId>sun-club-practice</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>sun-club-practice-server</artifactId><!-- maven的配置 --><properties><!-- 解决java: -source 1.5 中不支持 diamond 运算符 问题 --><java.version>1.8</java.version><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><!-- 版本的配置 --><spring-boot.version>2.4.2</spring-boot.version><spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version><spring-cloud.version>2020.0.6</spring-cloud.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.4.2</version><!-- 这里的日志跟log4j2冲突 --><exclusions><exclusion><artifactId>spring-boot-starter-logging</artifactId><groupId>org.springframework.boot</groupId></exclusion></exclusions></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.16</version></dependency><!-- mapstruct --><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId><version>1.4.2.Final</version></dependency><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-processor</artifactId><version>1.4.2.Final</version></dependency><!-- log4j2打印日志 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId><version>2.4.2</version></dependency><!-- fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.24</version></dependency><!-- guava本地缓存 --><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>19.0</version></dependency><!-- commons-lang3工具包,StringUtils.isNotBlank()... --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.11</version></dependency><!-- gson序列化 --><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.6</version></dependency><!-- redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.4.2</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.9.0</version></dependency><!-- mysql --><!-- jdbc --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId><version>2.4.2</version></dependency><!-- druid连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.22</version></dependency><!-- mysql8 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.22</version></dependency><!-- mybatis-plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</version></dependency><!-- mysql --><!-- nacos配置中心 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId><!--     由于上面指定了版本,会自动读取 --></dependency><!-- bootstrap --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-bootstrap</artifactId><!--     由于上面指定了版本,会自动读取 --></dependency><!-- nacos服务发现 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId><!--     由于上面指定了版本,会自动读取 --></dependency></dependencies><!-- 统一管理配置,以后所有的boot、cloud、alibaba都不需要指定版本了 --><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><!-- maven打包常规配置 --><build><finalName>${project.artifactId}</finalName><!--打包成jar包时的名字--><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><!-- 指定打包插件的版本 --><version>2.3.0.RELEASE</version><executions><execution><goals><!-- 将所有的包都打到这个模块中 --><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>
4.config下创建一个redis包,存放redis配置和工具类
1.结构

image-20240625152440231

2.RedisConfig.java
package com.sunxiansheng.practice.server.config.redis;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** Description: 原生 redis 的 template 的序列化器会产生乱码问题,重写改为 jackson* @Author sun* @Create 2024/6/5 14:16* @Version 1.0*/
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();RedisSerializer<String> redisSerializer = new StringRedisSerializer();redisTemplate.setConnectionFactory(redisConnectionFactory);redisTemplate.setKeySerializer(redisSerializer);redisTemplate.setHashKeySerializer(redisSerializer);redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());return redisTemplate;}private Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);ObjectMapper objectMapper = new ObjectMapper();objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);jsonRedisSerializer.setObjectMapper(objectMapper);return jsonRedisSerializer;}}
3.RedisUtil.java
package com.sunxiansheng.practice.server.config.redis;import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;/*** Description: RedisUtil工具类* @Author sun* @Create 2024/6/5 14:17* @Version 1.0*/
@Component
@Slf4j
public class RedisUtil {@Resourceprivate RedisTemplate redisTemplate;private static final String CACHE_KEY_SEPARATOR = ".";/*** 构建缓存key* @param strObjs* @return*/public String buildKey(String... strObjs) {return Stream.of(strObjs).collect(Collectors.joining(CACHE_KEY_SEPARATOR));}/*** 是否存在key* @param key* @return*/public boolean exist(String key) {return redisTemplate.hasKey(key);}/*** 删除key* @param key* @return*/public boolean del(String key) {return redisTemplate.delete(key);}public void set(String key, String value) {redisTemplate.opsForValue().set(key, value);}public boolean setNx(String key, String value, Long time, TimeUnit timeUnit) {return redisTemplate.opsForValue().setIfAbsent(key, value, time, timeUnit);}public String get(String key) {return (String) redisTemplate.opsForValue().get(key);}public Boolean zAdd(String key, String value, Long score) {return redisTemplate.opsForZSet().add(key, value, Double.valueOf(String.valueOf(score)));}public Long countZset(String key) {return redisTemplate.opsForZSet().size(key);}public Set<String> rangeZset(String key, long start, long end) {return redisTemplate.opsForZSet().range(key, start, end);}public Long removeZset(String key, Object value) {return redisTemplate.opsForZSet().remove(key, value);}public void removeZsetList(String key, Set<String> value) {value.stream().forEach((val) -> redisTemplate.opsForZSet().remove(key, val));}public Double score(String key, Object value) {return redisTemplate.opsForZSet().score(key, value);}public Set<String> rangeByScore(String key, long start, long end) {return redisTemplate.opsForZSet().rangeByScore(key, Double.valueOf(String.valueOf(start)), Double.valueOf(String.valueOf(end)));}/*** 可以使用这个来实现排行榜,指定键就相当于指定了一个排行榜,再指定成员和分数,则会给排行榜中的这个成员加分数* @param key zset的键* @param obj 成员,一般为用户的唯一标识* @param score 分数* @return*/public Object addScore(String key, Object obj, double score) {return redisTemplate.opsForZSet().incrementScore(key, obj, score);}public Object rank(String key, Object obj) {return redisTemplate.opsForZSet().rank(key, obj);}/*** 从 Redis 有序集合(Sorted Set)中按分数范围获取成员及其分数* @param key 排行榜的key* @param start 起始位置(包含)* @param end 结束位置(包含)* @return Set<ZSetOperations.TypedTuple<String>> : 每个 TypedTuple 对象包含以下内容:value: 集合中的成员,score: 成员的分数。*/public Set<ZSetOperations.TypedTuple<String>> rankWithScore(String key, long start, long end) {Set<ZSetOperations.TypedTuple<String>> set = redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);return set;}/*** 向Redis中的hash结构存储数据* @param key 一个hash结构的key* @param hashKey hash中的小key* @param hashVal hash中的小value*/public void putHash(String key, String hashKey, Object hashVal) {redisTemplate.opsForHash().put(key, hashKey, hashVal);}/*** Redis中的String类型,获取value时将其转换为int类型* @param key* @return*/public Integer getInt(String key) {return (Integer) redisTemplate.opsForValue().get(key);}/*** Redis中的String类型,将value增加一* @param key* @param count* @return*/public void increment(String key, Integer count) {redisTemplate.opsForValue().increment(key, count);}/*** Redis中的hash类型,根据key来将每一个hashKey和hashValue转换为Map类型* @param key* @return*/public Map<Object, Object> getHashAndDelete(String key) {Map<Object, Object> map = new HashMap<>();// 扫描hash,指定每一个Entry的类型,这里返回的就是Map的游标,可以进行遍历Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(key, ScanOptions.NONE);// 遍历每一条数据,放到map中while (cursor.hasNext()) {Map.Entry<Object, Object> next = cursor.next();Object hashKey = next.getKey();Object hashValue = next.getValue();map.put(hashKey, hashValue);// 每遍历一条就删除redisTemplate.opsForHash().delete(key, hashKey);}return map;}
}
5.config下创建登录拦截器和上下文将从Header中获取logId放到ThreadLocal中
1.结构

image-20240625153251290

2.GlobalConfig.java mvc的全局处理,空值不返回,存放自定义拦截器
package com.sunxiansheng.practice.server.config;import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.sunxiansheng.practice.server.config.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;import java.util.List;/*** mvc的全局处理*/
@Configuration
public class GlobalConfig extends WebMvcConfigurationSupport {@Overrideprotected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {super.configureMessageConverters(converters);converters.add(mappingJackson2HttpMessageConverter());}/*** 自定义mappingJackson2HttpMessageConverter* 目前实现:空值忽略,空字段可返回*/private MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {ObjectMapper objectMapper = new ObjectMapper();objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);return new MappingJackson2HttpMessageConverter(objectMapper);}/*** 将自定义拦截器放进去* @param registry*/@Overrideprotected void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor());}
}
3.LoginContextHolder.java ThreadLocal工具类
package com.sunxiansheng.practice.server.config.context;import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;/*** Description: 上下文对象(ThreadLocal)* @Author sun* @Create 2024/6/15 16:27* @Version 1.0*/
public class LoginContextHolder {// 这个ThreadLocal持有一个Mapprivate static final InheritableThreadLocal<Map<String, Object>> THREAD_LOCAL= new InheritableThreadLocal<>();/*** 为ThreadLocal持有的Map设值* @param key* @param val*/public static void set(String key, Object val) {Map<String, Object> map = getThreadLocalMap();map.put(key, val);}/*** 从ThreadLocal持有的Map取值* @param key* @return*/public static Object get(String key) {Map<String, Object> map = THREAD_LOCAL.get();return map.get(key);}/*** 清除ThreadLocal*/public static void remove() {THREAD_LOCAL.remove();}/*** 初始化一个ThreadLocal持有的Map,要保证这个Map是单例的* @return*/public static Map<String, Object> getThreadLocalMap() {// 获取到ThreadLocal的MapMap<String, Object> map = THREAD_LOCAL.get();// 如果是空的再创建一个Map,然后放进去if (Objects.isNull(map)) {map = new ConcurrentHashMap<>();THREAD_LOCAL.set(map);}// 放到ThreadLocal中return map;}// 以下为获取用户信息的方法public static String getLoginId() {return (String) getThreadLocalMap().get("loginId");}}
4.LoginInterceptor.java 登录拦截器,从Header中获取logId放到ThreadLocal
package com.sunxiansheng.practice.server.config.interceptor;import com.sunxiansheng.practice.server.config.context.LoginContextHolder;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** Description: 处理用户上下文的拦截器* @Author sun* @Create 2024/6/15 16:20* @Version 1.0*/
public class LoginInterceptor implements HandlerInterceptor {/*** 当请求到这里了,就说明,网关已经将用户的loginId放到了Header里了* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String loginId = request.getHeader("loginId");if (StringUtils.isNotBlank(loginId)) {// 将loginId放到ThreadLocal里面LoginContextHolder.set("loginId", loginId);}return true;}/*** 在操作结束后清除ThreadLocal,因为如果线程复用并且没清除,这个ThreadLocal还会存在,造成数据污染* @param request* @param response* @param handler* @param ex* @throws Exception*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {LoginContextHolder.remove();}
}
6.创建controller包
1.结构

image-20240625154206317

2.DemoController.java 测试
package com.sunxiansheng.practice.server.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** Description: 测试controller* @Author sun* @Create 2024/6/25 15:38* @Version 1.0*/
@RestController
@RequestMapping("/practice/")
@Slf4j
public class DemoController {@RequestMapping("test")public String isLogin() {return "test";}}
7.创建其余的包
1.结构

image-20240625154653794

2.DruidEncryptUtil.java 用于对yaml中的东西加解密
package com.sunxiansheng.practice.server.util;import com.alibaba.druid.filter.config.ConfigTools;import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;/*** 数据库加密util*/
public class DruidEncryptUtil {private static String publicKey;private static String privateKey;static {try {String[] keyPair = ConfigTools.genKeyPair(512);privateKey = keyPair[0];System.out.println("privateKey:" + privateKey);publicKey = keyPair[1];System.out.println("publicKey:" + publicKey);} catch (NoSuchAlgorithmException e) {e.printStackTrace();} catch (NoSuchProviderException e) {e.printStackTrace();}}public static String encrypt(String plainText) throws Exception {String encrypt = ConfigTools.encrypt(privateKey, plainText);System.out.println("encrypt:" + encrypt);return encrypt;}public static String decrypt(String encryptText) throws Exception {String decrypt = ConfigTools.decrypt(publicKey, encryptText);System.out.println("decrypt:" + decrypt);return decrypt;}public static void main(String[] args) throws Exception {String encrypt = encrypt("123456");System.out.println("encrypt:" + encrypt);}}
8.创建启动类
1.PracticeApplication.java 注意写MapperScan和ComponentScan,还有启动类注解
package com.sunxiansheng.practice.server;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;/*** Description: 练题模块* @Author sun* @Create 2024/6/25 15:48* @Version 1.0*/
@SpringBootApplication
// 扫描mapper接口
@MapperScan("com.sunxiansheng.**.dao")
// 扫描service和controller注解
@ComponentScan("com.sunxiansheng")
public class PracticeApplication {public static void main(String[] args) {SpringApplication.run(PracticeApplication.class, args);}}
9.创建配置文件
1.resource创建一个mapper文件夹

image-20240625155250318

2.application.yml
server:port: 3012# DataSource Config
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql:///sun_club?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=falseusername: rootpassword: N2THnj7YlFIA4zrfxaOq1tBpLjnG3NTOM4BL6kJMMSSoTW9xE/jNW+xjtLotTXZjKw6Jk1eDbW6BjCgTMDnTbA== # 加密后的密码type: com.alibaba.druid.pool.DruidDataSource # druid连接池druid:connectionProperties: config.decrypt=true;config.decrypt.key=${publicKey}; # 开启配置解密,读取公匙initial-size: 20 # 初始化连接数min-idle: 20 # 最小连接数max-active: 100 # 最大连接数max-wait: 60000 # 最大等待时间,单位毫秒stat-view-servlet:enabled: true # 是否开启监控url-pattern: /druid/* # 监控路径login-username:  # 登录用户名login-password:  # 登录密码filter:stat:enabled: true # 是否开启慢sql监控slow-sql-millis: 2000 # 慢sql阈值,单位毫秒log-slow-sql: true # 是否打印慢sqlwall:enabled: true # 是否开启防火墙config:enabled: true # 开启配置,可以解密redis:password:  # Redis服务器密码database: 0 # 默认数据库为0号timeout: 10000ms # 连接超时时间是10000毫秒lettuce:pool:max-active: 8 # 最大活跃连接数,使用负值表示没有限制,最佳配置为核数*2max-wait: 10000ms # 最大等待时间,单位为毫秒,使用负值表示没有限制,这里设置为10秒max-idle: 200 # 最大空闲连接数min-idle: 5 # 最小空闲连接数cluster:nodes:logging:config: classpath:log4j2-spring.xml # 日志配置文件
publicKey: 
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印sql
3.bootstrap.yml
spring:application:name: sub-club-practice # 服务名称profiles:active: dev # 激活的环境cloud:nacos:config:server-addr:  # Nacos地址prefix: ${spring.application.name} # 配置前缀为服务名,sub-club-practice-dev为配置文件名group: DEFAULT_GROUP # 配置分组namespace: # 命名空间,如果在public命名空间则不需要配置file-extension: yamldiscovery:enabled: true # 启用服务发现server-addr:  # Nacos地址
4.log4j2-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出 -->
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数 -->
<configuration monitorInterval="5"><!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --><!--变量配置 --><Properties><!-- 格式化输出:%date表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度 %msg:日志消息,%n是换行符 --><!-- %logger{36} 表示 Logger 名字最长36个字符 --><property name="LOG_PATTERN"value="%clr{%d{yyyy-MM-dd HH:mm:ss.SSS}}{faint} %clr{%5p} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n%xwEx" /><!-- 定义日志存储的路径,不要配置相对路径 --><property name="FILE_PATH" value="./logs" /><property name="FILE_NAME" value="SbTest" /></Properties><appenders><console name="Console" target="SYSTEM_OUT"><!--输出日志的格式 --><PatternLayout pattern="${LOG_PATTERN}" /><!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --><ThresholdFilter level="DEBUG" onMatch="ACCEPT"onMismatch="DENY" /></console><!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用 --><File name="Filelog" fileName="${FILE_PATH}/test.log"append="false"><PatternLayout pattern="${LOG_PATTERN}" /></File><!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 --><RollingFile name="RollingFileInfo"fileName="${FILE_PATH}/info.log"filePattern="${FILE_PATH}/${FILE_NAME}-INFO-%d{yyyy-MM-dd}_%i.log.gz"><!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --><ThresholdFilter level="info" onMatch="ACCEPT"onMismatch="DENY" /><PatternLayout pattern="${LOG_PATTERN}" /><Policies><!--interval属性用来指定多久滚动一次,默认是1 hour --><TimeBasedTriggeringPolicy interval="1" /><SizeBasedTriggeringPolicy size="10MB" /></Policies><!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖 --><DefaultRolloverStrategy max="15" /></RollingFile><!-- 这个会打印出所有的warn及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 --><RollingFile name="RollingFileWarn"fileName="${FILE_PATH}/warn.log"filePattern="${FILE_PATH}/${FILE_NAME}-WARN-%d{yyyy-MM-dd}_%i.log.gz"><!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --><ThresholdFilter level="warn" onMatch="ACCEPT"onMismatch="DENY" /><PatternLayout pattern="${LOG_PATTERN}" /><Policies><!--interval属性用来指定多久滚动一次,默认是1 hour --><TimeBasedTriggeringPolicy interval="1" /><SizeBasedTriggeringPolicy size="10MB" /></Policies><!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖 --><DefaultRolloverStrategy max="15" /></RollingFile><!-- 这个会打印出所有的error及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 --><RollingFile name="RollingFileError"fileName="${FILE_PATH}/error.log"filePattern="${FILE_PATH}/${FILE_NAME}-ERROR-%d{yyyy-MM-dd}_%i.log.gz"><!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --><ThresholdFilter level="error" onMatch="ACCEPT"onMismatch="DENY" /><PatternLayout pattern="${LOG_PATTERN}" /><Policies><!--interval属性用来指定多久滚动一次,默认是1 hour --><TimeBasedTriggeringPolicy interval="1" /><SizeBasedTriggeringPolicy size="10MB" /></Policies><!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖 --><DefaultRolloverStrategy max="15" /></RollingFile></appenders><!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。 --><!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效 --><loggers><!--过滤掉spring和mybatis的一些无用的DEBUG信息 --><logger name="org.mybatis" level="info" additivity="false"><AppenderRef ref="Console" /></logger><!--监控系统信息 --><!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。 --><Logger name="org.springframework" level="info"additivity="false"><AppenderRef ref="Console" /></Logger><root level="info"><appender-ref ref="Console" /><appender-ref ref="Filelog" /><appender-ref ref="RollingFileInfo" /><appender-ref ref="RollingFileWarn" /><appender-ref ref="RollingFileError" /></root></loggers></configuration>
10.启动测试,一次成功!

image-20240625162131168

image-20240625160254293

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/390958.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Halcon 二维匹配

匹配&#xff1a;在训练图像中呈现一个所谓的模板。系统从这个模板中派生出一个模型&#xff0c;通过使用该模型来定位于搜索图像中的相似模板对象。此方法能够处理打光、杂乱、位置和旋转变换的图像。 匹配优点&#xff1a;鲁棒性和灵活性。匹配不需要对目标进行任何形式的分割…

假期BUUCTF小练习3

文章目录 [极客大挑战 2019]BuyFlag[BJDCTF2020]Easy MD5[HCTF 2018]admin第一种方法 直接登录第二种方法 flack session伪造第三种方法Unicode欺骗 [MRCTF2020]你传你&#x1f40e;呢[护网杯 2018]easy_tornadoSSTI注入 [ZJCTF 2019]NiZhuanSiWei [极客大挑战 2019]BuyFlag 一…

vmware虚拟机linux服务器的IP需要重启才能生效问题

vmware虚拟机linux服务器的IP需要重启才能生效问题 问题说明处理办法关闭&禁用网络管理 再次重启linux服务器&#xff0c;IP显示正常 问题说明 用vmware虚拟的linux服务器&#xff0c;配置了静态IP&#xff0c;但是每次重启liunx&#xff0c;IP都不是设置的静态IP&#xf…

[GWCTF 2019]我有一个数据库1

打开题目&#xff0c;一串乱码&#xff0c;不认识的汉字 打开题目地址&#xff0c;用dirsearch对网站进行扫描&#xff0c;发现几个目录。 打开&#xff0c;其中一个显示为是一个MySQL数据库Version 4.8.1。 发现该数据给具有文件包含漏洞&#xff0c;输入以下代码验证存在 h…

java面向对象编程入门

一、前言&#xff1a; 在Java中&#xff0c;面向对象编程&#xff08;Object-Oriented Programming, OOP&#xff09;是一种核心的编程范式。Java的设计和开发都是基于面向对象的思想。面向对象编程的主要目的是提高代码的可重用性、灵活性和可维护性。以下是Java面向对象编程…

Neutralinojs教程项目实战初体验(踩坑指南),干翻 electron

Neutralinojs 项目实战初体验&#xff08;踩坑指南&#xff09;&#xff0c;干翻 electron 大家好我是csdn的red润 Neutralinojs 官方文档 卧槽卧槽&#xff0c;&#xff01;这个年轻人居然用浏览器把电脑关机了_哔哩哔哩_bilibili正是在下 本教程搭建的是纯原生项目&#x…

LeetCode每日一题_3143.正方形中的最多点数

解题思路&#xff1a;自己&#xff1a;先把points里的点拿来求出坐标系的离原点最远的点&#xff0c;然后根据最远距离新建一个数组&#xff0c;然后把points的点都在新建的数组上标记出来。然后再便利从边长为0的正方形开始&#xff0c;里面是否有重复的标签。但是代码写不出来…

vulhub-wordpress

1.打开wordpress关卡&#xff0c;选择简体中文 添加信息——点击安装WordPress 安装完成——登录 点击外观——编辑主题 可以加入一句话木马&#xff0c;但是我写入的是探针文件 也可以去上传一个带有木马的主题 上传之后会自动解压 1.php就是里面的木马文件

数据结构与算法 - 堆

1. 建堆 Floyd建堆算法作者&#xff08;也是之前龟兔赛跑判环作者&#xff09;&#xff1a; ①找到最后一个非叶子节点②从后往前&#xff0c;对每个节点执行下潜 一些规律 一棵满二叉树节点个数为2^h - 1&#xff0c;如下例中高度h 3节点数是2 ^ 3 - 1 7非叶子节点范围为…

PMP--冲刺--敏捷中的角色职责与3个工件--题干关键词

文章目录 敏 捷 中 的 角 色 职 责 与 3 个 工 件--题干关键词说明题目 敏 捷 中 的 角 色 职 责 与 3 个 工 件–题干关键词 角色职责 1、产品负责人&#xff1a;题干关键词 “优先级排序、与客户沟通、下次迭代做什么、接受或拒绝用户故事”。 2、Scrum Master&#xff1a;题…

uniapp自定义网格布局用于选择金额、输入框焦点事件以及点击逻辑实战

样式 <view class="withdraw-section"><text class="section-title">提现金额</text><view class="amount-options"><view v-for="(item, index) in list" :key="index" class="amount-opt…

学习Java的日子 Day59 学生管理系统 web1.0版本

Day59 学生管理系统 web1.0 1.项目需求 有两个角色&#xff0c;老师和学生&#xff0c;相同的功能提取到父类用户角色 2.数据库搭建 设计学生表 设计老师表 插入数据 (超级管理员) 设计学科表 3.项目搭建 处理基础页面&#xff0c;分包&#xff0c;实体类&#xff0c;导入数据…

【初学人工智能原理】【12】循环:序列依赖问题

前言 本文教程均来自b站【小白也能听懂的人工智能原理】&#xff0c;感兴趣的可自行到b站观看。 代码及工具箱 本专栏的代码和工具函数已经上传到GitHub&#xff1a;1571859588/xiaobai_AI: 零基础入门人工智能 (github.com)&#xff0c;可以找到对应课程的代码 正文 对于…

LabVIEW水下根石监测系统

开发了一种基于LabVIEW平台开发的水下根石监测系统。该系统利用高精度姿态传感器与位移传感器&#xff0c;实现了水下根石状态的实时自动监测&#xff0c;提高了水利工程安全管理的现代化和精细化水平&#xff0c;具有高精度、高稳定性和良好的操作性。 项目背景&#xff1a; …

canvas绘制文本时,该如何处理首行缩进、自动换行、多内容以省略号结束、竖排的呢?

实现如标题所示的这些文本效果&#xff0c;在css看来&#xff0c;不就是一两行css属性。然而&#xff0c;对于canvas来讲&#xff0c;要想呈现这样的文字样式&#xff0c;就没css那么轻松简便了。 既然如此&#xff0c;那为何还要使用对文本支持度不友好的canvas来绘制文字呢&a…

【开发视角】大模型 RAG 检索增强生成究竟是什么

【大白话讲懂】大模型 RAG 检索增强生成 话先说在前面&#xff0c;本文不讲不会讲太多原理&#xff0c;仅面向工程开发&#xff0c;从工作流程的宏观角度进行梳理&#xff0c;旨在快速上手。 RAG 是什么 基本定义 让我们先来解释名词&#xff0c;看看宏观框架。 RAG 的意思…

科普文:微服务之SpringBoot性能优化器动态线程池【Dynamic-Tp】特性和源码解读

一、简述 gitee地址&#xff1a;https://gitee.com/yanhom/dynamic-tp github地址&#xff1a;https://github.com/lyh200/dynamic-tp dynamic-tp是一个轻量级的动态线程池插件&#xff0c;它是一个基于配置中心的动态线程池&#xff0c;线程池的参数可以通过配置中心配置进…

C++中lambda使用mutable关键字详解

C中lambda使用mutable关键字详解 在《C初学者指南-5.标准库(第二部分)–更改元素算法》中&#xff0c;讲“generate”算法时有下面这段代码&#xff1a; auto gen [i0]() mutable { i 2; return i; }; std::vector<int> v; v.resize(7,0); generate(begin(v)1, begin…

C++ STL在算法题中的常用语法

Vector 1.将vector<int>中的元素全部置换为0 fill(vec.begin(), vec.end(), 0); 2.vector容器是可以直接用比较是否值等的&#xff01; Unordered_set 1. unordered_set的删除&#xff08;count的值也会减少&#xff09; 2.unordered_map中的int默认值是0&#xff0c;…

在Jira中使用AI

Jira已经可以使用AI功能了。 如果您使用的是Jira Cloud&#xff0c;您需要请管理员在管理页面中打开AI功能开关。&#xff08;AI功能在Standard版中未提供&#xff0c;请使用Premium或更高级的版本&#xff09;如果您使用的是自己部署的Jira Data Center&#xff0c;您需要请管…