项目初始化脚手架搭建

项目初始化脚手架搭建

仓库地址

easy-web: 一个快速初始化SpringBoot项目的脚手架 (gitee.com)

目前这个项目还是个单体项目,后续笔者有时间可能会改造成父子工程项目,将通用模块抽象出来,有兴趣的小伙伴也可以自行 CV 改造。

1、项目初始化

1.1 创建一个 Spring Boot 新项目

image-20230617153047260

1.2 选择项目版本和依赖

image-20230617152358958

1.3 该配置文件为 .yml 格式

image

2、数据库表设计

用户表(注:以下字符集是 MySQL 8 版本的)

CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
  `user_account` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '登录账号',
  `user_password` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码',
  `user_name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '用户昵称',
  `user_avatar` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '用户头像',
  `gender` tinyint DEFAULT '0' COMMENT '性别:0 - 未知;1 - 男;2 -女',
  `phone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '手机号码',
  `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '邮箱',
  `user_role` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'user' COMMENT '用户角色:user - 普通用户;admin - 管理员',
  `user_status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0' COMMENT '用户状态:0 - 正常;1 - 禁用',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除:0 - 未删;1 - 已删',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `uni_userAccount` (`user_account`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='用户表';

3、Mybatis X 插件自动生成代码

1.1

image

1.2

image

1.3

取消勾选 toString

image

1.4 生成的结果

image

将需要的代码拖进对应的软件包即可,导入 mybatis-plus 依赖

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>

选项解释

image-20230423133832870

image-20230423133856231

4、定义统一错误码

这是一个枚举类,用于定义系统中可能出现的错误码和对应的错误信息。

枚举值必须是私有且不可变

可以参考下阿里巴巴 Java 开发手册:

https://github.com/alibaba/p3c

public enum ErrorCode {

    SUCCESS(0, "ok"),
  
    PARAMS_ERROR(40000, "请求参数错误"),

    NOT_LOGIN_ERROR(40100, "未登录"),

    NO_AUTH_ERROR(40101, "无权限"),
    
    NOT_FOUND_ERROR(40400, "请求的数据不存在"),
    
    FORBIDDEN_EEOR(40300, "禁止访问"),

    SYSTEM_ERROR(50000, "系统内部异常"),

    OPERTATION_ERROR(50001, "操作失败");

    /**
     * 状态码
     */
    private final int code;

    /**
     * 信息
     */
    private final String message;

    ErrorCode(int code, String message) {
        this.code = code;
        this.message = message;
    }

    /**
     * 获取错误状态码
     *
     * @return int
     */
    public int getCode() {
        return code;
    }

    /**
     * 获取错误响应信息
     *
     * @return {@code String}
     */
    public String getMessage() {
        return message;
    }

}

5、创建通用返回类

@Data
public class BaseResponse<T> implements Serializable {

    private int code; // 状态码

    private T data; // 数据

    private String message; // 响应信息

    public BaseResponse(int code, T data, String message) {
        this.code = code;
        this.data = data;
        this.message = message;
    }

    public BaseResponse(int code, T data) {
        this(code, data, "");
    }

    public BaseResponse(ErrorCode errorCode) {
        this(errorCode.getCode(), null, errorCode.getMessage());
    }
}

6、异常处理

  1. 自定义异常类
public class BusinessException extends RuntimeException {

    private final int code;

    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
    }

    public BusinessException(ErrorCode errorCode) {
        super((errorCode.getMessage()));
        this.code = errorCode.getCode();
    }

    public BusinessException(ErrorCode errorCode, String message) {
        super(message);
        this.code = errorCode.getCode();
    }

    public int getCode() {
        return code;
    }
    
}
  1. 全局异常处理
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    public BaseResponse<?> businessExceptionHandler(BusinessException e) {
        log.error("businessException: " + e.getMessage(), e);
        return ResultUtils.error(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(RuntimeException.class)
    public BaseResponse<?> runtimeExceptionHandler(RuntimeException e) {
        log.error("runtimeException", e);
        return ResultUtils.error(ErrorCode.SYSTEM_ERROR, e.getMessage());
    }
}

@RestControllerAdvice 的作用:

它结合了 @ControllerAdvice@ResponseBody 两个注解的功能,用于全局异常处理和返回统一的格式化响应数据。

当控制器中抛出异常时,@RestControllerAdvice 会捕获这些异常,并根据异常类型返回相应的数据格式。我们可以在 @ExceptionHandler 注解下定义多个异常处理方法,每个方法处理一个或多个不同类型的异常。这些方法可以返回不同类型的数据,如 JSON、视图或其他对象。

除了异常处理,@RestControllerAdvice 还可以在控制器方法执行前或执行后执行一些操作,例如记录日志、数据验证等。

7、定义用户常量

避免使用魔法值

public interface UserConstant {

    /**
     * 用户登录态键
     */
    String USER_LOGIN_STATE = "userLoginState";

    /**
     * 系统用户 id(虚拟用户)
     */
    long SYSTEM_USER_ID = 0;

    //  region 权限

    /**
     * 默认权限
     */
    String DEFAULT_ROLE = "user";

    /**
     * 管理员权限
     */
    String ADMIN_ROLE = "admin";

    // endregion
}

8、返回结果工具类

public class ResultUtils {

    /**
     * 成功
     *
     * @param data 数据
     * @return {@code BaseResponse<T>}
     */
    public static <T> BaseResponse<T> success(T data) {
        return new BaseResponse<>(0, data, "ok");
    }

    /**
     * 失败
     *
     * @param errorCode 错误码
     * @return
     */
    public static BaseResponse error(ErrorCode errorCode) {
        return new BaseResponse<>(errorCode);
    }

    /**
     * 失败
     *
     * @param code 状态码
     * @param message 信息
     * @return
     */
    public static BaseResponse error(int code, String message) {
        return new BaseResponse(code, null, message);
    }

    /**
     * 失败
     *
     * @param errorCode 错误码
     * @param message 信息
     * @return
     */
    public static BaseResponse error(ErrorCode errorCode, String message) {
        return new BaseResponse(errorCode.getCode(), null, message);
    }
}

9、分层领域模型规约

  1. DO(Data Object):此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
  2. DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。
  3. BO(Business Object):业务对象,可以由 Service 层输出的封装业务逻辑的对象。
  4. Query:数据查询对象,各层接收上层的查询请求。注意超过 2 个参数的查询封装,禁止使用 Map 类来传输。
  5. VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。

10、userAccount.intern()

userAccount.intern() 是 Java 中的字符串常量池方法,它的作用是将字符串对象添加到字符串常量池中,并返回该字符串对象在常量池中的引用。如果常量池中已经存在该字符串,则直接返回该字符串在常量池中的引用。

  • 当我们使用字符串字面量(例如:String userAccount = "admin")创建字符串时,Java 虚拟机会自动将其添加到字符串常量池中,因此这种方式创建的字符串对象在常量池中只会存在一份
  • 而当我们通过 new 关键字创建字符串时(例如:String userAccount = new String("admin")),则会在堆内存中创建一个新的字符串对象,该对象不会被添加到常量池中。

在实际开发中,我们可以使用 intern() 方法在运行时将堆中的字符串对象添加到常量池中,以便更有效地利用内存。但需要注意的是,由于常量池是在运行时被创建的,因此在使用 intern() 方法时需要注意字符串的生命周期,以避免不必要的内存消耗。

        synchronized (userAccount.intern()) {
            // 3. 加密
            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + newPassword).getBytes());
            // 4. 更新数据
            user.setUserPassword(encryptPassword);
            boolean result = this.updateById(user);
            if (!result) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "密码找回失败,数据库错误");
            }
            // 5. 返回用户id
            return user.getId();
        }

11、分页查询

userVOPage.setRecords(userVOList)

这段代码是将一个 List 列表中的元素设置到一个分页对象(Page)中的记录(records)属性中。

通过调用 userVOPagesetRecords 方法,将 userVOList 列表中的元素设置到 userVOPage 的记录属性中,从而实现了将查询结果封装成一个分页对象的功能。这样,就可以将分页查询结果返回给前端展示,方便用户进行浏览和操作。

public Page<UserVO> listUserByPage(UserQueryRequest userQueryRequest, HttpServletRequest request) {
        long current = 1; // 默认当前页为第一页
        long size = 10; // 默认每页查询10条数据

        User userQuery = new User(); // 创建用户查询对象
        if (userQueryRequest != null) { // 如果查询请求参数不为空
            BeanUtils.copyProperties(userQueryRequest, userQuery); // 将请求参数拷贝到用户查询对象中
            current = userQueryRequest.getCurrent(); // 获取当前页数
            size = userQueryRequest.getPageSize(); // 获取每页查询记录数
        }

        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>(); // 创建Lambda查询条件对象
        Page<User> userPage = this.page(new Page<>(current, size), lambdaQueryWrapper); // 分页查询用户信息
        Page<UserVO> userVOPage = new PageDTO<>(userPage.getCurrent(), userPage.getSize(), userPage.getTotal()); // 创建用户VO分页对象

        List<UserVO> userVOList = userPage.getRecords().stream().map(user -> {
            UserVO userVO = new UserVO(); // 创建用户VO对象
            BeanUtils.copyProperties(user, userVO); // 将用户信息拷贝到用户VO对象中
            return userVO;
        }).collect(Collectors.toList()); // 将用户VO对象转化为List集合

        // 将userVOList列表中的元素设置到userVOPage的记录(recodes)属性中
        userVOPage.setRecords(userVOList);
        return userVOPage;
}

