SpringBoot与缓存(默认缓存组件以及redis缓存组件)

SpringBoot与缓存

1、JSR107 缓存规范

1.1、Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry和 Expiry.

  • CachingProvider定义子创建、配置、获取、管理和控制多个CacheManager,一个应用程序可以在运行期访问多个CachingProvider
  • CacheManager定义了创建、配置,获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。
  • Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。
  • Entry是一个存储在Cache中的key-value对。
  • Expiry每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。

2、Spring 缓存抽象

2.1、Spring从3.1开始定义了org.springframework.cache.Cache,和org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache (JSR-107)

  • Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
  • Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache ,ConcurrentMapCache等;
  • 每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
  • 已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
    • 确定方法需要被缓存以及他们的缓存策略
    • 从缓存中读取之前缓存存储的数据

2.2 、重要的缓存注解

Cache 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等
CacheManager 爱存管理器,管理各种缓存(Cache)组件
@Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
@CacheEvict @CacheEvict
@CachePut 保证方法被调用,又希望结果被缓存。
@EnableCaching 开启基于注解的缓存
keyGenerator 缓存数据时key生成策略
serialize 缓存数据时value序列化策略

3、创建项目(使用默认的缓存)

3.1、pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>Cloud</artifactId>
        <groupId>com.riest</groupId>
        <version>1.0.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>SpringBoot-Cache</artifactId>
    <dependencies>


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

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

	<!--一以下配置用来执行mybatis-generator-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.2</version>
                <configuration>
                    <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                    <verbose>true</verbose>
                    <overwrite>true</overwrite>
                </configuration>
                <executions>
                    <execution>
                        <id>Generate MyBatis Artifacts</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.2</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

3.2、yml

数据库用的postgresql

server:
  port: 9090


spring:
  application:
    name: SpringBootCache

  datasource:
    url: jdbc:postgresql://xxx:xxx/xxx?useUnicode=true&characterEncoding=utf8&useSSL=true
    username: xxx
    password: xxx
    driver-class-name: org.postgresql.Driver
    druid:
      # 连接池配置
      initial-size: 1
      max-active: 20
      min-idle: 1
      max-wait: 10000
      pool-prepared-statements: true
      max-open-prepared-statements: 20
      validation-query: SELECT 'x'
      validation-query-timeout: 5000
      test-on-borrow: false
      test-on-return: false
      test-while-idle: true
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 30000
      max-evictable-idle-time-millis: 60000
      removeAbandoned: true
      removeAbandonedTimeout: 1800
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      max-pool-prepared-statement-per-connection-size: 20
      filters: stat,wall #filters: #配置多个英文逗号分隔(统计,sql注入,log4j过滤)
    type: com.alibaba.druid.pool.DruidDataSource


# mybatis 配置
mybatis:
  mapper-locations: classpath:mapper/*/*.xml
  type-aliases-package: com.riest.modal
  configuration:
    #mabatis打印sql
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
debug: true

3.3、druid配置

package com.riest.config.druid;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.ResourceServlet;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * ClassName:DruidConf
 * Describe:
 * Author:DGJ
 * Data:2020/11/23 14:34
 */
@Slf4j
@Configuration
public class DruidConf {

//    @Value("${spring.datasource.publicKey}")
//    private String publicKey;


    @Value("${spring.datasource.url}")
    private String dbUrl;

    @Value("${spring.datasource.username}")
    private String username;

    @Value("${spring.datasource.password}")
    private String password;

    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;

    @Value("${spring.datasource.druid.initial-size}")
    private int initialSize;

    @Value("${spring.datasource.druid.min-idle}")
    private int minIdle;

    @Value("${spring.datasource.druid.max-active}")
    private int maxActive;

    @Value("${spring.datasource.druid.max-wait}")
    private int maxWait;

    @Value("${spring.datasource.druid.time-between-eviction-runs-millis}")
    private int timeBetweenEvictionRunsMillis;

    @Value("${spring.datasource.druid.min-evictable-idle-time-millis}")
    private int minEvictableIdleTimeMillis;

    @Value("${spring.datasource.druid.validation-query}")
    private String validationQuery;

    @Value("${spring.datasource.druid.test-while-idle}")
    private boolean testWhileIdle;

