ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

SpringBoot整合SpringCache

2021-04-19 00:01:34  阅读:185  来源: 互联网

标签:缓存 SpringCache SpringBoot userVo value 整合 import StatusEnum com


SpringCache

为什么要学习SpringCache,他解决了什么问题?

SpringCache是Spring3.1版本发布的,他是对使用缓存进行封装和抽象,通过在方法上使用annotation注解就能拿到缓存结果,正式因为用了annotation,所以它解决了业务代码和缓存代码的耦合度问题,即再不入侵业务代码的基础上让现有代码即刻支持缓存,让开发者无感知redis的存在
(对于redis缓存,springcache只支持string.其他的Hash,List,Set,ZSet都不支持)

开始

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

<!--springboot的redis支持-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
#服务配置
server:
  #端口
  port: 8081
  servlet:
    #项目路径
    context-path: /platform
  #服务编码
  tomcat:
    uri-encoding: UTF-8
#spring相关配置
spring:
  #应用配置
  application:
    #应用名称
    name: springboot-day01-basic-project
  #数据源配置
  datasource:
    #选择druid数据源
    druid:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/spring-boot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
      username: root
      password: root
  redis:
    host: 127.0.0.1 #Redis服务器地址
    port: 6379#Redis服务器连接端口
    timeout: 2000 #请求redis服务的超时时间,这里注意设置成0时取默认时间2000
    jedis: #阻塞的
      pool:
        #连接池最大连接数(使用负值表示没有限制)
        #建议为业务期望QPS/一个连接的QPS,例如50000/1000=50
        #一次命令时间(borrow|return resource+Jedis执行命令+网络延迟)的平均耗时约为1ms,一个连接的QPS大约是1000
        max-active: 50
        #连接池中的最大空闲连接
        #建议和最大连接数一致,这样做的好处是连接数从不减少,从而避免了连接池伸缩产生的性能开销。
        max-idle: 50
        #连接池中的最小空闲连接
        #建议为0,在无请求的状况下从不创建链接
        min-idle: 0
        #连接池最大阻塞等待时间 毫秒(-1表示没有限制)
        #建议不要为-1,连接池占满后无法获取连接时将在该时间内阻塞等待,超时后将抛出异常。
        max-wait: 2000
#mubatis配置
mybatis-plus:
  # MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名
  type-aliases-package: com.springboot.pojo
  # 该配置请和 typeAliasesPackage 一起使用,如果配置了该属性,则仅仅会扫描路径下以该类作为父类的域对象 。
  type-aliases-super-type: com.springboot.basic.BasicPojo
  configuration:
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
    log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl
    # 驼峰下划线转换
    map-underscore-to-camel-case: true
    use-generated-keys: true
    default-statement-timeout: 60
    default-fetch-size: 100
  global-config:
    db-config:
      #主键类型(雪花ID)
      id-type: assign_id
    #机器 ID 部分(影响雪花ID)
    worker-id: 1
    #数据标识 ID 部分(影响雪花ID)
    datacenter-id: 1
logging:
  config: classpath:logback.xml

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.itheima.springboot.serializer.KryoRedisSerializer;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;

import java.time.Duration;

/**
 * @ClassName RedisCacheConfig.java
 * @Description redis配置
 */
@Configuration
@EnableCaching
public class RedisCacheConfig {


