相关推荐recommended
从0开始搭建jdk-21 springboot 3.1项目
作者:mmseoamin日期:2024-02-04
  • 从0开始搭建jdk-21 springboot项目
    • idea创建新项目
    • pom添加依赖
    • 编写程序入口
    • 添加yml配置文件
    • 添加测试类
    • 添加并测试home接口
    • 添加commons lang 依赖
    • mysql数据库操作相关
      • 添加musql-connect-java依赖
      • 在yml文件中配置数据库信息
      • 引入mybatis plus
      • 引入mybatis plus generator插件
      • 添加knife4j 4.0
        • knife4j增强配置
        • 使用mybatis plus进行分页查询
          • 自定义分页查询
          • 接口返回数据格式的相关配置
            • 设置jackjson的默认时间格式
            • long类型返回前端的精度丢失问题
            • 添加redis相关
              • 引入spring-boot-starter-data-redis
              • 引入redisson
              • 引入webflux用于请求外部接口

                从0开始搭建jdk-21 springboot项目

                idea创建新项目

                从0开始搭建jdk-21 springboot 3.1项目,IDEA new project,第1张

                pom添加依赖

                
                
                    4.0.0
                    com.glc.client
                    glc-client
                    1.0-SNAPSHOT
                    
                        org.springframework.boot
                        spring-boot-starter-parent
                        3.1.5
                        
                    
                    
                        21
                        21
                        UTF-8
                        UTF-8
                        true
                    
                    
                        
                            org.springframework.boot
                            spring-boot-starter-web
                        
                        
                            org.projectlombok
                            lombok
                            1.18.30
                            provided
                        
                    
                
                

                编写程序入口

                package com.glc.client;
                import org.springframework.boot.SpringApplication;
                import org.springframework.boot.autoconfigure.SpringBootApplication;
                @SpringBootApplication
                public class GlcClientApplication {
                    public static void main(String[] args) {
                        SpringApplication.run(GlcClientApplication.class, args);
                    }
                }
                

                然后直接启动程序就可以了,启动成功的日志如下

                  .   ____          _            __ _ _
                 /\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
                ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
                 \/  ___)| |_)| | | | | || (_| |  ) ) ) )
                  '  |____| .__|_| |_|_| |_\__, | / / / /
                 =========|_|==============|___/=/_/_/_/
                 :: Spring Boot ::                (v3.1.0)
                2023-11-12T15:19:22.634+08:00  INFO 13420 --- [           main] com.glc.client.GlcClientApplication      : Starting GlcClientApplication using Java 21.0.1 with PID 13420 (/Users/cgl/codes/glc-client/target/classes started by cgl in /Users/cgl/codes/glc-client)
                2023-11-12T15:19:22.635+08:00  INFO 13420 --- [           main] com.glc.client.GlcClientApplication      : No active profile set, falling back to 1 default profile: "default"
                2023-11-12T15:19:22.948+08:00  INFO 13420 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
                2023-11-12T15:19:22.952+08:00  INFO 13420 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
                2023-11-12T15:19:22.952+08:00  INFO 13420 --- [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.8]
                2023-11-12T15:19:22.988+08:00  INFO 13420 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
                2023-11-12T15:19:22.989+08:00  INFO 13420 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 333 ms
                2023-11-12T15:19:23.116+08:00  INFO 13420 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
                2023-11-12T15:19:23.120+08:00  INFO 13420 --- [           main] com.glc.client.GlcClientApplication      : Started GlcClientApplication in 0.622 seconds (process running for 0.858)
                

                添加yml配置文件

                1. 在resources目录下添加application.yml, application-dev.yml, application-prod.yml文件
                2. 指定一下程序的名称和端口, 在 application.yml中添加如下配置:
                spring:
                  application:
                    name: glc-client
                  profiles:
                    active: dev
                server:
                  port: 8077
                
                1. 启动程序,可以看到启动日志里面配置生效了:
                The following 1 profile is active: "dev"
                Tomcat initialized with port(s): 8077 (http)
                

                添加测试类

                1. 添加test依赖:
                
                    org.springframework.boot
                    spring-boot-starter-test
                
                

                2.在test.java.com.glc.client下面添加GlcClientApplicationTest文件,输入以下测试代码:

                package com.glc.client;
                import org.junit.jupiter.api.Test;
                import org.springframework.boot.test.context.SpringBootTest;
                @SpringBootTest
                public class GlcClientApplicationTest {
                    @Test
                    void main() {
                        System.out.println("test");
                    }
                }
                
                1. 执行,可以看到输出了test字符串,test功能可以正常运行了

                添加并测试home接口

                1. 创建controller文件夹
                2. 创建HomeController.java文件
                3. 编写接口,如下:
                package com.glc.client.controller;
                import org.springframework.web.bind.annotation.GetMapping;
                import org.springframework.web.bind.annotation.RestController;
                @RestController
                public class HomeController {
                    @GetMapping("/")
                    public String home(){
                        return "Hello World!";
                    }
                }
                
                1. 浏览器访问:http://localhost:8077,可以看到返回了"Hello World!",说明接口生效了。

                添加commons lang 依赖

                后面会用到StringUtils,这里先导入 apache commons lang依赖

                
                
                    org.apache.commons
                    commons-lang3
                    3.13.0
                
                

                mysql数据库操作相关

                添加musql-connect-java依赖

                
                
                    mysql
                    mysql-connector-java
                    8.0.33
                    runtime
                
                

                关于这个依赖的解释:


                这段代码是一个Maven依赖配置,用于指定项目依赖的MySQL JDBC驱动程序 mysql-connector-java。在这里,每个标签的含义如下:

                • : 指定依赖的组织或项目的标识符,在这个例子中是 mysql。这通常指定了开发或维护该依赖的组织。
                • : 指定具体的依赖名称,在这个例子中是 mysql-connector-java。这是该依赖在组中的唯一标识。
                • : 指定依赖的版本,这里是 8.0.33。这告诉Maven要下载并使用该特定版本的依赖。
                • : 定义依赖的范围。在这个例子中,范围被设置为 runtime,意味着这个依赖只在运行时需要,并不在编译时需要。这是因为JDBC驱动通常在运行时用来建立数据库连接,而在编译代码时不是必须的。

                  简而言之,这段配置告诉Maven项目在运行时需要使用版本为8.0.33的MySQL JDBC驱动程序。


                  在yml文件中配置数据库信息

                  在application-dev.yml添加数据库信息,同时本项目使用springboot默认的连接池hikari:

                  spring:
                    datasource:
                      driver-class-name: com.mysql.cj.jdbc.Driver
                      url: jdbc:mysql://127.0.0.1:3306/glc?useUnicode=true&allowMultiQueries=true&characterEncoding=utf8&serverTimezone=GMT%2B8
                      username: root
                      password: 123456
                      hikari:
                        minimum-idle: 5
                        idle-timeout: 600000
                        maximum-pool-size: 32
                        auto-commit: true
                        pool-name: HikariCorePool
                        max-lifetime: 1800000
                        connection-timeout: 30000
                        connection-test-query: select TABLE_NAME from information_schema.tables limit 1
                        connection-init-sql: SET NAMES utf8mb4
                  

                  在做完下面的mybatis plus相关的引入之后启动项目并第一次测试数据库查询相关接口之后,可以看到HikariCorePool - Start completed.,说明数据库连接池启动成功。

                  引入mybatis plus

                  1. 添加依赖。最新的版本在mvnrepository查看。
                  
                  
                      com.baomidou
                      mybatis-plus-boot-starter
                      3.5.4.1
                  
                  
                  1. 添加配置文件

                    创建config目录,在下面创建MybatisPlusConfig文件,写入以下内容:

                  package com.glc.client.config;
                  import com.baomidou.mybatisplus.annotation.DbType;
                  import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
                  import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
                  import org.mybatis.spring.annotation.MapperScan;
                  import org.springframework.context.annotation.Bean;
                  import org.springframework.context.annotation.Configuration;
                  @Configuration
                  @MapperScan("com.glc.client.mapper*")
                  public class MybatisPlusConfig {
                      @Bean
                      public MybatisPlusInterceptor mybatisPlusInterceptor() {
                          MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
                          PaginationInnerInterceptor innerInterceptor = new PaginationInnerInterceptor();
                          innerInterceptor.setDbType(DbType.MYSQL);
                          innerInterceptor.setMaxLimit(100000L);
                          interceptor.addInnerInterceptor(innerInterceptor);
                          return interceptor;
                      }
                  }
                  

                  引入mybatis plus generator插件

                  该插件主要用于一键生成表相关的实体类。

                  1. 引入依赖mybatis-plus-generator和velocity-engine-core
                  
                  
                      com.baomidou
                      mybatis-plus-generator
                      3.5.4.1
                  
                  
                  
                      org.apache.velocity
                      velocity-engine-core
                      2.3
                  
                  
                  1. 创建MybatisPlusGenerator

                    在com.glc.client下面创建MybatisPlusGenerator文件,写入以下内容:

                  package com.glc.client;
                  import com.baomidou.mybatisplus.annotation.FieldFill;
                  import com.baomidou.mybatisplus.annotation.IdType;
                  import com.baomidou.mybatisplus.generator.FastAutoGenerator;
                  import com.baomidou.mybatisplus.generator.config.OutputFile;
                  import com.baomidou.mybatisplus.generator.config.rules.DateType;
                  import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
                  import com.baomidou.mybatisplus.generator.engine.VelocityTemplateEngine;
                  import com.baomidou.mybatisplus.generator.fill.Column;
                  import com.baomidou.mybatisplus.generator.fill.Property;
                  import org.apache.commons.lang3.StringUtils;
                  import java.sql.Types;
                  import java.util.Collections;
                  public class MybatisPlusGenerator {
                      public static void main(String[] args) {
                          String tableName = "user"; //需要生成的类对应的表名
                          IdType idType = IdType.ASSIGN_ID; //插入数据是默认的主键id的生成算法
                          String userDir = System.getProperty("user.dir");
                          String outputDir = StringUtils.join(userDir, "/src/main/java/");
                          String mapperXmlDir = StringUtils.join(userDir, "/src/main/resources/mapper");
                          //创建代码生成器对象
                          String url = "jdbc:mysql://127.0.0.1:3306/glc?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&failOverReadOnly=false";
                          String username = "root";
                          String password = "123456";
                          FastAutoGenerator.create(url, username, password)
                                  .globalConfig(builder -> {
                                      builder.author("gulong") // 设置作者
                                              .fileOverride() // 覆盖已生成文件
                                              .commentDate("yyyy-MM-dd") // 注释里面的日期的格式
                                              .dateType(DateType.ONLY_DATE)   //定义生成的实体类中日期类型 DateType.ONLY_DATE 默认值: DateType.TIME_PACK
                                              .outputDir(outputDir); // 指定输出目录
                                  })
                                  .dataSourceConfig(builder -> {
                                      builder.schema("glc")
                                              .typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
                                          int typeCode = metaInfo.getJdbcType().TYPE_CODE;
                                          if (typeCode == Types.SMALLINT) {
                                              // 自定义类型转换
                                              return DbColumnType.INTEGER;
                                          }
                                          return typeRegistry.getColumnType(metaInfo);
                                      });
                                  })
                                  .packageConfig(builder -> {
                                      builder.parent("com.glc.client") // 设置父包名
                  //                            .moduleName("") // 设置父包模块名
                                              .controller("controller")
                                              .entity("entity")
                                              .service("service")
                                              .service("service.impl")
                                              .mapper("mapper")
                                              .pathInfo(Collections.singletonMap(OutputFile.xml, mapperXmlDir)); // 设置mapperXml生成路径
                                  })
                                  .strategyConfig(builder -> {
                                      builder.addInclude(tableName) // 设置需要生成的表名
                  //                            .addTablePrefix("t_") // 设置过滤表前缀
                                              .serviceBuilder()//service策略配置
                                              .formatServiceFileName("%sService") //去掉类名中默认的I前缀
                                              .formatServiceImplFileName("%sServiceImpl")
                                              .entityBuilder()// 实体类策略配置
                                              .idType(idType)//主键策略  这里AUTO是数据库自增ID,如果需要雪花算法生成的id可以改成ASSIGN_ID
                                              .addTableFills(new Column("create_time", FieldFill.INSERT)) // 自动填充配置
                                              .addTableFills(new Property("update_time", FieldFill.INSERT_UPDATE))
                                              .enableLombok() //开启lombok
                                              .logicDeleteColumnName("deleted")// 假删除字段
                                              .enableTableFieldAnnotation()// 自动添加表字段的注解
                                              .controllerBuilder() //controller 策略配置
                                              .formatFileName("%sController")
                                              .enableRestStyle() // 开启RestController注解
                                              .mapperBuilder()// mapper策略配置
                                              .formatMapperFileName("%sMapper")
                                              .formatXmlFileName("%sMapper");
                                  })
                                  .templateEngine(new VelocityTemplateEngine())
                                  .execute();
                      }
                  }
                  
                  1. 测试执行MybatisPlusGenerator程序

                    在上述配置中,我们指定了表为user表,点击执行,看看生成结果:

                    从0开始搭建jdk-21 springboot 3.1项目,generator生成的文件,第2张

                  这样mybatis的代码生成器就配置完成了,之后需要添加新的表只需要改一下MybatisPlusGenerator里的表名就行了,idType看实际需求选择AUTO(自增id)还是ASSIGN_ID(雪花算法生成的id)

                  添加knife4j 4.0

                  为了方便调试接口,可以引入knife4j。在knife4j官方文档中,有如下说明:

                  由于springfox长久未更新,并且Swagger2规范在目前来看,一定程度上也并未升级,规范已经全部往OpenAPI3规范靠拢,因此,在Spring Boot 3.x版本中,开发者应该选择OpenAPI3规范来作为应用框架的开发首选方案。

                  1. 引入依赖
                  
                  
                      com.github.xiaoymin
                      knife4j-openapi3-jakarta-spring-boot-starter
                      4.3.0
                  
                  
                  1. 配置文件

                    在application.yml中,添加如下配置(点此查看官方关于权限控制的说明):

                  # springdoc-openapi项目配置
                  springdoc:
                    swagger-ui:
                      path: /swagger-ui.html
                      tags-sorter: alpha
                      operations-sorter: alpha
                    api-docs:
                      path: /v3/api-docs
                    group-configs:
                      - group: 'client'
                        paths-to-match: '/**'
                        packages-to-scan: com.glc.client
                  # knife4j的增强配置,不需要增强可以不配
                  knife4j:
                    enable: true
                    setting:
                      language: zh_cn
                    production: false
                  
                  1. 用knife4j官方的例子来测试一下是否成功

                    首先在controller包里添加BodyController文件:

                  package com.glc.client.controller;
                  import com.glc.client.model.FileResp;
                  import io.swagger.v3.oas.annotations.Operation;
                  import io.swagger.v3.oas.annotations.Parameter;
                  import io.swagger.v3.oas.annotations.Parameters;
                  import io.swagger.v3.oas.annotations.enums.ParameterIn;
                  import io.swagger.v3.oas.annotations.tags.Tag;
                  import org.springframework.http.ResponseEntity;
                  import org.springframework.web.bind.annotation.*;
                  @RestController
                  @RequestMapping("body")
                  @Tag(name = "body参数")
                  public class BodyController {
                      @Operation(summary = "普通body请求")
                      @PostMapping("/body")
                      public ResponseEntity body(@RequestBody FileResp fileResp) {
                          return ResponseEntity.ok(fileResp);
                      }
                      @Operation(summary = "普通body请求+Param+Header+Path")
                      @Parameters({
                              @Parameter(name = "id", description = "文件id", in = ParameterIn.PATH),
                              @Parameter(name = "token", description = "请求token", required = true, in = ParameterIn.HEADER),
                              @Parameter(name = "name", description = "文件名称", required = true, in = ParameterIn.QUERY)
                      })
                      @PostMapping("/bodyParamHeaderPath/{id}")
                      public ResponseEntity bodyParamHeaderPath(@PathVariable("id") String id, @RequestHeader("token") String token, @RequestParam("name") String name, @RequestBody FileResp fileResp) {
                          fileResp.setName(fileResp.getName() + ",receiveName:" + name + ",token:" + token + ",pathID:" + id);
                          return ResponseEntity.ok(fileResp);
                      }
                  }
                  

                  其中FileResp如下:

                  package com.glc.client.model;
                  import lombok.Data;
                  @Data
                  public class FileResp {
                      private String name;
                      private Long fileSize;
                      private Boolean deleted;
                  }
                  

                  然后启动项目,访问:http://127.0.0.1:8077/doc.html, 看到如下界面, 说明成功了:

                  从0开始搭建jdk-21 springboot 3.1项目,knife4j主界面,第3张

                  测试接口结果如下:

                  从0开始搭建jdk-21 springboot 3.1项目,knife4j接口测试,第4张

                  至此knife4j就配置完成了,下面来看看knife的增强配置。

                  knife4j增强配置

                  首先看看官方说明:

                  在以前的版本中,开发者需要在配置文件中手动使用@EnableKnife4j来使用增强,自2.0.6版本后,只需要在配置文件中配置knife4j.enable=true即可不在使用注解

                  1. 登录认证
                  knife4j:
                    # 开启增强配置 
                    enable: true
                   # 开启Swagger的Basic认证功能,默认是false
                    basic:
                        enable: true
                        # Basic认证用户名
                        username: test
                        # Basic认证密码
                        password: 123
                  

                  目前暂时没有其他增强配置的需求。官方文档已经很详细了,有需要的话直接查阅官方文档。

                  使用mybatis plus进行分页查询

                  下面我们将使用mybatis自带的分页功能进行分页查询

                  1. 配置分页拦截器

                    之前其实已经配置过了,就是在MybatisPlusConfig文件里面,具体配置如下所示:

                  package com.glc.client.config;
                  import com.baomidou.mybatisplus.annotation.DbType;
                  import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
                  import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
                  import org.mybatis.spring.annotation.MapperScan;
                  import org.springframework.context.annotation.Bean;
                  import org.springframework.context.annotation.Configuration;
                  @Configuration
                  @MapperScan("com.glc.client.mapper*")
                  public class MybatisPlusConfig {
                      @Bean
                      public MybatisPlusInterceptor mybatisPlusInterceptor() {
                          MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
                          PaginationInnerInterceptor innerInterceptor = new PaginationInnerInterceptor();
                          innerInterceptor.setDbType(DbType.MYSQL);
                          innerInterceptor.setMaxLimit(100000L);
                          interceptor.addInnerInterceptor(innerInterceptor);
                          return interceptor;
                      }
                  }
                  
                  1. 编写一个用户表的查询接口

                    在userController中添加如下接口代码:

                  package com.glc.client.controller;
                  import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
                  import com.baomidou.mybatisplus.core.toolkit.Wrappers;
                  import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
                  import com.glc.client.entity.User;
                  import com.glc.client.service.impl.UserService;
                  import org.springframework.beans.factory.annotation.Autowired;
                  import org.springframework.http.ResponseEntity;
                  import org.springframework.web.bind.annotation.GetMapping;
                  import org.springframework.web.bind.annotation.RequestMapping;
                  import org.springframework.web.bind.annotation.RequestParam;
                  import org.springframework.web.bind.annotation.RestController;
                  /**
                   * 

                  * 前端控制器 *

                  * * @author gulong * @since 2023-11-12 */ @RestController @RequestMapping("user") public class UserController { @Autowired private UserService userService; /** * 用户列表分页查询 * * @param current 当前页数 * @param size 每页条数 * @return ResponseEntity> */ @GetMapping("/page") public ResponseEntity> userPageQuery(@RequestParam("current") Integer current, @RequestParam("size") Integer size) { Page pageParam = new Page<>(current, size); LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); queryWrapper.select(User::getId, User::getCreateTime, User::getCreateTime) .ge(User::getCreateTime, "2023-11-12"); Page pageResult = userService.page(pageParam, queryWrapper); return ResponseEntity.ok(pageResult); } }
                  1. 使用knife4j测试接口

                    接下来我们使用之前配置好的knife4j来测试user/page接口,结果如下:

                    从0开始搭建jdk-21 springboot 3.1项目,user/page接口测试,第5张

                  自定义分页查询

                  有时候我们可能对一些复杂的查询使用自定义的sql,我们可以用如下的方法进行分页查询。

                  UserController.java

                      /**
                       * 分页查询当天注册的用户信息
                       *
                       * @param current 当前页数
                       * @param size 每页条数
                       * @return user
                       */
                      @GetMapping("/today")
                      public ResponseEntity> todayUserPageQuery(@RequestParam("current") Integer current,
                             @RequestParam("size") Integer size) {
                          Page page = new Page<>(current, size);
                          String todayDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                          userMapper.selectPageVo(page, todayDate);
                          return ResponseEntity.ok(page);
                      }
                  

                  UserMapper.java

                  package com.glc.client.mapper;
                  import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
                  import com.glc.client.entity.User;
                  import com.baomidou.mybatisplus.core.mapper.BaseMapper;
                  import org.apache.ibatis.annotations.Param;
                  /**
                   * 

                  * Mapper 接口 *

                  * * @author gulong * @since 2023-11-12 */ public interface UserMapper extends BaseMapper { Page selectPageVo(@Param("page") Page page, @Param("todayDate") String todayDate); }

                  UserMapper.xml

                  
                  
                  
                      
                  
                  

                  这样就实现了自定义的分页查询。

                  接口返回数据格式的相关配置

                  设置jackjson的默认时间格式

                  在上面的接口返回结果中,我们可以看到createTime的格式对前端不友好,我们统一个改成常用的yyyy-MM-dd HH:mm:ss格式。本项目暂时未引入fastjson,在springboot中默认使用的是jackjson对接口返回的数据进行序列化,所以我们可以在yml文件中配置全局默认时间序列化格式。

                  1. 在application.yml中添加如下配置:
                  spring:
                    jackson:
                      date-format: yyyy-MM-dd HH:mm:ss
                      time-zone: GMT+8
                  
                  1. 测试接口返回时间格式

                    如下图所示,可以看到已经按我们常用的格式返回了:

                    从0开始搭建jdk-21 springboot 3.1项目,测试接口返回时间格式,第6张

                  long类型返回前端的精度丢失问题

                  在上面的结果中,id的类型为long,这种类型的数据在返回给前端的时候如果数字过大,就会产生精度丢失的问题。

                  来看看AI智能助手对于这个问题的解释:

                  JavaScript中的Number类型仅能安全地表示-253到253之间的整数(即安全整数范围),而超出此范围的大整数在JavaScript中可能无法精确表示。

                  通常对此类问题的处理方法就是把这个long序列为string。

                  本项目默认使用jackjson序列化,所以可以使用如下注解来解决:

                  @JsonSerialize(using = ToStringSerializer.class)
                  private Long id;
                  

                  如果是使用fastjson序列化,则用如下注解:

                  @JSONField(serializeUsing = ToStringSerializer.class)
                  private Long id;
                  

                  添加redis相关

                  web开发中,redis相关的操作是基本离不开的,下面将添加redis相关的一些依赖

                  引入spring-boot-starter-data-redis

                  经过对比,目前使用spring-boot-starter-data-redis是最合适的,下面开始引入。

                  拓展:点进依赖可以看到spring-boot-starter-data-redis是引入了spring-data-redis和lettuce-core,其默认使用lettuce-core客户端,线程安全

                  1. 添加依赖,

                  其中commons-pool2是为了使用连接池。许多Redis客户端库,如Jedis、Lettuce等,在实现其连接池功能时依赖于commons-pool2。这些库使用commons-pool2来管理Redis连接的创建、借用、返回和销毁。

                  
                  
                      org.springframework.boot
                      spring-boot-starter-data-redis
                  
                  
                  
                      org.apache.commons
                      commons-pool2
                  
                  
                  1. 添加redis配置信息

                    在application-dev.yml配置redis信息:

                  spring:
                    data:
                      redis:
                        host: localhost
                        port: 6379
                        password: 123456
                        database: 0
                        connect-timeout: 30000
                        timeout: 10000
                        lettuce:
                          pool:
                            min-idle: 16
                            max-idle: 32
                            max-active: 128
                            max-wait: 30000
                  
                  1. 关于redis的序列化
                  • 如果不进行配置,直接使用redisTemplate,则默认使用的jdk序列化,可读性很差,而且其他程序比如python就无法读取。
                  • 可以自定义序列化配置,但是注意最好不要使用GenericJackson2JsonRedisSerializer,这种会在redis里面存储类的信息,对于其他项目读取该数据非常不友好,

                    而且如果其他项目比如python项目设置了一个值,该项目将无法读取。

                  • 本项目选择采用stringRedisTemplate,这种就是key和value都存储为字符串,在序列化和反序列化的时候自己手动使用jackjson或者fastjson就行了
                    1. 测试

                      在test.java.com.glc.client下创建RedisTest文件,输入如下测试代码并执行

                    package com.glc.client;
                    import com.fasterxml.jackson.core.JsonProcessingException;
                    import com.fasterxml.jackson.core.type.TypeReference;
                    import com.fasterxml.jackson.databind.ObjectMapper;
                    import com.glc.client.model.FileResp;
                    import org.junit.jupiter.api.Test;
                    import org.springframework.beans.factory.annotation.Autowired;
                    import org.springframework.boot.test.context.SpringBootTest;
                    import org.springframework.data.redis.core.StringRedisTemplate;
                    import java.util.Arrays;
                    import java.util.List;
                    import java.util.stream.Collectors;
                    @SpringBootTest
                    public class RedisTest {
                        @Autowired
                        StringRedisTemplate stringRedisTemplate;
                        @Test
                        void testString() {
                            //存储/读取字符串
                            stringRedisTemplate.opsForValue().set("test-string", "redis-value");
                            String value = stringRedisTemplate.opsForValue().get("test-string");
                            System.out.printf(value);
                            stringRedisTemplate.delete("test-string");
                        }
                        @Test
                        void testJsonString() throws JsonProcessingException {
                            FileResp afile = new FileResp("afile", 123l, true);
                            FileResp bfile = new FileResp("bfile", 123l, false);
                            List list = Arrays.asList(afile, bfile);
                            stringRedisTemplate.opsForValue().set("test-json-string", new ObjectMapper().writeValueAsString(list));
                            String jsonValue = stringRedisTemplate.opsForValue().get("test-json-string");
                            System.out.printf(jsonValue);
                            List fromRedis = new ObjectMapper().readValue(jsonValue, new TypeReference<>() {
                            });
                            for (FileResp item : fromRedis) {
                                System.out.println(item);
                            }
                            stringRedisTemplate.delete("test-json-string");
                        }
                        @Test
                        void testHash() throws JsonProcessingException {
                            FileResp afile = new FileResp("afile", 123l, true);
                            FileResp bfile = new FileResp("bfile", 123l, false);
                            stringRedisTemplate.opsForHash().put("test-hash", "hash-1", new ObjectMapper().writeValueAsString(afile));
                            stringRedisTemplate.opsForHash().put("test-hash", "hash-2", new ObjectMapper().writeValueAsString(bfile));
                            String value = (String) stringRedisTemplate.opsForHash().get("test-hash", "hash-2");
                            System.out.println(value);
                            FileResp fileResp = new ObjectMapper().readValue(value, FileResp.class);
                            System.out.println(fileResp);
                            stringRedisTemplate.delete("test-hash");
                        }
                        @Test
                        void testList() {
                            FileResp afile = new FileResp("afile", 123l, true);
                            FileResp bfile = new FileResp("bfile", 123l, false);
                            List list = Arrays.asList(afile, bfile);
                            //数组里面只能是string
                            ObjectMapper objectMapper = new ObjectMapper();
                            List jsonStrings = list.stream()
                                    .map(fileResp -> {
                                        try {
                                            return objectMapper.writeValueAsString(fileResp);
                                        } catch (JsonProcessingException e) {
                                            throw new RuntimeException(e);
                                        }
                                    })
                                    .collect(Collectors.toList());
                            stringRedisTemplate.opsForList().rightPushAll("test-list", jsonStrings);
                            List range = stringRedisTemplate.opsForList().range("test-list", 0, -1);
                            System.out.println(range);
                            stringRedisTemplate.delete("test-list");
                        }
                    }
                    

                    测试结果发现序列化和反系列化都正常,至此,redis的普通操作都完成了。接下去引入redisson实现分布式锁

                    引入redisson

                    由于redisson提供了比较好的分布式锁解决方案,所以直接在项目中引入redisson

                    1. 添加依赖
                    
                    
                        org.redisson
                        redisson-spring-boot-starter
                        3.24.3
                    
                    
                    1. 配置文件
                    2. 测试

                    引入webflux用于请求外部接口

                    有时候我们需要请求第三方接口,我们有多种选择,具体选择哪个比较好,我们来看看AI智能助手的回答:

                    在Spring Boot中发送HTTP请求可以使用多种框架,每个框架都有其特点和适用场景。以下是几个常用的选项:

                    1. Spring’s RestTemplate: Spring Boot内置支持,易于集成和使用。RestTemplate提供了丰富的方法来消费RESTful服务。它包括了多种便捷的方法来远程调用HTTP服务,并可以轻松地将响应绑定到POJOs上。但从Spring 5开始,RestTemplate被标记为不推荐使用(deprecated),Spring团队推荐使用WebClient。
                    2. Spring’s WebClient: WebClient是Spring 5中引入的,它是一个更现代的、功能更全面的替代RestTemplate的选项。WebClient支持同步和异步请求,并且提供了更灵活的错误处理机制。WebClient是在Spring WebFlux中引入的,但也可以在Spring MVC应用程序中使用。
                    3. OkHttp: OkHttp是一个高效的HTTP客户端,支持同步和异步请求,提供了快速的性能和广泛的功能,如连接池、GZIP压缩和响应缓存。OkHttp适用于需要精细控制HTTP通信的场景,但它不是Spring的一部分,需要单独添加依赖和配置。
                    4. Apache HttpClient: 这是另一个流行的HTTP客户端库。它提供了丰富的功能,如连接管理、认证机制、缓存支持等。与OkHttp类似,Apache HttpClient不是Spring框架的一部分,需要额外配置。

                    选择哪个框架取决于您的具体需求:

                    • 如果您需要与Spring生态系统紧密集成,并且不介意使用反应式编程模型,可以选择WebClient。
                    • 如果您需要更精细的控制和高性能,并且不介意添加额外的依赖和配置,可以选择OkHttp或Apache HttpClient。
                    • 如果您的项目已经使用了Spring 5以下的版本,RestTemplate仍然是一个可行的选项,尽管它已被标记为不推荐使用。

                      总的来说,OkHttp是一个非常好的选择,特别是在需要高性能和定制HTTP请求的场景中。但如果您希望与Spring更紧密地集成,并利用Spring的反应式编程功能,那么WebClient可能是更好的选择。

                    基于上面的信息,本项目决定使用webflux,有以下几点考虑: