赞
踩
1)配置繁琐
2)依赖繁琐
SpringBoot的自动配置是一个运行时(更准确的说,是应用程序启动时)的过程,考虑了众多因素,才决定Spring配置应该用哪个,不应该用哪个。该过程是SpringBoot自动完成的。
起步依赖本质上是一个Maven项目对象模型,定义了对其他库的传递依赖,这些东西加在一起即支持某项目功能
提供了一些大型项目中常见的非功能性特征,如嵌入式服务器、安全、指标、健康检查、外部配置。
*总结:SpringBoot提供了一种快速开发Spring项目的方式,而不是对Spring的功能上的增强。
下载慢可以配置代理:
- //继承父工程
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.7.3</version>
- </parent>
- //导入起步依赖
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
*总结:
1、在spring-boot-starter-parent中定义了各种技术的版本信息,组合了一套最优搭配的技术版本
2、在各种starter中,定义了完成该功能需要的坐标合集,其中大部分版本信息来自于父工程
3、我们的工程继承parent,引入starter后,通过依赖传递,就可以简单方便获取需要的jar包,并且不会存在版本冲突等问题。
SpringBoot 是基础约定熟成的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用application.properties或者application.yml(application.yaml)进行配置。
server.port=8080
- server:
- port: 8080
-
- #对象(map):键值对的集合
- person:
- name: zhangsan
- person: {name: zhangsan}#行内写法
-
- #数组:一组按次序排列的值
- address:
- - bejing
- - shanghai
- address: [beijing,shanghai]#行内写法
-
- #纯量:单个的、不可再分的值
- msg1: 'hello \n world' #单引号忽略转义字符
- msg2: "hello \n world" #双引号识别转义字符
-
-
- #参数引用
- name:zhgangsan
- person:
- name: ${name} #引用上边定义的name值