    @Value("${spring.datasource.druid.test-on-borrow}")
    private boolean testOnBorrow;

    @Value("${spring.datasource.druid.test-on-return}")
    private boolean testOnReturn;

    @Value("${spring.datasource.druid.pool-prepared-statements}")
    private boolean poolPreparedStatements;

    @Value("${spring.datasource.druid.max-pool-prepared-statement-per-connection-size}")
    private int maxPoolPreparedStatementPerConnectionSize;

    @Value("${spring.datasource.druid.filters}")
    private String filters;

    @Value("{spring.datasource.druid.connection-properties}")
    private String connectionProperties;


    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() throws SQLException {
        DruidDataSource datasource = new DruidDataSource();
//        datasource.setFilters("config");
//        datasource.setConnectionProperties("config.decrypt=true;config.decrypt.key=" + publicKey);

//        Properties conkey = new Properties();
//        conkey.setProperty("config.decrypt","true");
//        conkey.setProperty("config.decrypt.key",publicKey);

        datasource.setUrl(dbUrl);
        datasource.setUsername(username);
        datasource.setPassword(password);
        datasource.setDriverClassName(driverClassName);
        //configuration
        datasource.setInitialSize(initialSize);
        datasource.setMinIdle(minIdle);
        datasource.setMaxActive(maxActive);
        datasource.setMaxWait(maxWait);
        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        datasource.setValidationQuery(validationQuery);
        datasource.setTestWhileIdle(testWhileIdle);
        datasource.setTestOnBorrow(testOnBorrow);
        datasource.setTestOnReturn(testOnReturn);
        datasource.setPoolPreparedStatements(poolPreparedStatements);
        datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);

        try {
            datasource.setFilters(filters);
        } catch (Exception e) {
            log.error("druid configuration initialization filter", e);
        }
        datasource.setConnectionProperties(connectionProperties);

        return datasource;
    }

        /**
         * 主要实现web监控的配置处理
         * @return
         */
    @Bean
    @ConditionalOnMissingBean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        //白名单:
        servletRegistrationBean.addInitParameter("allow","127.0.0.1");
        //IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.
        servletRegistrationBean.addInitParameter("deny","192.168.6.73");
        //登录查看信息的账号密码, 用于登录Druid监控后台
        servletRegistrationBean.addInitParameter("loginUsername", "admin");
        servletRegistrationBean.addInitParameter("loginPassword", "admin");
        //是否能够重置数据.
        servletRegistrationBean.addInitParameter("resetEnable", "true");
        return servletRegistrationBean;
    }

    /**
     *  监控
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new WebStatFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }

}

3.4、用mybatis-generator 生成sql、mapper、modal

该步骤省略

3.5、Service

package com.riest.service.device;

import com.riest.dao.device.PowerControllerdeviceMapper;
import com.riest.model.device.PowerControllerdevice;
import com.riest.model.device.PowerControllerdeviceAll;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.awt.SunHints;


import javax.annotation.Resource;
import java.util.List;

/**
 * ClassName:TermService
 * Describe:
 * Author:DGJ
 * Data:2020/11/6 17:01
 *
 * 默认使用的是 ConcurrentMapCacheManager ,将数据保存在  ConcurrentMap<String, Cache>
 */
// 公共的缓存配置,有了该配置,下面方法上的value就可以省略
//@CacheConfig(cacheNames = "con")
@Service
public class ConService {

    @Resource
    private PowerControllerdeviceMapper controllerdeviceDao;


    /**将方法的运行结果缓存
     *
     * CacheManager 管理多个Cache缓存组件,对缓存的操作在其组件之中,每一个缓存组件都有一个自己的名字
     * @Cacheable
     *  属性:
     *      value/cacheNames:指定缓存组件的名字
     *      key:缓存数据的key 默认用方法参数的值
     *      keyGenerator:key的生成器,key/keyGenerator 2选1
     *      cacheManager:缓存管理器
     *      cacheResolver:缓存解析器  cacheManager/cacheResolver 2选1
     *      condition:符合条件的情况下进行缓存
     *      unless:指定的条件 true,方法的返回值不缓存,可以获取到结果进行判断
     *      sync:异步模式
     *
     * @param
     * @return
     */
    @Cacheable(cacheNames = {"con"})
    public PowerControllerdevice GetSingleData(Long condevid){
        return controllerdeviceDao.selectByPrimaryKey(condevid);

    }