12、分页插件配置

@Configuration
@MapperScan("com.chenmeng.project.mapper")
public class MyBatisPlusConfig {

    /**
     * 拦截器配置
     *
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

13、Knife4j 接口文档配置

knife4j 是为 Java MVC 框架集成 Swagger 生成 Api 文档的增强解决方案,前身是 swagger-bootstrap-ui,取名 knife4j 是希望它能像一把匕首一样小巧、轻量,并且功能强悍。其底层是对 Springfox 的封装,使用方式也和 Springfox 一致,只是对接口文档 UI 进行了优化

参考博客:

Swagger与Knife4j的学习

1、导入依赖

        <!-- https://doc.xiaominfo.com/knife4j/documentation/get_start.html-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>

2、指定 MVC 匹配路径 - yml 文件中

  mvc:
    pathmatch:
      # 指定URL路径匹配的策略--Ant--风格,默认的URL路径匹配的策略是AntPathMatcher
      matching-strategy: ANT_PATH_MATCHER

不指定的话会报错:Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException

原因:在配置 Swagger 后,

springBoot 处理映射匹配的默认策略发生了变化,Spring MVC 处理映射匹配的默认策略已从 AntPathMatcher 更改为 PathPatternParser

参考博客:

https://blog.csdn.net/Shipley_Leo/article/details/129100908

3、写配置文件

@Configuration
@EnableSwagger2
@Profile("dev")
public class Knife4jConfig {

    @Bean
    public Docket defaultApi2() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(new ApiInfoBuilder()
                        .title("project-backend")
                        .description("后端接口项目文档")
                        .version("1.0")
                        .build())
                .select()
                // 指定 Controller 扫描包路径
                .apis(RequestHandlerSelectors.basePackage("com.chenmeng.project.controller"))
                .paths(PathSelectors.any())
                .build();
    }
}

4、显示以下页面表示配置成功

image

14、全局跨域配置

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 覆盖所有请求
        registry.addMapping("/**")
                // 允许发送 Cookie
                .allowCredentials(true)
                // 放行哪些域名(必须用 patterns,否则 * 会和 allowCredentials 冲突)
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .exposedHeaders("*");
    }
}

15、自定义权限校验注解

@Target(ElementType.METHOD) // 表示该注解只能添加在方法上
@Retention(RetentionPolicy.RUNTIME) // 表示该注解在运行时可用
public @interface AuthCheck {

    /**
     * 有任何一个角色
     *
     * @return {@code String[]}
     */
    String[] anyRole() default "";

    /**
     * 必须有某个角色
     *
     * @return {@code String}
     */
    String mustRole() default "";

}

16、AOP

首先需要导入 AOP 依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

1、权限校验拦截器 AOP

@Aspect
@Component
public class AuthInterceptor {

    @Resource
    private UserService userService;

    /**
     * 执行拦截
     *
     * @param joinPoint 连接点,即被拦截到的方法
     * @param authCheck 自定义注解 - 身份验证检查
     * @return {@code Object}
     */
    @Around("@annotation(authCheck)")
    public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {
        // 获取 @AuthCheck 注解的 anyRole 属性值,并将 非空字符串 添加到列表中。
        List<String> anyRole = Arrays.stream(authCheck.anyRole()).filter(StringUtils::isNotBlank).collect(Collectors.toList());
        // 获取 @AuthCheck 注解的 mustRole 属性值
        String mustRole = authCheck.mustRole();
        // 获取当前请求的属性
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        // 获取当前请求对象
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        // 获取当前登录用户
        User user = userService.getLoginUser(request);
        // 拥有任意权限即可通过
        if (CollectionUtils.isNotEmpty(anyRole)) { // 判断 anyRole 列表是否非空
            String userRole = user.getUserRole();
            if (!anyRole.contains(userRole)) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
            }
        }
        // 必须拥有所有权限才可通过
        if (StringUtils.isNotBlank(mustRole)) { // 判断 mustRole 列表是否非空
            String userRole = user.getUserRole();
            if (!mustRole.equals(userRole)) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
            }
        }
        // 通过权限校验,放行
        return joinPoint.proceed();
    }
}

@Aspect 注解和 @Around 注解,用于拦截带有 @AuthCheck 注解的方法,进行身份验证校验。

  • @Aspect 注解表示这是一个切面类,用于定义切点和通知。
  • @Component 注解表示这个类是一个 Spring 组件,需要被 Spring 容器管理。
  • @Around("@annotation(authCheck)") 表示它拦截带有 @AuthCheck 注解的方法,并在方法执行前进行身份验证校验。

AuthCheck authCheck 表示被拦截方法上的 @AuthCheck 注解对象。

2、请求响应日志拦截器 AOP

@Aspect
@Component
@Slf4j
public class LogInterceptor {

    /**
     * 执行拦截
     *
     * @param point 连接点
     * @return {@code Object}
     * @throws Throwable throwable
     */
    @Around("execution(* com.chenmeng.project.controller.*.*(..))")
    public Object doInterceptor(ProceedingJoinPoint point) throws Throwable {
        // 计时
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        // 获取请求路径
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
        // 生成请求唯一 id
        String requestId = UUID.randomUUID().toString();
        String url = httpServletRequest.getRequestURI();
        // 获取请求参数
        Object[] args = point.getArgs();
        String reqParam = "[" + StringUtils.join(args, ", ") + "]";
        // 输出请求日志
        log.info("request start,id: {}, path: {}, ip: {}, params: {}", requestId, url,
                httpServletRequest.getRemoteHost(), reqParam);
        // 执行原方法
        Object result = point.proceed();
        // 输出响应日志
        stopWatch.stop();
        long totalTimeMillis = stopWatch.getTotalTimeMillis();
        log.info("request end, id: {}, cost: {}ms", requestId, totalTimeMillis);
        return result;
    }
}

依赖解析

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.chenmeng</groupId>
    <artifactId>easy-web</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>easy-web</name>
    <description>Spring Boot 后端项目脚手架</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <!--commons-lang3工具类-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
        <!--hutool工具类-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.18</version>
        </dependency>
        <!-- swagger + knife4j 接口文档-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>
        <!-- gson解析库 -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.9.0</version>
        </dependency>
        <!--AOP-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.1</version>
        </dependency>

        <!-- devtools热部署依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <!-- 防止将依赖传递到其他模块中 -->
            <optional>true</optional>
            <!-- 只在运行时起作用,打包时不打进去(防止线上执行打包后的程序,启动文件监听线程File Watcher,耗费大量的内存资源) -->
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

1、热部署依赖

spring-boot-devtools 提供以下功能:

  1. 自动应用程序重启:在开发应用程序时,当我们对代码进行更改并保存后,应用程序将自动重新启动,以便更快地查看更改的结果,而无需手动重启应用程序。
  2. LiveReload 支持:当我们更改 HTML、CSS、JavaScript 文件时,LiveReload 将自动重新加载浏览器页面,以便更快地查看更改的结果。
  3. 全局默认属性设置:在开发期间,我们可能需要设置一些默认属性,例如开启调试模式,禁用缓存等。spring-boot-devtools 提供了一种简单的方式来设置这些属性,以便在整个应用程序中生效。

需要注意的是,spring-boot-devtools 只应该在开发环境中使用,不应该在生产环境中使用。因为在生产环境中,自动重启和 LiveReload 可能会导致应用程序的性能下降,甚至可能会引起一些安全问题。

热部署:https://www.jianshu.com/p/de544b13b9d5

注册器参数设置解析

首先在 IDEA 中,按 Ctrl + Alt + Shift + /(或 Cmd + Option + Shift + / on macOS)打开 Registry。

  1. compiler.automake.build.while.idle.timeout 是 IntelliJ IDEA 的注册表(Registry)中的一项设置。这个设置项控制的是在系统空闲状态下,IDEA 自动构建项目的等待时间。
  2. compiler.automake.postpone.when.idle.less.than 是 IntelliJ IDEA 注册表(Registry)中的另一个设置项,它控制了在系统空闲时间小于特定值时是否延迟自动构建。

注意事项

请注意,这种自动构建可能会对系统的资源使用产生影响,尤其是在大型项目中。如果你发现系统响应变慢或者 CPU 使用率过高,可以尝试调低这个设置值或关闭自动构建功能。

2、gson 解析依赖库

Gson 是 Google 提供的一个 Java 库,用于将 Java 对象和 JSON 数据相互转换。它可以将一个 Java 对象序列化为 JSON 格式的字符串,也可以将一个 JSON 格式的字符串反序列化为一个 Java 对象。

Gson 支持 Java 中的基本数据类型、集合类型和自定义对象类型,并且可以对 Java 对象进行自定义序列化和反序列化操作。

        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.9.0</version>
        </dependency>

可以看看下面这篇文章:

Gson的基本使用

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式-stm32-基于HAL库的感应开关盖垃圾桶项目(开源)

嵌入式-stm32-感应开关盖垃圾桶项目&#xff08;开源&#xff09; 网盘资料 《嵌入式-stm32-基于HAL库的感应开关盖垃圾桶项目&#xff08;开源&#xff09;》 目录 一&#xff1a;项目概述 二&#xff1a;材料准备 三&#xff1a;细节分析&#xff08;重点&#xff09; 四&…

有详细一些的考研数学真题解析吗?

考研数学真题解析可以写得很详细&#xff0c;但是纸质资料可能受限于篇幅与排版等原因&#xff0c;没有把过程写得很详细。 但是&#xff0c;如果解析步骤不够详细的话&#xff0c;可能读者在看的时候就会因为其中某一个被省略的步骤而“卡壳”&#xff0c;进而需要花费很多额…