*总结:在同一级目录下优先级为:properties>yml>yaml
1)@Value
- @Value("${name}")//name要和配置文件的名字一致
- private String name;
-
- @Value("${person.name}")
- private String name2;
-
- @Value("${person.age}")
- private int age;
-
- @Value("${address[0]}")
- private int address1;
-
- @Value("${address[1]}")
- private int address1;
2)Environment
- @Autowired
- private Emvironment env
-
-
- //获取
-
- System.out.println(env.getProperty('preson.name'));
- System.out.println(env.getProperty('address[0]'));
3)@ConfigurationProperties
- @ConfigurationProperties(prefix = "person")//前缀指定那个变量
- public class Person {
-
- private String name;
-
- private int age;
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-

-
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-configuration-processor</artifactId>
- <optional>true</optional>
- </dependency>
profile 可以让 Spring 对不同的环境提供不同配置的功能,可以通过激活、指定参数等方式快速切换环境。 换句话说,就是我们需要在不同的场景下使用不同的配置,profile的出现就是要解决我们多环境下切换配置复杂的问题。
在实际开发环境中,我们存在开发环境的配置,部署环境的配置,测试环境的配置等等,里面的配置信息很多时,例如:端口、上下文路径、数据库配置等等,若每次切换环境时,我们都需要进行修改这些配置信息时,会比较麻烦,profile的出现就是为了解决这个问题。
语法规则:application-{profile}.properties(.yaml/.yml)
如果需要创建自定义的的properties文件时,可以用application-xxx.properties/yml的命名方式,这也是官方提供的,其中xxx可以根据自己的需求来定义。
测试案例:创建application-dev.yml和application-prod.yml两个配置文件,指定不同的端口
- # 开发环境
- jdbc:
- driverclass: com.mysql.jdbc.Driver
- url: jdbc:mysql://localhost:3306/java79?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
- username: zhangsan
- password: 123456
- # 测试环境
- jdbc:
- driverclass: com.mysql.jdbc.Driver
- url: jdbc:mysql://localhost:3306/java79?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
- username: test
- password: test
如果需要在两种环境下进行切换,只需要在application.yml中加入如下内容即可
- spring:
- profiles:
- active: dev
application.properties:主配置文件 application-dev.properties:开发环境配置文件 application-test.properties:测试环境配置文件 application-prod.properties:生产环境配置文件
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-devtools</artifactId>
- <optional>true</optional>
- <scope>runtime</scope>
- </dependency>
- spring:
- #热部署配置
- devtools:
- restart:
- enabled: true #设置开启热部署
- additional-paths: src/main/java #重启目录
- exclude: WEB-INF/**
- freemarker:
- cache: false #页面不加载缓存修改及时生效
- <!--文档框架 springboot2 + Openapi3-->
- <dependency>
- <groupId>com.github.xiaoymin</groupId>
- <artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
- <version>4.3.0</version>
- </dependency>
- # 文档框架配置
- springdoc:
- swagger-ui:
- path: /swagger-ui.html
- tags-sorter: alpha
- operations-sorter: alpha
- show-extensions: true
- api-docs:
- path: /v3/api-docs
- group-configs:
- - group: 'default'
- paths-to-match: '/**'
- packages-to-scan: com.xxgc.demo.controller #包扫描路径
- default-flat-param-object: false
- knife4j:
- enable: true
- setting:
- language: zh_cn
- swagger-model-name: 实体类列表
- basic:
- enable: true #进入文档需要输入密码
- username: admin
- password: 123456

- package com.xxgc.demo.config;
-
- import io.swagger.v3.oas.models.Components;
- import io.swagger.v3.oas.models.OpenAPI;
- import io.swagger.v3.oas.models.info.Info;
- import io.swagger.v3.oas.models.info.License;
- import io.swagger.v3.oas.models.media.StringSchema;
- import io.swagger.v3.oas.models.parameters.HeaderParameter;
- import io.swagger.v3.oas.models.security.SecurityRequirement;
- import io.swagger.v3.oas.models.security.SecurityScheme;
- import org.springdoc.core.GroupedOpenApi;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.http.HttpHeaders;
-
-
- /**
- * @Author:SJY
- * @Date :2023/9/5 - 09 - 05 - 16:35
- * thanksjava@qq.com
- */
- @Configuration
- public class SwaggerConfig {
- /**
- * 根据@Tag 上的排序,写入x-order
- *
- * @return the global open api customizer
- */
- /* @Bean
- public GlobalOpenApiCustomizer orderGlobalOpenApiCustomizer() {
- return openApi -> {
- if (openApi.getTags()!=null){
- openApi.getTags().forEach(tag -> {
- Map<String,Object> map=new HashMap<>();
- map.put("x-order", RandomUtil.randomInt(0,100));
- tag.setExtensions(map);
- });
- }
- if(openApi.getPaths()!=null){
- openApi.addExtension("x-test123","333");
- openApi.getPaths().addExtension("x-abb",RandomUtil.randomInt(1,100));
- }
- };
- }*/
-
- @Bean
- public GroupedOpenApi userApi(){
- String[] paths = { "/**" };
- String[] packagedToMatch = { "com.xiaominfo.knife4j.demo.web" };
- return GroupedOpenApi.builder().group("用户模块")
- .pathsToMatch(paths)
- .addOperationCustomizer((operation, handlerMethod) -> {
- return operation.addParametersItem(new HeaderParameter().name("groupCode").example("测试").description("集团code").schema(new StringSchema()._default("BR").name("groupCode").description("集团code")));
- })
- .packagesToScan(packagedToMatch).build();
- }
- @Bean
- public OpenAPI customOpenAPI() {
- return new OpenAPI()
- .info(new Info()
- .title("XXX用户系统API")
- .version("1.0")
-
- .description( "Knife4j集成springdoc-openapi示例")
- .termsOfService("http://doc.xiaominfo.com")
- .license(new License().name("Apache 2.0")
- .url("http://doc.xiaominfo.com"))
- ).addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION))
- .components(new Components().addSecuritySchemes(HttpHeaders.AUTHORIZATION,new SecurityScheme()
- .name(HttpHeaders.AUTHORIZATION).type(SecurityScheme.Type.HTTP).scheme("bearer")));
- }
-
-
- }