    /**
     *  @Caching:配置复杂的缓存规则
     *
     * @param data
     * @return
     */
    @Caching(
            cacheable = {
                 @Cacheable(value = "con",key = "#data.conname")
            },
            put = {
                    @CachePut(value = "con",key = "#data.condevid"),
                    @CachePut(value = "con",key = "#data.condevsn"),
            }
    )
    public  List<PowerControllerdevice> Query(PowerControllerdevice data){
        PowerControllerdeviceAll rundata = new PowerControllerdeviceAll();
        PowerControllerdeviceAll.Criteria cri = rundata.createCriteria();
        List<PowerControllerdevice> powerControllerdevices = controllerdeviceDao.selectByExample(rundata);
        return powerControllerdevices;
    }

    /**getCondevid
     * @CachePut:
     *  先调用目标方法,然后将方法的但结果缓存
     *  key = "#data.condevid",同步更新缓存
     * @param data
     * @return
     * @throws Exception
     */
    @CachePut(value = "con" ,key = "#data.condevid")
    @Transactional(rollbackFor = Exception.class)
    public int Update(PowerControllerdevice data) throws  Exception{
        return controllerdeviceDao.updateByPrimaryKeySelective(data);
    }

    /**
     * @CacheEvict
     * 清除缓存,指定key clean
     * allEntries = true,清除缓存中的所有数据
     * beforeInvocation = false(默认) 默尔是在方法之后执行,方法出现异常,缓存不清除
     *                  true 缓存的清除是否在方法之前执行,方法出现异常,缓存也会删除
     * @param data
     * @return
     * @throws Exception
     */
    @CacheEvict(value = "con",key = "#data.condevid")
    @Transactional(rollbackFor = Exception.class)
    public int Delete(PowerControllerdevice data) throws Exception{
        return controllerdeviceDao.deleteByPrimaryKey(data.getCondevid());
    }
}

3.6、controller

package com.riest.controller.device;

import com.riest.model.device.PowerControllerdevice;
import com.riest.service.device.ConService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * ClassName:TermController
 * Describe:
 * Author:DGJ
 * Data:2020/11/6 17:00
 */
@RestController
@RequestMapping("/con")
public class ConDevController {

    @Autowired
    private ConService termService;

    @RequestMapping(value = "/getsingledata",method = RequestMethod.POST)
    public PowerControllerdevice GetSingleData(Long  condevid){
        return termService.GetSingleData(condevid);
    }

    @RequestMapping(value = "/select",method = RequestMethod.POST)
    public Object Select(PowerControllerdevice data, HttpServletRequest request, HttpServletResponse response){
        List<PowerControllerdevice> query = termService.Query(data);
        return query;
    }

    @RequestMapping(value = "/update",method = RequestMethod.POST)
    public Object Update(PowerControllerdevice data) throws Exception {
        int update = termService.Update(data);
        return update;
    }

    @RequestMapping(value = "/delete",method = RequestMethod.POST)
    public Object Delete(PowerControllerdevice data) throws Exception {
        return termService.Delete(data);
    }
}

3.7、主启动

package com.riest;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

/**
 * ClassName:CacheApplication
 * Describe:
 * Author:DGJ
 * Data:2020/11/12 19:30
 */
@SpringBootApplication
// 开启缓存
@EnableCaching
@MapperScan({"mapper","com.riest.dao.*"})
public class CacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(CacheApplication.class,args);
    }
}

3.8 测试该缓存走的是默认的缓存(测试只测试了一个方法)

postman post请求,可以看到第一次,走数据库查询(根据控制台日志查看)

postman post请求,可以看到第二次,走缓存查询(根据控制台日志查看)

4、整合redis

redis不用多说,想必大家都或多或少有了解

redis中文网:http://www.redis.cn/

4.1、docker 安装redis

	·docker pull redis
	·docker run -d -p 6379:6379 --name myredis redis

4.2、测试redis

4.3、pom

​ 加入reids-start

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

4.4 、yml(所有配置)

server:
  port: 9090


spring:
  application:
    name: SpringBootCache

  datasource:
    url: jdbc:postgresql://xxx:xxx/xxx?useUnicode=true&characterEncoding=utf8&useSSL=true
    username: xxx
    password: xxx
    driver-class-name: org.postgresql.Driver
    druid:
      # 连接池配置
      initial-size: 1
      max-active: 20
      min-idle: 1
      max-wait: 10000
      pool-prepared-statements: true
      max-open-prepared-statements: 20
      validation-query: SELECT 'x'
      validation-query-timeout: 5000
      test-on-borrow: false
      test-on-return: false
      test-while-idle: true
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 30000
      max-evictable-idle-time-millis: 60000
      removeAbandoned: true
      removeAbandonedTimeout: 1800
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      max-pool-prepared-statement-per-connection-size: 20
      filters: stat,wall #filters: #配置多个英文逗号分隔(统计,sql注入,log4j过滤)
    type: com.alibaba.druid.pool.DruidDataSource


  ## Redis 配置
  redis:
    ## Redis数据库索引(默认为0)
    database: 1
    ## Redis服务器地址
    host: xxxxxxxx
    ## Redis服务器连接端口
    port: 6379
    ## Redis服务器连接密码(默认为空)
    password:

# mybatis 配置
mybatis:
  mapper-locations: classpath:mapper/*/*.xml
  type-aliases-package: com.riest.modal
  configuration:
    #mabatis打印sql
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
debug: true

4.5、service

package com.riest.service.device;

import com.riest.dao.device.PowerControllerdeviceMapper;
import com.riest.dao.device.PowerTerminaldeviceMapper;
import com.riest.model.device.PowerControllerdevice;
import com.riest.model.device.PowerControllerdeviceAll;
import com.riest.model.device.PowerTerminaldevice;
import com.riest.model.device.PowerTerminaldeviceAll;
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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;

/**
 * ClassName:ConService
 * Describe:
 * Author:DGJ
 * Data:2020/11/25 9:34
 */
@Service
public class TermService {

    @Resource
    private PowerTerminaldeviceMapper terminaldeviceDao;

    /**terminaldeviceDao
     *  将查询的的结果缓存
     * @param termdevid
     * @return
     */
    @Cacheable(cacheNames = {"term"},key = "#termdevid")
    public PowerTerminaldevice GetSingleData(Long termdevid){
        return terminaldeviceDao.selectByPrimaryKey(termdevid);
    }

    public List<PowerTerminaldevice> Query(PowerTerminaldevice data){
        PowerTerminaldeviceAll rundata = new PowerTerminaldeviceAll();
        PowerTerminaldeviceAll.Criteria cri = rundata.createCriteria();
        List<PowerTerminaldevice> powerControllerdevices = terminaldeviceDao.selectByExample(rundata);
        return powerControllerdevices;
    }

    /**
     *  将方法的返回值更新到缓存
     * @param data
     * @return
     * @throws Exception
     */
    @CachePut(value = "term" ,key = "#data.termdevid")
    @Transactional(rollbackFor = Exception.class)
    public PowerTerminaldevice Update(PowerTerminaldevice data) throws Exception{
        terminaldeviceDao.updateByPrimaryKeySelective(data);
        PowerTerminaldevice terminaldevice = GetSingleData(data.getTermdevid());
        return terminaldevice;

    }

    @Transactional(rollbackFor = Exception.class)
    public int Delete(PowerTerminaldevice data) throws Exception{
        return terminaldeviceDao.deleteByPrimaryKey(data.getCondevid());
    }
}

4.6、controller

package com.riest.controller.device;

import com.riest.model.device.PowerTerminaldevice;
import com.riest.service.device.TermService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * ClassName:TermController
 * Describe:
 * Author:DGJ
 * Data:2020/11/6 17:00
 */
@RestController
@RequestMapping(value = "/term")
public class TermController {

    @Autowired
    private TermService termService;

    @Autowired
    private RedisTemplate redisTemplate;


    @RequestMapping(value = "/getsingledata",method = RequestMethod.POST)
    public PowerTerminaldevice GetSingleData(Long termdevid){
        return termService.GetSingleData(termdevid);
    }