CAAC无人机操作证考证报名流程及白底证件照片制作方法

在这个无人机技术日新月异的时代&#xff0c;拥有一张CAAC民用无人机操作证不仅意味着你能够合法地在天空翱翔&#xff0c;也象征着你对飞行技术的尊重和对规章制度的遵守。如果你怀揣着成为无人机飞行员的梦想&#xff0c;那么&#xff0c;让我们一起揭开CAAC民用无人机操作证…

9.java——(杂例)组合,代理,向上转型static,fianl,关键字(有道云笔记复制粘贴,大家整体性的把握)

组合——内部有类&#xff08;心中有对象&#xff01;&#xff01;&#xff01;&#xff09;&#xff08;足球 和足球运动员梅西和脚下的足球一样&#xff09; has和is的区别&#xff0c;has是组合&#xff0c;是有&#xff0c;持有的意思&#xff1b;is是继承&#xff0c;是…

Vue开发中使用Element UI过程中遇到的问题及解决方案Missing required prop: “value”

一、vue中使用el-table的typeindex有时不显示序号 Table 表格 用于展示多条结构类似的数据&#xff0c;可对数据进行排序、筛选、对比或其他自定义操作。 当el-table元素中注入data对象数组后&#xff0c;在el-table-column中用prop属性来对应对象中的键名即可填入数据&…

类加载器的分类

类加载器的任务就是把 java字节码文件内容 加载到 JVM 中去 分类 类加载器一般分为两类&#xff1a; 启动类加载器&#xff1a; 这个类加载器使用C实现 是虚拟机自身的一部分&#xff0c;启动类加载器加载的内容 包括java.lang.Object、java.lang.String等在内的Java核心类库。…

uniapp微信小程序投票系统实战 (SpringBoot2+vue3.2+element plus ) -小程序端TabBar搭建

锋哥原创的uniapp微信小程序投票系统实战&#xff1a; uniapp微信小程序投票系统实战课程 (SpringBoot2vue3.2element plus ) ( 火爆连载更新中... )_哔哩哔哩_bilibiliuniapp微信小程序投票系统实战课程 (SpringBoot2vue3.2element plus ) ( 火爆连载更新中... )共计21条视频…

从0开始python学习-40.通过正则表达式/json进行接口关联

目录 1. 正则表达式&#xff1a;使用re库&#xff08;需安装-pip install re&#xff09;&#xff0c;只能提取字符串的数据。 1.1 re.seach&#xff1a;提取一个值&#xff0c;得到的是一个对象&#xff0c;通过下标group(1)取值&#xff0c;如果没有匹配到值则返回None 1.…

Vue3集成scss实现清除浏览器默认样式

1.首先去npm官网找到对应的reset.scss文件&#xff0c;复制内容在本地src下style建一个一模一样的文件&#xff0c;内容复制进去npm | Home 2.在style文件夹下再建一个index.scss文件&#xff0c;在它里边引入刚刚建好的reset.scss文件&#xff0c;如下 import ./reset.scss; …

conda虚拟环境搭建和打包,删除,移动等全流程及相关问题汇总

私人笔记无偿分享&#xff0c;更多内容请访问&#xff1a;链接&#xff1a;https://pan.baidu.com/s/19mS5N9XJ_AotF20kUwSA3w?pwdp5kx 提取码&#xff1a;p5kx 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 4.4. anaconda虚拟环境搭建&#xff1a; 网址&am…

人工智能_机器学习089_DBSCAN聚类案例_DBSCAN聚类算法效果展示_使用轮廓系数来评分DBSCAN效果---人工智能工作笔记0129

