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