@Api(tags = "类上的描述")
@ApiOperation(value = "方法的描述", notes = "接口的描述")
@ApiModel(value = "模型的描述", description = "")
@ApiModelProperty("字段的描述")
- @Operation(summary = "获取信息测试",description = "测试用")
-
-
-
- @Parameters({
- @Parameter(name = "uname",description = "用户名",required = true,example = "admin"),
- @Parameter(name = "pword",description = "密码",required = true,example = "123"),
- })
-
- @Schema(description = "系统内部状态码")
- <!--lombok 自动生成set、get、有参、无参、toString等常用方法-->
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <version>1.18.28</version>
- <scope>provided</scope>
- </dependency>
LomBok
- /**
- * 项目统一返回结果对象
- * lombok 自动生成set get方法
- * @Data set+get+tostring 方法
- * @AllArgsConstructor 全参构造方法
- * @NoArgsConstructor 无参构造方法
- */
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public class Result<T> {
-
- @Schema(description = "系统内部状态码")
- private int code;
-
- @Schema(description = "系统信息")
- private String msg;
-
- @Schema(description = "返回数据")
- private T data;

-
- <dependency>
- <groupId>cn.hutool</groupId>
- <artifactId>hutool-all</artifactId>
- <version>5.8.16</version>
- </dependency>
HutoolHutool 官方文档https://doc.hutool.cn/
- package com.xgd.demo.controller.result;
-
- import com.fasterxml.jackson.annotation.JsonInclude;
- import io.swagger.v3.oas.annotations.media.Schema;
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
-
- /**
- * 项目统一返回结果对象
- * lombok 自动生成set get方法
- * @Data set+get+tostring 方法
- * @AllArgsConstructor 全参构造方法
- * @NoArgsConstructor 无参构造方法
- */
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- //如果值为空,在转json时就移除
- @JsonInclude(JsonInclude.Include.NON_NULL)
- public class Result<T> {
-
- @Schema(description = "系统内部状态码")
- private int code;
-
- @Schema(description = "系统信息")
- private String msg;
-
- @Schema(description = "返回数据")
- private T data;
-
- //--------------改变部分信息-----------------
- public Result msg(String msg){
- this.msg = msg;
- return this;
- }
- public Result code(int code){
- this.code = code;
- return this;
- }
-
- //请求成功的返回
- public static <T> Result<T> ok(T data){
- return new Result(200,"请求成功",data);
- }
- //请求成功的返回 不带参数
- public static <T> Result<T> ok(){
- return new Result(200,"请求成功",null);
- }
-
- //请求失败的返回 不带参数
- public static <T> Result<T> error(){
- return new Result(500,"请求失败",null);
- }
- //请求失败的返回 异常拦截使用
- public static <T> Result<T> error(int code,T data){
- return new Result(code,"请求失败",data);
- }
- }

- package com.xxgc.demo.controller.error;
-
- import com.xxgc.demo.controller.result.Result;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.ResponseEntity;
- import org.springframework.http.converter.HttpMessageNotReadableException;
- import org.springframework.validation.BindException;
- import org.springframework.validation.ObjectError;
- import org.springframework.web.bind.MethodArgumentNotValidException;
- import org.springframework.web.bind.annotation.ControllerAdvice;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- import org.springframework.web.bind.annotation.ResponseBody;
-
- import javax.validation.ConstraintViolation;
- import javax.validation.ConstraintViolationException;
- import javax.validation.ValidationException;
- import java.util.List;
- import java.util.stream.Collectors;
-
- /**
- * @Author: SJY
- * @Date :2023/9/8 - 09 - 08 - 14:37
- * 全局异常拦截器 @ControllerAdvice
- */
- @ControllerAdvice
- @ResponseBody
- public class GlobalExceptionHandler {
-
- //参数校验的拦截
- @ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class})
- public ResponseEntity<Result> handleValidatedException(Exception e) {
- Result<List<String>> result = null;
- if (e instanceof MethodArgumentNotValidException) {
- MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e;
- List<String> errorMessages = ex.getBindingResult().getAllErrors().stream()
- .map(ObjectError::getDefaultMessage)
- .collect(Collectors.toList());
- result = Result.error(HttpStatus.BAD_REQUEST.value(), errorMessages);
- } else if (e instanceof ConstraintViolationException) {
- ConstraintViolationException ex = (ConstraintViolationException) e;
- List<String> errorMessages = ex.getConstraintViolations().stream()
- .map(ConstraintViolation::getMessage)
- .collect(Collectors.toList());
- result = Result.error(HttpStatus.BAD_REQUEST.value(), errorMessages);
- } else if (e instanceof BindException) {
- BindException ex = (BindException) e;
- List<String> errorMessages = ex.getAllErrors().stream()
- .map(ObjectError::getDefaultMessage)
- .collect(Collectors.toList());
- result = Result.error(HttpStatus.BAD_REQUEST.value(), errorMessages);
- }
- return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
- }
-
- //参数转换
- @ExceptionHandler(value = {HttpMessageNotReadableException.class})
- public ResponseEntity<Result> httpMessageNotReadableException(Exception e) {
- return new ResponseEntity<>(Result.error().msg("参数类型转换失败").errorMsg(e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
- }
-
-
- /**------------------------ 一定写在最下面 ----------------------------------**/
- /**------------------@TODO 开发环境和测试环境不用 只在生产环境用 留坑 (虚竹)-------------------**/
- //运行时的异常
- @ExceptionHandler(value = {Exception.class})
- public ResponseEntity<Result> runException(Exception e) {
- return new ResponseEntity<>(Result.error().msg("当前服务器压力大,请稍后重试"), HttpStatus.INTERNAL_SERVER_ERROR);
- }
-
- }

- @Valid: 该注解用于指示一个参数或请求体需要进行验证。可以将该注解添加到参数上,表示该参数需要进行验证。
- @NotNull: 该注解用于指示一个参数不能为null。如果参数值为null,则会抛出一个异常。
- @NotEmpty: 该注解用于指示一个参数不能为空。如果参数值为空,则会抛出一个异常。
- @Size(min=, max=): 该注解用于指示一个参数的长度必须在指定范围内。其中,min表示参数长度的最小值,max表示参数长度的最大值。
- @Pattern(regex=, flags=): 该注解用于指示一个参数的值必须匹配指定的正则表达式。其中,regex表示正则表达式,flags表示正则表达式的标志。
- @Min(value=): 该注解用于指示一个参数的值必须大于等于指定的最小值。
- @Max(value=): 该注解用于指示一个参数的值必须小于等于指定的最大值。
- @DecimalMin(value=): 该注解用于指示一个参数的值必须大于等于指定的最小十进制值。
- @DecimalMax(value=): 该注解用于指示一个参数的值必须小于等于指定的最大十进制值。
- @Email(message=): 该注解用于指示一个参数的值必须是一个有效的电子邮件地址。如果参数值不是有效的电子邮件地址,则会抛出一个异常,并且可以在message中指定异常消息。
- @EqualTo(referringParamName=): 该注解用于指示一个参数的值必须等于另一个参数的值。其中,referringParamName表示另一个参数的名称。
- <!--SpringAOP的包-->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-aop</artifactId>
- </dependency>
-
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
-
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.RUNTIME)
- public @interface CheckIdCard {
-
- }
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.annotation.Aspect;
- import org.springframework.stereotype.Component;
-
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- import java.util.Date;
-
- @Aspect
- @Component
- public class IdCardInterceptor {
-
- private SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
-
- @Around("execution(* com.yourpackage..*(..)) && args(idCard)")'execution(* com.yourpackage..*(..))' 是一个方法匹配器,表示这个环绕建议应用于com.yourpackage包及其子包中所有的方法。'args(idCard)' 是一个参数匹配器,表示这个环绕建议只应用于含有idCard参数的方法
- //@Around("@annotation(CheckIdCard)")表示这个方法会在带有CheckIdCard注解的方法被调用之前执行
- public Object checkIdCard(ProceedingJoinPoint joinPoint, String idCard) throws ParseException {
- try {
- Date birthDate = sdf.parse(idCard.substring(6, 14)); // 假设身份证号的出生日期在从左起第6到14位
- Date currentDate = new Date();
- if (currentDate.after(birthDate)) {
- throw new RuntimeException("身份证号出生日期不能在当前日期之前");
- }
- } catch (ParseException e) {
- throw new RuntimeException("身份证号格式不正确", e);
- }
- return joinPoint.proceed();
- }
- }