    @RequestMapping(value = "/select",method = RequestMethod.POST)
    public Object Select(PowerTerminaldevice data, HttpServletRequest request, HttpServletResponse response){
        List<PowerTerminaldevice> query = termService.Query(data);
        return query;
    }

    /**query
     *  修改数据的同属修改缓存中的数据
     * @param data
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/update",method = RequestMethod.POST)
    public Object Update(PowerTerminaldevice data) throws Exception {
        PowerTerminaldevice update = termService.Update(data);
        return update;
    }

    @RequestMapping(value = "/delete",method = RequestMethod.POST)
    public Object Delete(PowerTerminaldevice data) throws Exception {
        return termService.Delete(data);

    }
}

4.7、redisconf

package com.riest.config.redis;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
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.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * ClassName:RedisConf
 * Describe: 数据序列化
 * Author:DGJ
 * Data:2020/11/25 9:33
 */
@Configuration
public class RedisConf {

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;


    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(objectMapper);

        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;
    }

    @Bean
    public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));
        return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
    }

}

4.8、redisutils(该utils来自https://www.cnblogs.com/zeng1994/p/03303c805731afc9aa9c60dbbd32a323.html)

package com.riest.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * ClassName:RedisUtils
 * Describe:
 * Author:DGJ
 * Data:2020/11/25 9:52
 */
@Component
public class RedisUtils {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;


    // =============================common============================

    /**
     * 指定缓存失效时间
     *
     * @param key  键
     * @param time 时间(秒)
     *             29
     * @return 30
     */


    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代表为永久有效
     * 47
     */


    public long getExpire(String key) {

        return redisTemplate.getExpire(key, TimeUnit.SECONDS);

    }


    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */


    public boolean hasKey(String key) {

        try {

            return 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(CollectionUtils.arrayToList(key));

            }

        }

    }


    // ============================String=============================

    /**
     * 普通缓存获取
     *
     * @param key 键
     * @return 值
     */


    public Object get(String key) {

        return key == null ? null : redisTemplate.opsForValue().get(key);

    }


    /**
     * 普通缓存放入
     *
     * @param key   键
     * @param value 值
     * @eturn 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)
     * @return 134
     */


    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)
     * @return
     */


    public long decr(String key, long delta) {

        if (delta < 0) {

            throw new RuntimeException("递减因子必须大于0");

        }

        return redisTemplate.opsForValue().increment(key, -delta);

    }


    // ================================Map=================================

    /**
     * HashGet
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return 值
     */


    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 对应多个键值
     * @return true 成功 false 失败
     */


    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)
     * @return 274
     */


    public double hincr(String key, String item, double by) {

        return redisTemplate.opsForHash().increment(key, item, by);

    }


    /**
     * hash递减
     *
     * @param key  键
     * @param item 项
     * @param by   要减少记(小于0)
     * @return 285
     */


    public double hdecr(String key, String item, double by) {

        return redisTemplate.opsForHash().increment(key, item, -by);

    }

    // ============================set=============================

    /**
     * 292
     * 根据key获取Set中的所有值
     * 293
     *
     * @param key 键
     *            294
     * @return 295
     */


    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;

        }

    }


    /**
     * 355
     * 获取set缓存的长度
     * 356
     *
     * @param key 键
     *            357
     * @return 358
     */


    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 {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    // ===============================list=================================

    /**
     * 获取list缓存的内容
     *
     * @param key   键
     * @param start 开始
     * @param end   结束 0 到 -1代表所有值
     * @return 391
     */


    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 键
     * @return 405
     */


    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倒数第二个元素,依次类推
     * @return 420
     */


    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
     * @return
     */
    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  时间(秒)
     * @return 453
     */


    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 {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }

    }
}

4.9、测试

  • 将查询的结果缓存

  • 将数据更的结果缓存

4.10、以上只是介绍了最基本的缓存配置,很多细节性的没有说到,有很多原理性的东西暂未讲解到

  • redis的RedisTemplate的自动配置(redisconf)原理?
  • 缓存key的生成策略、如何自定义key?
  • 如何更加优雅的用缓存?
  • 真实项目中缓存怎么用等等......
  • 以上细节后续会马上补充

5、最终项目结构图

 TO BE CONTINUED

posted @ 2020-11-25 11:47  一个努力的人QAQ  阅读(489)  评论(0编辑  收藏  举报