时间有限,先整理实现。后面会补充详解
1.引用mvn依赖
项目中,未引用AOP 导致
org.springframework.data
spring-data-redis
1.7.2.RELEASE
org.apache.commons
commons-lang3
3.3.2
org.springframework
spring-context
4.3.3.RELEASE
org.springframework
spring-aop
4.3.3.RELEASE
2. 配置redis
#redis settings
# server IP
redis.host=10.2.*.*
# server port
redis.port=6379
# server pass
redis.pass=Jzyt2@2018
# use dbIndex
redis.database=0
#max idel instance of jedis
redis.maxIdle=300
redis.maxTotal=100
#if wait too long ,throw JedisConnectionException
redis.maxWait=3000
#if true,it will validate before borrow jedis instance,what you get instance is all usefull
redis.testOnBorrow=true
3.配置 applicationContext-redis.xml
4. 创建缓存管理类
package com.hollysmart.admin.common.cache;
import org.apache.commons.lang3.SerializationUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import java.io.Serializable;
import java.util.concurrent.Callable;
/**
* 基于redisTemplate的缓存
*/
@Configuration
@EnableCaching
public class RedisCache implements Cache {
private RedisTemplate redisTemplate; //操作redis的模板
private String name; // 对应redsi.xml文件中的redisCacheManager中多个redis管理器
/**
* 缓存清理
*/
@Override
public void clear() {
redisTemplate.execute(new RedisCallback() {
@Override
public String doInRedis(RedisConnection connection) throws DataAccessException {
connection.flushDb();
return "ok";
}
});
}
/**
* 删除缓存
* @param key redisKey
*/
@Override
public void evict(Object key) {
final String keyf = key.toString();
redisTemplate.execute(new RedisCallback() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
return connection.del(keyf.getBytes());
}
});
}
/**
* 获取缓存
* @param key redisKey
* @return obj
*/
@Override
public ValueWrapper get(Object key) {
final String keyf = key.toString();
Object object = null;
object = redisTemplate.execute(new RedisCallback
5. 使用alibaba的 fastJson将数据转JSON序列化
package com.hollysmart.admin.common.cache;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class JsonHelper {
/**
* Java对象序列化为JSON字符串
*
* @param obj Java对象
* @return json字符串
*/
public static String toJson(Object obj) {
return JSON.toJSONString(obj, SerializerFeature.WriteMapNullValue);
}
}
6. 判断是否为简单类型
package com.hollysmart.admin.common.cache;
import org.springframework.util.ClassUtils;
import java.net.URI;
import java.net.URL;
import java.util.Date;
import java.util.Locale;
public class BeanHelper {
/**
* 判断是否是简单值类型.包括:基础数据类型、CharSequence、Number、Date、URL、URI、Locale、Class;
*
* @param clazz
* @return
*/
public static boolean isSimpleValueType(Class> clazz) {
return (ClassUtils.isPrimitiveOrWrapper(clazz) || clazz.isEnum() || CharSequence.class.isAssignableFrom(clazz)
|| Number.class.isAssignableFrom(clazz) || Date.class.isAssignableFrom(clazz) || URI.class == clazz
|| URL.class == clazz || Locale.class == clazz || Class.class == clazz);
}
}
7. 自定义redis缓存的key,
1.建议在 pojo类中重写toString()方法,中间碰到一个问题,就是对参数进行拼接的时候通过hashCode()取值后,每次的hashCode的值都不一样。导致同接口同参数请求接口自定义生成的key每次都不一样,缓存失败。
2.建议在实体类中尽量的不要循环引用,否则在序列化对象的时候,会造成 栈内存溢出异常,解决方式 在循环引用的对象get方法上添加 @JSONField(serialize = false) 来解决。
package com.hollysmart.admin.common.cache;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.Serializable;
/**
* 自定义key生成,项目名:类名:方法名:参数名 通过sha256算法加密后生成
*/
@Configuration
public class CacheKeyGenerator implements Serializable{
private final static int NO_PARAM_KEY = 0;
private static final long serialVersionUID = 8350803698373991855L;
private static String keyPrefix = "kw";// key前缀,用于区分不同项目的缓存,建议每个项目单独设置
@Bean
public KeyGenerator keyGenerator(){
return (target,method,params) ->{
char sp = ':';
StringBuilder strBuilder = new StringBuilder(30);
strBuilder.append(keyPrefix);
strBuilder.append(sp);
// 类名
strBuilder.append(target.getClass().getSimpleName());
strBuilder.append(sp);
// 方法名
strBuilder.append(method.getName());
strBuilder.append(sp);
if (params.length > 0) {
// 参数值
for (Object object : params) {
if (BeanHelper.isSimpleValueType(object.getClass())) {
strBuilder.append(object);
} else {
strBuilder.append(JsonHelper.toJson(object).hashCode());
}
}
} else {
strBuilder.append(NO_PARAM_KEY);
}
return DigestUtils.sha256Hex(strBuilder.toString());
};
}
}
8. 在service层进行注解实现,以及 @CacheConfig @Cacheable @CachePut @CacheEvict的 简单介绍使用
以下是伪代码:
package com.hollysmart.admin.modules.project.service;
import com.hollysmart.admin.common.service.CrudService;
import com.hollysmart.admin.modules.project.dao.BusHotSearchDao;
import com.hollysmart.admin.modules.project.entity.BusHotSearch;
import com.hollysmart.admin.modules.project.entity.BusSearchRecord;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 热搜词 测试接口
*
* @CacheConfig 中 cacheNames : 定义redis缓存名称,与/spring/applicationContext.xml中的 做关联
* cacheManager: 定义 redis的缓存管理 与/spring/applicationContext.xml中的 做关联
* @Cacheable 缓存查询
* @Cacheput 缓存更新/新增 使用的方法的返回结果作为缓存内容进行缓存
* @CacheEvict 缓存删除
*
* 自定义缓存主键 要求使用 keyGenerator = "keyGenerator" || key = ""
*
*
*/
@Service
@Transactional(readOnly = true)
@CacheConfig(cacheNames = {"hotSearch"}, cacheManager = "redisCacheManager")
public class BusHotSearchService extends CrudService {
public List findBusHotSearch(String searchType) {
return dao.findBusHotSearch(searchType);
}
@Transactional(readOnly = false)
public void insert(BusHotSearch busHotSearch) {
busHotSearch.preInsert();
dao.insert(busHotSearch);
}
@Transactional(readOnly = false)
public void update(BusHotSearch busHotSearch) {
busHotSearch.preUpdate();
dao.update(busHotSearch);
}
public int findOneBusHotSearch(String searchTerm, String searchType) {
return dao.findOneBusHotSearch(searchTerm, searchType);
}
@Transactional(readOnly = true)
@Cacheable( key = "#id")
public BusHotSearch findOneBusHotSearchById(String id) {
logger.info("redis中没有数据,从数据库中读");
return dao.findOneBusHotSearchById(id);
}
/**
* @cache*** 底层使用的是AOP
* @Cacheable 查询缓存,如果缓存中没有,会执行sql查询数据库
* sync true: 同步 false :异步
*
*
*
* @cacheable()的写法
* @Cacheable(value = "",key = "#root.methodName + #index")
* @Cacheable(cacheNames = {"",""},key = "#root.methodName + #index")
* @Cacheable(cacheNames = {"",""},key = "#root.methodName + #index",sync = false)
* @param index
* @return
*/
@Cacheable(cacheNames = {"",""},key = "#root.methodName + #index",sync = false)
public List findMoreBusHotSearchList(Integer index) {
logger.info("redis中没有数据,从数据库中读");
return dao.findMoreBusHotSearchList(index);
}
@Transactional(readOnly = false)
@CachePut(key = "#busHotSearch.id")
public BusHotSearch updateBusHotSearchList(BusHotSearch busHotSearch) {
busHotSearch.preUpdate();
dao.updateBusHotSearchList(busHotSearch);
return busHotSearch;
}
/**
* @CachePut() 修改缓存
* key 缓存的key
* keyGenerator 同key 一样,定义缓存的key,优先级比key低
* 缓存的key 可以定义为:
* key = "#busHotSearch.id" //参数id
* key = "#root.method" //当前方法
* keyGenerator = "keyGenerator" //自定义主键 ,上面写的自定义主键格式DigestUtils.sha256Hex(项目名:类名:方法名:参数拼接的hashCode)保证键不重复
* key = "#root.methodName + '_test'" // 当前方法名+ 自定义名称 拼接
*
* @param busHotSearch 热搜词对象
* @return 热搜词
*/
@Transactional(readOnly = false)
@CachePut(key = "#busHotSearch.id")
public BusHotSearch insertBusHotSearchList(BusHotSearch busHotSearch) {
busHotSearch.preInsert();
dao.insert(busHotSearch);
return busHotSearch;
}
/**
* @CacheEvict() 删除缓存
* 此处为定义key的值,会删除 缓存对象 为 hotSearch的所有缓存
* allEntries 是否把上面cacheNames指定的所有的缓存都清除掉,默认false
* beforeInvocation 是否让清理缓存动作在目标方法之前执行,默认是false,若在之后执行的话,目标方法一旦抛出异常了,那缓存就清理不掉了
* condition SpEL,可以使用#root.** 可以搜索一下详细的表达式使用
* @param id id
* @return
*/
@Transactional(readOnly = false)
// @CacheEvict(key = "'findMoreBusHotSearchList0'",allEntries = true)
@CacheEvict(cacheNames = {"hotSearch"},allEntries = true,beforeInvocation = false)
public int delBusHotSearchList(String id) {
int affectRows = dao.deleteHotSearchList(id);
return affectRows;
}
}
OK,有时间再对该实现做详解补充。这只是简单的使用。如果有好的建议谢谢各位留言。