- import java.lang.annotation.Documented;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- import javax.validation.Constraint;
- import javax.validation.Payload;
-
- @Documented
- @Constraint(validatedBy = {IdCardBirthdayBeforeCurrentDateValidator.class})//用于指定一个或多个验证器来验证类或字段的约束。在这个例子中,它指定了 IdNoValidator 类作为验证器来验证该类或字段的约束。具体来说,它可以用于验证身份证号码、账号、手机号码等各种类型的标识号码。具体的验证逻辑需要在 IdNoValidator 类中实现。
- @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
- @Retention(RetentionPolicy.RUNTIME)
- public @interface IdCardBirthdayBeforeCurrentDate {
- String message() default "身份证号码的出生日期不能在当前日期之前";
-
- Class<?>[] groups() default {};
-
- Class<? extends Payload>[] payload() default {};
-
- String regexp() default "^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$";
- }

在这个注解类中,我们定义了一个regexp属性,用于存储身份证号码的正则表达式。这个正则表达式可以匹配所有的有效身份证号码,包括出生日期在当前日期之前的身份证号码。
- import javax.validation.ConstraintValidator;
- import javax.validation.ConstraintValidatorContext;
- import java.time.LocalDate;
- import java.time.format.DateTimeFormatter;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
-
- public class IdCardBirthdayBeforeCurrentDateValidator implements ConstraintValidator<IdCardBirthdayBeforeCurrentDate, String> {
- private String regexp;
-
- @Override
- public void initialize(IdCardBirthdayBeforeCurrentDate constraintAnnotation) {
- this.regexp = constraintAnnotation.regexp();
- }
-
- @Override
- public boolean isValid(String value, ConstraintValidatorContext context) {
- if (value == null || value.isEmpty()) {
- return true;
- }
-
- Pattern pattern = Pattern.compile(regexp);
- Matcher matcher = pattern.matcher(value);
- if (!matcher.matches()) {
- return false;
- }
-
- //int year = Integer.parseInt(idNo.substring(6, 10));
- //int month = Integer.parseInt(idNo.substring(10, 12));
- //int day = Integer.parseInt(idNo.substring(12, 14));
-
- // Calendar calendar = Calendar.getInstance();
- //calendar.set(year, month, day);
- //return calendar.getTime().before(new Date());
-
- String birthdayStr = value.substring(6, 14);
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
- LocalDate birthday = LocalDate.parse(birthdayStr, formatter);
- LocalDate currentDate = LocalDate.now();
- return birthday.isBefore(currentDate);
- }
- }

