yml文件
redis:host: 127.0.0.1port: 40197password: 123456timeout: 5000database: 0jedis:pool:min-idle: 0max-idle: 8max-active: 8max-wait: -1
RedisConfig.java
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
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.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;
import redis.clients.jedis.JedisPoolConfig;import java.io.Serializable;
import java.time.Duration;/*** @author */
@Configuration
@ConditionalOnProperty("spring.redis.host")
@EnableCaching
@Slf4j
public class RedisConfig extends CachingConfigurerSupport {@Value("${spring.redis.host}")String redisHost;@Value("${spring.redis.port}")int redisPort;@Value("${spring.redis.password}")String redisPassword;@Value("${spring.redis.timeout}")int redisTimeout;@Value("${spring.redis.database}")int redisDatabase;@Value("${spring.redis.jedis.pool.min-idle}")int jedisMinIdle;@Value("${spring.redis.jedis.pool.max-idle}")int jedisMaxIdle;@Value("${spring.redis.jedis.pool.max-active}")int jedisMaxActive;@Value("${spring.redis.jedis.pool.max-wait}")int jedisMaxWait;/*** 自定义缓存key生成策略* @return KeyGenerator*/@Bean@Overridepublic KeyGenerator keyGenerator() {return (target, method, params) -> {StringBuilder sb = new StringBuilder();sb.append(target.getClass().getName());sb.append(method.getName());for (Object obj : params) {sb.append(obj.toString());}return sb.toString();};}@Beanpublic RedisConnectionFactory connectionFactory() {JedisPoolConfig poolConfig = new JedisPoolConfig();poolConfig.setMaxTotal(jedisMaxActive);poolConfig.setMaxIdle(jedisMaxIdle);poolConfig.setMaxWaitMillis(jedisMaxWait);poolConfig.setMinIdle(jedisMinIdle);poolConfig.setTestOnBorrow(true);poolConfig.setTestOnReturn(false);poolConfig.setTestWhileIdle(true);JedisClientConfiguration clientConfig = JedisClientConfiguration.builder().usePooling().poolConfig(poolConfig).and().readTimeout(Duration.ofMillis(redisTimeout)).build();// 单点redisRedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();log.debug(JSON.toJSONString(redisStandaloneConfiguration));redisStandaloneConfiguration.setHostName(redisHost);redisStandaloneConfiguration.setPassword(RedisPassword.of(redisPassword));redisStandaloneConfiguration.setPort(redisPort);redisStandaloneConfiguration.setDatabase(redisDatabase);return new JedisConnectionFactory(redisStandaloneConfiguration, clientConfig);}/*** 缓存管理器* @param redisConnectionFactory RedisConnectionFactory* @return CacheManager*/@Beanpublic CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {// 设置缓存有效期一小时int hour = 1;RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(hour));return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory)).cacheDefaults(redisCacheConfiguration).build();}@Beanpublic RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory){RedisTemplate<String, String> template = new RedisTemplate<>();template.setConnectionFactory(factory);//设置序列化工具setSerializer(template);template.afterPropertiesSet();return template;}private void setSerializer(RedisTemplate<String, String> template) {RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();RedisSerializer<Serializable> genericToStringSerializer = new GenericToStringSerializer<>(Serializable.class);log.debug(String.valueOf(genericToStringSerializer));RedisSerializer<Object> jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();log.debug(String.valueOf(jdkSerializationRedisSerializer));Jackson2JsonRedisSerializer<Serializable> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Serializable.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);RedisSerializer<Object> genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();RedisSerializer<Serializable> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Serializable.class);template.setEnableDefaultSerializer(true);template.setDefaultSerializer(fastJsonRedisSerializer);template.setStringSerializer(stringRedisSerializer);template.setKeySerializer(stringRedisSerializer);template.setValueSerializer(genericJackson2JsonRedisSerializer);template.setHashKeySerializer(stringRedisSerializer);template.setHashValueSerializer(fastJsonRedisSerializer);}}
应用Java
import com.alibaba.fastjson.JSON;
import com.chinamobile.framework.redis.enums.BaseRedisEnum;
import com.chinamobile.framework.redis.service.RedisObjectService;
import com.chinamobile.framework.redis.vo.BaseRedisVo;
import com.chinamobile.scm.order.service.RedisService;
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.ConditionalOnProperty;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;@Service
@ConditionalOnProperty("spring.redis.host")
@Slf4j
public class RedisServiceImpl<T extends Serializable> implements RedisService<T> {@Value("${spring.application.name}")String app;@AutowiredRedisTemplate<String, String> redisTemplate;@Overridepublic final String getApp() {return app;}@Overridepublic final RedisTemplate<String, String> getRedisTemplate() {return redisTemplate;}@Overridepublic void set(String key, String value) {Assert.notNull(key, "参数id不能为空");log.debug("set key:" + key + ",value:" + value);redisTemplate.opsForValue().set(key, value);}@Overridepublic String get(String key) {Assert.notNull(key, "参数key不能为空");log.info("get id:" + key +" start get value");Object obv ="";try {obv = redisTemplate.opsForValue().get(key);}catch (Exception exp){String messageerr=exp.getMessage();if(messageerr.indexOf("loginUserId")>-1){int idxloginUserId=messageerr.indexOf("loginUserId");String loginstartidx=messageerr.substring(idxloginUserId-1);int idxsencond=loginstartidx.indexOf(",");String endindexstr=loginstartidx.substring(0,idxsencond);String valuestr="{"+endindexstr.replaceAll(",","")+"}";obv=valuestr;}}// Could not read JSON: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class'\n at [Source: (byte[])\"{\"timestamp\":\"1607414522055\",\"loginUserId\":\"zhanghuan6@tj.cmcc\",\"loginIp\":\"220.196.49.26\",\"loginAddrCode\":\"TJ\",\"loginType\":\"LOGINPAGE\"}\String vo=(String)obv;log.info("get id:" + key + ",value:" + vo);return vo;}
public static void main(String[] args)
{String messageerr="Could not read JSON: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class'\n at [Source: (byte[])\"{\"timestamp\":\"1607414522055\",\"loginUserId\":\"zhanghuan6@tj.cmcc\",\"loginIp\":\"220.196.49.26\",\"loginAddrCode\":\"TJ\",\"loginType\":\"LOGINPAGE\"}\"; line: 1, column: 135]; nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class'\n at [Source: (byte[])\"{\"timestamp\":\"1607414522055\",\"loginUserId\":\"zhanghuan6@tj.cmcc\",\"loginIp\":\"220.196.49.26\",\"loginAddrCode\":\"TJ\",\"loginType\":\"LOGINPAGE\"}\"; line: 1, column: 135]";if(messageerr.indexOf("loginUserId")>-1){int idxloginUserId=messageerr.indexOf("loginUserId");String loginstartidx=messageerr.substring(idxloginUserId-1);int idxsencond=loginstartidx.indexOf(",");String endindexstr=loginstartidx.substring(0,idxsencond);String valuestr="{"+endindexstr.replaceAll(",","")+"}";System.out.println(valuestr);}}}
基础Service Redis类
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.chinamobile.framework.common.constant.CommonConstant;
import com.chinamobile.framework.redis.enums.BaseRedisEnum;
import com.chinamobile.framework.redis.vo.BaseRedisVo;
import lombok.SneakyThrows;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;@SuppressWarnings("unused")
public interface BaseRedisService {String getApp();RedisTemplate<String, String> getRedisTemplate();/*** 根据module和id生成redis的key* @param module 业务场景code* @param id 主键* @return key*/default String generateKey(BaseRedisEnum module, String id) {StringBuilder buffer = new StringBuilder();Assert.hasText(getApp(), "配置spring.application.name错误");Assert.notNull(module, "参数module错误");Assert.notNull(id, "参数id不能为空");Assert.isTrue(!id.startsWith("\"") && !id.endsWith("\""), "id不能以引号开始且不能以引号结束");buffer.append(getApp()).append(CommonConstant.COLON).append(module.getKey());if (!StringUtils.isEmpty(id)) {return buffer.append(CommonConstant.COLON).append(id).toString();}return "\"" + buffer.toString() + "\"";//必须加引号,超过32个字符}default Collection<String> generateKeys(BaseRedisEnum module, Collection<String> ids) {if (ids == null) {return null;}Collection<String> keys = new ArrayList<>();for (String id : ids) {keys.add(generateKey(module, id));}return keys;}/*** 删除缓存 根据key精确匹配删除* @param module 业务场景code* @param id 主键*/default void delete(BaseRedisEnum module, String id) {this.delete(module, new String[]{id});}/*** 删除缓存 根据keys批量精确匹配删除* @param module 业务场景code* @param ids 主键数组*/default void delete(BaseRedisEnum module, String[] ids) {if (ids != null && ids.length > 0) {List<String> delKeys = new ArrayList<>();for (String id : ids) {if (!StringUtils.hasText(id)) {delKeys.add(generateKey(module, id));}}getRedisTemplate().delete(delKeys);}}/*** 指定缓存的失效时间* @param module 业务场景code* @param id 主键* @param second 有效时间,单位秒* @return 是否成功*/default Boolean expire(BaseRedisEnum module, String id, long second) {return expire(module, id, second, TimeUnit.SECONDS);}default Boolean expire(BaseRedisEnum module, String id, long time, TimeUnit unit) {Assert.notNull(id, "参数id不能为空");String key = generateKey(module, id);if (time > 0) {return getRedisTemplate().expire(key, time, unit);}return Boolean.FALSE;}/*** 查看key是否存在* @param module 业务场景code* @param id 主键* @return redis的key集合*/default Set<String> keys(BaseRedisEnum module, String id) {StringBuilder pattern = new StringBuilder();Assert.hasText(getApp(), "配置spring.application.name错误");Assert.notNull(module, "参数module错误");pattern.append(getApp()).append(CommonConstant.COLON).append(module.getKey());if (!StringUtils.isEmpty(id)) {pattern.append(CommonConstant.COLON).append(id);}pattern.append("*");return getRedisTemplate().keys(pattern.toString());}/*** 检查key是否存在* @param module 业务场景code* @param id 主键* @return 是否存在,true存在,false不存在*/default boolean exist(BaseRedisEnum module, String id) {Assert.notNull(id, "参数id不能为空");String key = generateKey(module, id);Boolean bool = getRedisTemplate().hasKey(key);if (bool == null) {return false;}return bool;}default <T extends Serializable> Map<String, String> generateMap(BaseRedisEnum module, Map<String, T> data) {Map<String, String> map = new HashMap<>();for (Map.Entry<String, T> entry : data.entrySet()) {BaseRedisVo<T> vo = new BaseRedisVo<>(entry.getValue());map.put(generateKey(module, entry.getKey()), JSON.toJSONString(vo));}return map;}@SuppressWarnings("unchecked")@SneakyThrowsdefault <T> T transfer(String vo) {if (vo == null) {return null;}JSONObject json = JSON.parseObject(vo);BaseRedisVo<JSONObject> v = json.toJavaObject(BaseRedisVo.class);Class<T> clazz = (Class<T>) Class.forName(v.getClazzT());JSONObject data = v.getData();return data.toJavaObject(clazz);}@SneakyThrowsdefault <T extends Serializable> List<T> transfer(List<String> list) {List<T> result = new ArrayList<>();if (CollectionUtils.isEmpty(list)) {return result;}for (String vo : list) {if (vo != null) {result.add(transfer(vo));}}return result;}@SneakyThrowsdefault <T extends Serializable> Set<T> transfer(Set<String> set) {Set<T> result = new HashSet<>();if (CollectionUtils.isEmpty(set)) {return result;}for (String vo : set) {if (vo != null) {result.add(transfer(vo));}}return result;}}
调用方分类
import com.alibaba.fastjson.JSON;
import com.chinamobile.framework.redis.enums.BaseRedisEnum;
import com.chinamobile.framework.redis.service.RedisObjectService;
import com.chinamobile.framework.redis.vo.BaseRedisVo;
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.ConditionalOnProperty;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;@Service
@ConditionalOnProperty("spring.redis.host")
@Slf4j
public final class RedisObjectServiceImpl<T extends Serializable> implements RedisObjectService<T> {@Value("${spring.application.name}")String app;@AutowiredRedisTemplate<String, String> redisTemplate;@Overridepublic final String getApp() {return app;}@Overridepublic final RedisTemplate<String, String> getRedisTemplate() {return redisTemplate;}@Overridepublic final void set(BaseRedisEnum module, String id, T value) {Assert.notNull(id, "参数id不能为空");BaseRedisVo<T> vo = new BaseRedisVo<>(value);String v = JSON.toJSONString(vo);log.debug("set id:" + id + ",value:" + v);redisTemplate.opsForValue().set(generateKey(module, id), v);}@Overridepublic final void set(BaseRedisEnum module, String id, T value, long second) {Assert.notNull(id, "参数id不能为空");BaseRedisVo<T> vo = new BaseRedisVo<>(value);String key = generateKey(module, id);redisTemplate.opsForValue().set(key, JSON.toJSONString(vo), second, TimeUnit.SECONDS);}@Overridepublic final Boolean setIfAbsent(BaseRedisEnum module, String id, T value) {Assert.notNull(id, "参数id不能为空");BaseRedisVo<T> vo = new BaseRedisVo<>(value);return redisTemplate.opsForValue().setIfAbsent(generateKey(module, id), JSON.toJSONString(vo));}@Overridepublic final void multiSet(BaseRedisEnum module, Map<String, T> data) {Assert.notEmpty(data, "参数data不能为空");redisTemplate.opsForValue().multiSet(generateMap(module, data));}@Overridepublic final Boolean multiSetIfAbsent(BaseRedisEnum module, Map<String, T> data) {Assert.notEmpty(data, "参数data不能为空");return redisTemplate.opsForValue().multiSetIfAbsent(generateMap(module, data));}@Overridepublic final T get(BaseRedisEnum module, String id) {Assert.notNull(id, "参数id不能为空");String vo = redisTemplate.opsForValue().get(generateKey(module, id));log.debug("get id:" + id + ",value:" + vo);return transfer(vo);}@Overridepublic final T getAndSet(BaseRedisEnum module, String id, T value) {Assert.notNull(id, "参数id不能为空");BaseRedisVo<T> v = new BaseRedisVo<>(value);String vo = redisTemplate.opsForValue().getAndSet(generateKey(module, id), JSON.toJSONString(v));return transfer(vo);}@Overridepublic final List<T> multiGet(BaseRedisEnum module, Collection<String> ids) {Assert.notEmpty(ids, "参数ids不能为空");List<String> list = redisTemplate.opsForValue().multiGet(generateKeys(module, ids));return transfer(list);}@Overridepublic final long generate(BaseRedisEnum module, String id) {Assert.notNull(id, "参数id不能为空");return generate(module, id, 1);}@Overridepublic final long generate(BaseRedisEnum module, String id, Date expireTime) {Assert.notNull(id, "参数id不能为空");return generate(module, id, 1, expireTime);}@Overridepublic final long generate(BaseRedisEnum module, String id, int increment) {Assert.notNull(id, "参数id不能为空");RedisAtomicLong counter = new RedisAtomicLong(generateKey(module, id), Objects.requireNonNull(redisTemplate.getConnectionFactory()));return counter.addAndGet(increment);}@Overridepublic final long generate(BaseRedisEnum module, String id, int increment, Date expireTime) {Assert.notNull(id, "参数id不能为空");RedisAtomicLong counter = new RedisAtomicLong(generateKey(module, id), Objects.requireNonNull(redisTemplate.getConnectionFactory()));counter.expireAt(expireTime);return counter.addAndGet(increment);}}
分布式锁
import com.chinamobile.framework.common.constant.CommonConstant;
import com.chinamobile.framework.common.exception.CommonException;
import com.chinamobile.framework.redis.enums.CommonDistributedLockEnum;
import com.chinamobile.framework.redis.service.CommonDistributedLockService;
import com.chinamobile.framework.redis.service.RedisObjectService;
import com.chinamobile.framework.utils.ProjectUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;@Service
@ConditionalOnProperty("spring.redis.host")
@Slf4j
public class CommonDistributedLockServiceImpl implements CommonDistributedLockService {private static final long DEFAULT_EXPIRE = -1;@Resourceprivate ApplicationContext applicationContext;@Resourceprivate RedisObjectService<String> redisObjectService;@Overridepublic boolean lock(String id) {log.debug("lock:id=" + id);return this.lock(id, DEFAULT_EXPIRE);}@SneakyThrows@Overridepublic boolean lock(String id, long second) {log.debug("lock:id=" + id + ",time(s)=" + second);if (!isValidLock(id)) {throw new CommonException(CommonConstant.TOO_FREQUENT_CODE, "操作太频繁,请稍候再试。");}String ip = ProjectUtil.getIp(applicationContext.getEnvironment());String port = ProjectUtil.getPort(applicationContext.getEnvironment());String value = ip + "_" + port;if (!redisObjectService.setIfAbsent(CommonDistributedLockEnum.COMMON_DISTRIBUTED_LOCK, id, value)) {throw new CommonException(CommonConstant.TOO_FREQUENT_CODE, "操作太频繁,请稍候再试。");}if (second > 0) {return redisObjectService.expire(CommonDistributedLockEnum.COMMON_DISTRIBUTED_LOCK, id, second);}return true;}@Overridepublic void unlock(String id) {log.debug("unlock:id=" + id);
// redisObjectService.delete(CommonDistributedLockEnum.COMMON_DISTRIBUTED_LOCK, id);redisObjectService.expire(CommonDistributedLockEnum.COMMON_DISTRIBUTED_LOCK, id, 1, TimeUnit.NANOSECONDS);//改成1毫秒过期}@SneakyThrows@Overridepublic void cleanLock() {List<String> deletes = new ArrayList<>();Set<String> keys = redisObjectService.keys(CommonDistributedLockEnum.COMMON_DISTRIBUTED_LOCK, null);if (!CollectionUtils.isEmpty(keys)) {String ip = ProjectUtil.getIp(applicationContext.getEnvironment());String port = ProjectUtil.getPort(applicationContext.getEnvironment());String data = ip + "_" + port;for (String key : keys) {String vo = redisObjectService.getRedisTemplate().opsForValue().get(key);if (vo != null && data.equals(redisObjectService.transfer(vo))) {deletes.add(key);redisObjectService.getRedisTemplate().expire(key, 1, TimeUnit.NANOSECONDS);}}log.info("cleanLock:ids=" + deletes);
// redisObjectService.getRedisTemplate().delete(deletes);}}@Overridepublic boolean isValidLock(String id) {log.debug("isValidLock:id=" + id);return !redisObjectService.exist(CommonDistributedLockEnum.COMMON_DISTRIBUTED_LOCK, id);}}