SpringBoot與快取
阿新 • • 發佈:2020-11-25
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