    /**
     * 申明缓存管理器,会创建一个切面(aspect)并触发Spring缓存注解的切点(pointcut)
     * 根据类或者方法所使用的注解以及缓存的状态,这个切面会从缓存中获取数据,将数据添加到缓存之中或者从缓存中移除某个值

     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // KryoRedisSerializer 替换默认序列化
//        KryoRedisSerializer kryoRedisSerializer = new KryoRedisSerializer(Object.class);

        // 配置序列化(解决乱码的问题)
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(60))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer))
                .disableCachingNullValues();

        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(config)
                .build();
    }
}

- @Cacheable:触发缓存写入。
- @CacheEvict:触发缓存清除。
- @CachePut:更新缓存(不会影响到方法的运行)。
- @Caching:重新组合要应用于方法的多个缓存操作
- @CacheConfig:设置类级别上共享的一些常见缓存设置

@Cacheable注解

​ 如果缓存中没有:查询数据库,存储缓存,返回结果,如果缓存中有:直接返回结果

​ 作用:可以用来进行缓存的写入,将结果存储在缓存中,以便于在后续调用的时候可以直接返回缓存中的值,而不必再执行实际的方法。 最简单的使用方式,注解名称=缓存名称,使用例子如下:
在这里插入图片描述

@CacheEvict注解

@CacheEvict:删除缓存的注解,这对删除旧的数据和无用的数据是非常有用的。这里还多了一个参数(allEntries),设置allEntries=true时,可以对整个条目进行批量删除
在这里插入图片描述

@CachePut注解

@CachePut:当需要更新缓存而不干扰方法的运行时 ,可以使用该注解。也就是说,始终执行该方法,并将结果放入缓存
在这里插入图片描述

@Caching注释

在使用缓存的时候,有可能会同时进行更新和删除,会出现同时使用多个注解的情况.而@Caching可以实现

//添加user缓存的同时,移除userPage的缓存
@Caching(put =@CachePut(value = "user",key ="#userVo.id"),
		evict = @CacheEvict(value = "userPage",allEntries = true))
@CacheConfig注解

缓存提供了许多的注解选项,但是有一些公用的操作,我们可以使用@CacheConfig在类上进行全局设置。 以下是个简单的例子

@CacheConfig("books") 
public class BookRepositoryImpl implements BookRepository {

    @Cacheable
    public Book findBook(ISBN isbn) {...}
} 


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.springboot.basic.ResponseWrap;
import com.itheima.springboot.enums.StatusEnum;
import com.itheima.springboot.exception.ProjectException;
import com.itheima.springboot.pojo.User;
import com.itheima.springboot.service.IUserService;
import com.itheima.springboot.utils.BeanConv;
import com.itheima.springboot.utils.EmptyUtil;
import com.itheima.springboot.utils.ExceptionsUtil;
import com.itheima.springboot.vo.UserVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.web.bind.annotation.*;

import java.util.Date;

/**
 * @Description:前端控制器
 */
@RestController
@RequestMapping("/springboot/user")
@Api(tags = "用户操作")
@Log4j2
@CrossOrigin
public class UserController {

    @Autowired
    IUserService userService;

    /***
     * @description 注册用户
     * @param userVo 注册信息
     * @return: java.lang.Boolean
     */
    @PostMapping
    @ApiOperation(value = "用户注册",notes = "用户注册")
    @ApiImplicitParam(name = "userVo",value = "注册信息",required = true,dataType = "UserVo")
    @Caching(put =@CachePut(value = "user",key ="#result.data.id"),evict = @CacheEvict(value = "userPage",allEntries = true))
    public ResponseWrap<UserVo> saveUser(@RequestBody UserVo userVo) throws ProjectException {
        try {
            User user = BeanConv.toBean(userVo, User.class);
            userService.save(user);
            BeanConv.toBean(user, userVo);
            return ResponseWrap.<UserVo>builder()
                    .code(StatusEnum.SUCCEED.getCode())
                    .msg(StatusEnum.SUCCEED.getMsg())
                    .operationTime(new Date())
                    .data(userVo)
                    .build();
        }catch (Exception e){
            log.error("用户注册:{}", ExceptionsUtil.getStackTraceAsString(e));
            throw new ProjectException(StatusEnum.REGISTER_USER_FAIL.getCode(),
                    StatusEnum.REGISTER_USER_FAIL.getMsg());
        }
    }

    /***
     * @description 删除用户
     * @param userId 用户Id
     * @return: java.lang.Boolean
     */
    @DeleteMapping("{userId}")
    @ApiOperation(value = "删除用户",notes = "删除用户")
    @ApiImplicitParam(name = "userId",value = "删除用户",required = true,dataType = "Long")
    @Caching(evict = {@CacheEvict(value = "user",key ="#userId"),@CacheEvict(value = "userPage",allEntries = true)})
    public ResponseWrap<Boolean> deleteUser(@PathVariable("userId") Long userId) throws ProjectException {
        try {
            Boolean flag = userService.removeById(userId);
            return ResponseWrap.<Boolean>builder()
                    .code(StatusEnum.SUCCEED.getCode())
                    .msg(StatusEnum.SUCCEED.getMsg())
                    .operationTime(new Date())
                    .data(flag)
                    .build();
        }catch (Exception e){
            log.error("删除用户:{}", ExceptionsUtil.getStackTraceAsString(e));
            throw new ProjectException(StatusEnum.DELETE_USER_FAIL.getCode(),
                    StatusEnum.DELETE_USER_FAIL.getMsg());
        }
    }