在这个验证器类中,我们实现了isValid方法,用于检查身份证号码的出生日期是否在当前日期之前。我们首先检查身份证号码是否符合正则表达式。如果不符合,则返回false。如果符合,则将出生日期转换为LocalDate类型,并与当前日期进行比较。如果出生日期在当前日期之前,则返回true,否则返回false。
- import javax.validation.constraints.Pattern;
-
- public class User {
- @Pattern(regexp = "^[1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$", message = "身份证号码的出生日期不能在当前日期之前")
- private String idCard;
-
- // getters and setters
- }
ConstraintValidator
接口是Hibernate Validator库中的一个重要组件,用于实现自定义的验证规则。这些规则可以应用于Java Bean中的字段,以进行各种复杂的数据验证。 3)ConstraintValidator
接口有两个主要方法:initialize
和isValid
。initialize
方法:此方法在验证器创建时被调用,可以用于执行任何必要的初始化工作,例如,加载配置信息、初始化状态等。isValid
方法:此方法用于执行实际的验证逻辑。它接收两个参数:一个是待验证的实体对象(即要进行验证的Java Bean),另一个是ConstraintValidatorContext
对象,它可以提供额外的上下文信息,如字段名称、字段索引等。通过这个方法,你可以实现自定义的验证逻辑,例如检查数据是否满足特定的格式、范围、业务规则等- <dependency>
- <groupId>com.auth0</groupId>
- <artifactId>java-jwt</artifactId>
- <version>3.4.0</version>
- </dependency>
JWT工具-JWTUtil | HutoolHutool 官方文档https://doc.hutool.cn/pages/JWTUtil/#%E4%BD%BF%E7%94%A8
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-boot-starter</artifactId>
- <version>最新版本</version>
- </dependency>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>8.0.26</version>
- </dependency>
-
- spring:
- # 配置数据源信息
- datasource:
- # 配置数据源类型
- type: com.zaxxer.hikari.HikariDataSource
- # 配置连接数据库信息
- driver-class-name: com.mysql.cj.jdbc.Driver
- url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
- username: root
- password: 123456
- # MyBatis Plus配置
- mybatis-plus:
- # 搜索指定包别名
- typeAliasesPackage: com.ruoyi.**.domain
- # 配置mapper的扫描,找到所有的mapper.xml映射文件
- mapperLocations: classpath*:mapper/**/*Mapper.xml
- configLocation:
- classpath:mybatis/mybatis-config.xml # 加载全局的配置文件
- log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #是否打印sql语句