dbscan = DBSCAN(eps = 0.2,min_samples =3) 我们指定半径是0.2 然后每个圆圈至少是3个数据就可以归为一类 dbscan.fit(X) 然后进行训练 # 得到每个样本的标签,分类结果 y_ =dbscan.labels_ 然后得到结果 ,注意这里不需要进行predict,因为fit直接就相当于分类了 plt.scatte…

已解决‘ping‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。”的问题

已解决‘ping‘ 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。”的问题 文章目录 问题介绍 问题分析 解决思路 解决方法 检查并修复环境变量 进入c:\windows\system32再ping 使用系统工具修复系统文件 Q1 - 问题介绍 当您尝试在Windows命令提示符下…

GO语言笔记1-变量与基本数据类型

变量使用步骤 声明赋值使用 package main import "fmt" func main(){var age int //声明一个 int类型的变量叫ageage 18 //给变量用 赋值fmt.Println(age) //使用变量 输出变量的值 } 编译运行输出变量值 变量的四种使用方式 package main import "fmt&q…

免费在线游戏探索平台

免费在线游戏平台 免费在线游戏平台&#xff0c;上百款游戏随便玩 关于POKI 免费在线小游戏 Poki是个性化的免费在线游戏探索平台。我们每天都会为您精心挑选最新颖的游戏&#xff0c;保证您玩得尽兴&#xff01;在这里&#xff0c;您不会感到无聊&#xff0c;因为我们将根据…

rime中州韵小狼毫 help lua Translator 帮助消息翻译器

lua 是 Rime中州韵/小狼毫输入法强大的武器&#xff0c;掌握如何在Rime中州韵/小狼毫中使用lua&#xff0c;你将体验到什么叫 随心所欲。 先看效果 在 rime中州韵 输入效果一览 中的 &#x1f447; help效果 一节中&#xff0c; 我们看到了在Rime中州韵/小狼毫输入法中输入 h…

「解析」Windows 如何优雅使用 Terminal

所谓工欲善其事必先利其器&#xff0c;对于开发人员 Linux可能是首选&#xff0c;但是在家学习的时候&#xff0c;我还是更喜欢使用 Windows系统&#xff0c;首先是稳定&#xff0c;其次是习惯了。当然了&#xff0c;我还有一台专门安装 Linux系统的小主机用于学习Linux使用&am…

解决在test以外的目录下导入junit无效

以上引用来自src目录下的文件&#xff0c;可以看到&#xff0c;和junit有关的导入都飘红&#xff0c;但明明junit已经被正确导入进了项目中。 再看右侧的Maven的依赖下方&#xff0c;junit的右边有一个很不起眼的(test) 这是因为junit作为测试框架&#xff0c;可能包含仅适用于…

整除判断-判断正整数a能否被b整除,如果不能整除,输出商和余数 C语言xdoj42

问题描述 判断正整数a能否被b整除&#xff0c;如果不能整除&#xff0c;输出商和余数 输入说明 输入两个正整数a和b&#xff08;0<a, b<10000&#xff09;&#xff0c;a和b之间用空格分隔。 输出说明 如果a能被b整除&#xff0c;输出yes&#xff0c;否则在同…

mysql四大引擎、账号管理以及建库

目录 一.数据库存储引擎1.1存储引擎的查看1.2InnoDB1.3MyISAM1.4 MEMORY1.5 Archive 二.数据库管理2.1元数据库分类2.2 操作2.3 MySQL库 三.数据表管理3.1三大范式3.2 整形3.3 实数3.4 字符串3.5 text&blob3.6 日期类型3.7 选中标识符 四.数据库账号管理4.1 查询用户4.2查看…

锂电池寿命预测 | Matlab基于LSTM长短期记忆神经网络的锂电池寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 锂电池寿命预测 | Matlab基于LSTM长短期记忆神经网络的锂电池寿命预测 程序设计 完整程序和数据获取方式&#xff1a;私信博主回复Matlab基于LSTM长短期记忆神经网络的锂电池寿命预测。 参考资料 [1] http://t.csdn…