    /***
     * @description 编辑用户
     * @param userVo 注册信息
     * @return: java.lang.Boolean
     */
    @PutMapping
    @ApiOperation(value = "编辑用户",notes = "编辑用户")
    @ApiImplicitParam(name = "userVo",value = "编辑用户",required = true,dataType = "UserVo")
    @Caching(put =@CachePut(value = "user",key ="#userVo.id"),evict = @CacheEvict(value = "userPage",allEntries = true))
    public ResponseWrap<Boolean> updateUser(@RequestBody UserVo userVo) throws ProjectException {
        try {
            Boolean flag = userService.saveOrUpdate(BeanConv.toBean(userVo, User.class));
            return ResponseWrap.<Boolean>builder()
                    .code(StatusEnum.SUCCEED.getCode())
                    .msg(StatusEnum.SUCCEED.getMsg())
                    .operationTime(new Date())
                    .data(flag)
                    .build();
        }catch (Exception e){
            log.error("查询用户:{}", ExceptionsUtil.getStackTraceAsString(e));
            throw new ProjectException(StatusEnum.UPDATE_USER_FAIL.getCode(),
                    StatusEnum.UPDATE_USER_FAIL.getMsg());
        }
    }

    /***
     * @description 查询用户
     * @param userId 用户Id
     * @return: java.lang.Boolean
     */
    @GetMapping("/{userId}")
    @ApiOperation(value = "查询用户",notes = "查询用户")
    @ApiImplicitParam(name = "userId",value = "查询用户",required = true,dataType = "Long")
    @Cacheable(value = "user",key ="#userId")
    public ResponseWrap<UserVo> findUserById(@PathVariable("userId") Long userId) throws ProjectException {
        try {
            User user = userService.getById(userId);
            return ResponseWrap.<UserVo>builder()
                    .code(StatusEnum.SUCCEED.getCode())
                    .msg(StatusEnum.SUCCEED.getMsg())
                    .operationTime(new Date())
                    .data(BeanConv.toBean(user,UserVo.class))
                    .build();
        }catch (Exception e){
            log.error("查询用户:{}", ExceptionsUtil.getStackTraceAsString(e));
            throw new ProjectException(StatusEnum.FIND_USER_FAIL.getCode(),
                    StatusEnum.FIND_USER_FAIL.getMsg());
        }
    }

    /***
     * @description 分页查询用户
     * @param current 当前页面
     * @param size 每页条数
     * @return: java.lang.Boolean
     */
    @PostMapping("{current}/{size}")
    @ApiOperation(value = "分页查询用户",notes = "查询用户")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "current",value = "当前页码",required = true,dataType = "Long"),
            @ApiImplicitParam(name = "size",value = "每页条数",required = true,dataType = "Long"),
            @ApiImplicitParam(name = "userVo",value = "用户条件",required = true,dataType = "UserVo")
    })
    @Cacheable(value = "userPage",key ="'current_'+#current+'_userVo_'+#userVo.hashCode()")
    public ResponseWrap<Page<UserVo>> findUserPage(@PathVariable("current") Long current,
                                                   @PathVariable("size") Long size,
                                                   @RequestBody UserVo userVo) throws ProjectException {
        try {
            //构建分页
            Page<User> page = new Page<>(current,size);
            //查询条件
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            if (!EmptyUtil.isNullOrEmpty(userVo.getLoginName())) {
                queryWrapper.likeRight(StringUtils.camelToUnderline(User.Fields.loginName),userVo.getLoginName());
            }
            if (!EmptyUtil.isNullOrEmpty(userVo.getMobil())) {
                queryWrapper.likeRight(StringUtils.camelToUnderline(User.Fields.mobil),userVo.getMobil());
            }
            if (!EmptyUtil.isNullOrEmpty(userVo.getEnableFlag())) {
                queryWrapper.eq(StringUtils.camelToUnderline(User.Fields.enableFlag),userVo.getEnableFlag());
            }
            //执行查询
            Page<User> userPageResult = userService.page(page, queryWrapper);
            Page<UserVo> userVoPageResult = new Page<>();
            BeanConv.toBean(userPageResult,userVoPageResult);
            userVoPageResult.setRecords(BeanConv.toBeanList(userPageResult.getRecords(),UserVo.class));
            return ResponseWrap.<Page<UserVo>>builder()
                    .code(StatusEnum.SUCCEED.getCode())
                    .msg(StatusEnum.SUCCEED.getMsg())
                    .operationTime(new Date())
                    .data(userVoPageResult)
                    .build();
        }catch (Exception e){
            log.error("用户注册:{}", ExceptionsUtil.getStackTraceAsString(e));
            throw new ProjectException(StatusEnum.FIND_USER_FAIL.getCode(),
                    StatusEnum.FIND_USER_FAIL.getMsg());
        }
    }

}

标签:缓存,SpringCache,SpringBoot,userVo,value,整合,import,StatusEnum,com
来源: https://blog.csdn.net/weixin_49553416/article/details/115800206

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有