- <!-- redis 缓存操作 -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
- # redis 配置
- redis:
- # 地址
- host: localhost
- # 端口,默认为6379
- port: 6379
- # 数据库索引
- database: 0
- # 密码
- password:
- # 连接超时时间
- timeout: 10s
- lettuce:
- pool:
- # 连接池中的最小空闲连接
- min-idle: 0
- # 连接池中的最大空闲连接
- max-idle: 8
- # 连接池的最大数据库连接数
- max-active: 8
- # #连接池最大阻塞等待时间(使用负值表示没有限制)
- max-wait: -1ms

- @Configuration
- public class RedisConfig {
-
- @Bean
- public RedisSerializer<Object> redisSerializer() {
- //创建JSON序列化器
- Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
- ObjectMapper objectMapper = new ObjectMapper();
- objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
- objectMapper.configure(MapperFeature.USE_ANNOTATIONS, false);
- objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
- objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
- //解决localDateTime的序列化问题
- objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
- objectMapper.registerModule(new JavaTimeModule());
- objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
- //必须设置,否则无法将JSON转化为对象,会转化成Map类型
- objectMapper.disableDefaultTyping();
- objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
- //objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);
- serializer.setObjectMapper(objectMapper);
- return serializer;
- }
-
- @Bean
- public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
- RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
- //设置Redis缓存有效期为1天
- RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
- .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1));
- return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
- }
-
-
- @Bean
- @ConditionalOnMissingBean
- public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
- RedisSerializer<Object> serializer = redisSerializer();
- RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
- redisTemplate.setConnectionFactory(redisConnectionFactory);
- redisTemplate.setKeySerializer(new StringRedisSerializer());
- redisTemplate.setValueSerializer(serializer);
- redisTemplate.setHashKeySerializer(new StringRedisSerializer());
- redisTemplate.setHashValueSerializer(serializer);
- redisTemplate.afterPropertiesSet();
- return redisTemplate;
- }
- }

- @Component
- public class RedisUtil {
- @Resource
- private RedisTemplate<String, Object> redisTemplate;
- // =============================Common 基础============================
- /**
- * 指定缓存失效时间
- *
- * @param key 键
- * @param time 时间(秒)
- */
- public boolean expire(String key, long time) {
- try {
- if (time > 0) {
- redisTemplate.expire(key, time, TimeUnit.SECONDS);
- }
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * 根据key 获取过期时间
- *
- * @param key 键 不能为null
- * @return 时间(秒) 返回0代表为永久有效
- */
- public long getExpire(String key) {
- // 如果返回值为 null,则返回 0L
- return redisTemplate.getExpire(key, TimeUnit.SECONDS);
- }
- /**
- * 判断key是否存在
- *
- * @param key 键
- * @return true 存在 false不存在
- */
- public boolean hasKey(String key) {
- try {
- return Boolean.TRUE.equals(redisTemplate.hasKey(key));
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * 删除缓存
- *
- * @param key 可以传一个值 或多个
- */
- @SuppressWarnings("unchecked")
- public void del(String... key) {
- if (key != null && key.length > 0) {
- if (key.length == 1) {
- redisTemplate.delete(key[0]);
- } else {
- redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
- }
- }
- }
- // ============================String 字符串=============================
- /**
- * 普通缓存获取
- *
- * @param key 键
- * @return 值
- */
- public Object get(String key) {
- return key == null ? null : redisTemplate.opsForValue().get(key);
- }
- /**
- * 普通缓存放入
- *
- * @param key 键
- * @param value 值
- * @return true成功 false失败
- */
- public boolean set(String key, Object value) {
- try {
- redisTemplate.opsForValue().set(key, value);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * 普通缓存放入并设置时间
- *
- * @param key 键
- * @param value 值
- * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
- * @return true成功 false 失败
- */
- public boolean set(String key, Object value, long time) {
- try {
- if (time > 0) {
- redisTemplate.opsForValue().set(key, value, time,
- TimeUnit.SECONDS);
- } else {
- set(key, value);
- }
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * 递增
- *
- * @param key 键
- * @param delta 要增加几(大于0)
- */
- public long incr(String key, long delta) {
- if (delta < 0) {
- throw new RuntimeException("递增因子必须大于0");
- }
- return redisTemplate.opsForValue().increment(key, delta);
- }
- /**
- * 递减
- *
- * @param key 键
- * @param delta 要减少几(小于0)
- */
- public long decr(String key, long delta) {
- if (delta < 0) {
- throw new RuntimeException("递减因子必须大于0");
- }
- return redisTemplate.opsForValue().increment(key, -delta);
- }
- // ===============================List 列表=================================
- /**
- * 获取list缓存的内容
- *
- * @param key 键
- * @param start 开始
- * @param end 结束 0 到 -1代表所有值
- */
- public List<Object> lGet(String key, long start, long end) {
- try {
- return redisTemplate.opsForList().range(key, start, end);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
- /**
- * 获取list缓存的长度
- *
- * @param key 键
- */
- public long lGetListSize(String key) {
- try {
- return redisTemplate.opsForList().size(key);
- } catch (Exception e) {
- e.printStackTrace();
- return 0;
- }
- }
- /**
- * 通过索引 获取list中的值
- *
- * @param key 键
- * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0
- * 时,-1,表尾,-2倒数第二个元素,依次类推
- */
- public Object lGetIndex(String key, long index) {
- try {
- return redisTemplate.opsForList().index(key, index);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
- /**
- * 将list放入缓存
- *
- * @param key 键
- * @param value 值
- */
- public boolean lSet(String key, Object value) {
- try {
- redisTemplate.opsForList().rightPush(key, value);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * 将list放入缓存
- *
- * @param key 键
- * @param value 值
- * @param time 时间(秒)
- */
- public boolean lSet(String key, Object value, long time) {
- try {
- redisTemplate.opsForList().rightPush(key, value);
- if (time > 0) {
- expire(key, time);
- }
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * 将list放入缓存
- *
- * @param key 键
- * @param value 值
- * @return 赋值结果
- */
- public boolean lSet(String key, List<Object> value) {
- try {
- redisTemplate.opsForList().rightPushAll(key, value);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * 将list放入缓存
- *
- * @param key 键
- * @param value 值
- * @param time 时间(秒)
- * @return 赋值结果
- */
- public boolean lSet(String key, List<Object> value, long time) {
- try {
- redisTemplate.opsForList().rightPushAll(key, value);
- if (time > 0) {
- expire(key, time);
- }
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * 根据索引修改list中的某条数据
- *
- * @param key 键
- * @param index 索引
- * @param value 值
- * @return 赋值结果
- */
- public boolean lUpdateIndex(String key, long index, Object value) {
- try {
- redisTemplate.opsForList().set(key, index, value);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * 移除N个值为value
- *
- * @param key 键
- * @param count 移除多少个
- * @param value 值
- * @return 移除的个数
- */
- public long lRemove(String key, long count, Object value) {
- try {
- return redisTemplate.opsForList().remove(key, count, value);
- } catch (Exception e) {
- e.printStackTrace();
- return 0;
- }
- }
- // ============================Set 集合=============================
- /**
- * 根据key获取Set中的所有值
- *
- * @param key 键
- */
- public Set<Object> sGet(String key) {
- try {
- return redisTemplate.opsForSet().members(key);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
- /**
- * 根据value从一个set中查询,是否存在
- *
- * @param key 键
- * @param value 值
- * @return true 存在 false不存在
- */
- public boolean sHasKey(String key, Object value) {
- try {
- return redisTemplate.opsForSet().isMember(key, value);
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * 将数据放入set缓存
- *
- * @param key 键
- * @param values 值 可以是多个
- * @return 成功个数
- */
- public long sSet(String key, Object... values) {
- try {
- return redisTemplate.opsForSet().add(key, values);
- } catch (Exception e) {
- e.printStackTrace();
- return 0;
- }
- }
- /**
- * 将set数据放入缓存
- *
- * @param key 键
- * @param time 时间(秒)
- * @param values 值 可以是多个
- * @return 成功个数
- */
- public long sSetAndTime(String key, long time, Object... values) {
- try {
- Long count = redisTemplate.opsForSet().add(key, values);
- if (time > 0) {
- expire(key, time);
- }
- return count;
- } catch (Exception e) {
- e.printStackTrace();
- return 0;
- }
- }
- /**
- * 获取set缓存的长度
- *
- * @param key 键
- */
- public long sGetSetSize(String key) {
- try {
- return redisTemplate.opsForSet().size(key);
- } catch (Exception e) {
- e.printStackTrace();
- return 0;
- }
- }
- /**
- * 移除值为value的
- *
- * @param key 键
- * @param values 值 可以是多个
- * @return 移除的个数
- */
- public long setRemove(String key, Object... values) {
- try {
- return redisTemplate.opsForSet().remove(key, values);
- } catch (Exception e) {
- e.printStackTrace();
- return 0;
- }
- }
- // ================================Hash 哈希=================================
- /**
- * HashGet
- *
- * @param key 键 不能为null
- * @param item 项 不能为null
- */
- public Object hget(String key, String item) {
- return redisTemplate.opsForHash().get(key, item);
- }
- /**
- * 获取hashKey对应的所有键值
- *
- * @param key 键
- * @return 对应的多个键值
- */
- public Map<Object, Object> hmget(String key) {
- return redisTemplate.opsForHash().entries(key);
- }
- /**
- * HashSet
- *
- * @param key 键
- * @param map 对应多个键值
- */
- public boolean hmset(String key, Map<String, Object> map) {
- try {
- redisTemplate.opsForHash().putAll(key, map);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * HashSet 并设置时间
- *
- * @param key 键
- * @param map 对应多个键值
- * @param time 时间(秒)
- * @return true成功 false失败
- */
- public boolean hmset(String key, Map<String, Object> map, long time) {
- try {
- redisTemplate.opsForHash().putAll(key, map);
- if (time > 0) {
- expire(key, time);
- }
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * 向一张hash表中放入数据,如果不存在将创建
- *
- * @param key 键
- * @param item 项
- * @param value 值
- * @return true 成功 false失败
- */
- public boolean hset(String key, String item, Object value) {
- try {
- redisTemplate.opsForHash().put(key, item, value);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * 向一张hash表中放入数据,如果不存在将创建
- *
- * @param key 键
- * @param item 项
- * @param value 值
- * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
- * @return true 成功 false失败
- */
- public boolean hset(String key, String item, Object value, long time) {
- try {
- redisTemplate.opsForHash().put(key, item, value);
- if (time > 0) {
- expire(key, time);
- }
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
- /**
- * 删除hash表中的值
- *
- * @param key 键 不能为null
- * @param item 项 可以使多个 不能为null
- */
- public void hdel(String key, Object... item) {
- redisTemplate.opsForHash().delete(key, item);
- }
- /**
- * 判断hash表中是否有该项的值
- *
- * @param key 键 不能为null
- * @param item 项 不能为null
- * @return true 存在 false不存在
- */
- public boolean hHasKey(String key, String item) {
- return redisTemplate.opsForHash().hasKey(key, item);
- }
- /**
- * hash递增 如果不存在,就会创建一个 并把新增后的值返回
- *
- * @param key 键
- * @param item 项
- * @param by 要增加几(大于0)
- */
- public double hincr(String key, String item, double by) {
- return redisTemplate.opsForHash().increment(key, item, by);
- }
- /**
- * hash递减
- *
- * @param key 键
- * @param item 项
- * @param by 要减少记(小于0)
- */
- public double hdecr(String key, String item, double by) {
- return redisTemplate.opsForHash().increment(key, item, -by);
- }
- }

- //web层 打通 service层
- @Autowired
- private IDemoUsersService iDemoUsersService;
- @Autowired
- private RedisUtil redisUtil;//注入redis工具类
-
-
- @GetMapping("/getUserInfoById")
- public Result getUserInfoById(Long userId){
- String key = "user"+userId;
- //缓存是否有这个对象
- boolean b = redisUtil.hasKey(key);
- if(b){//如果有
- log.info("我是缓存中获取的");
- DemoUsers demoUsers = (DemoUsers) redisUtil.get(key);
- return Result.ok(demoUsers);
- }else{//如果缓存没有
- log.info("我是数据库获取的");
- DemoUsers demoUsers = iDemoUsersService.getById(userId);
- //往缓存中丢一份
- redisUtil.set(key,demoUsers);
- return Result.ok(demoUsers);
- }
- }

- @cacheable(cacheNames="userInfo",key="#userId",unless="#userId == 1",condition="#userId == 1")
- //缓存组件名、 key名、 userId=1 时不使用缓存、userId=2 时使用缓存
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。