redis与Spring整合做缓存
项目中要用到redis,没用过,费了很多功夫,还是做了一个勉强能可以使用的。因为项目中用mysql和mangoDB结合主从配合使用,感觉配置有点让人头大。
pom.xml
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.0</version>
</dependency>
<!-- Jackson Json处理工具包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.3</version>
</dependency>
redis数据源配置
使用的基本算是最原始的方法,因为不太了解,怕出错。
<!-- Jedis 连接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="2000" />
<property name="maxIdle" value="2000" />
<property name="minIdle" value="50" />
<property name="maxWaitMillis" value="2000" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="testWhileIdle" value="false" />
</bean>
<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool">
<constructor-arg ref="jedisPoolConfig" />
<constructor-arg>
<list>
<bean class="redis.clients.jedis.JedisShardInfo">
<constructor-arg value="127.0.0.1" />
<constructor-arg type="int" value="6379" />
<constructor-arg value="redis1" />
</bean>
</list>
</constructor-arg>
</bean>
<bean id="jedisClient" class="*.redis.JedisClientSingle" />
基于注解的aop的redis缓存
自定义注解@RedisCache
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface RedisCache {
@SuppressWarnings("rawtypes")
Class type();
public int expire() default 120; //缓存多少秒,默认无限期
public String cacheKey() default "key"; //默认key值 为key
}
自定义注解@RedisEvict 清除数据,但是不太友好,很暴力,可以自己按照需求修改自己的
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RedisEvict {
@SuppressWarnings("rawtypes")
Class type();
}
JsonUtils 对象pojo 要实现序列化
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JsonUtils {
@SuppressWarnings("unused")
private static Logger logger = LoggerFactory.getLogger(JsonUtils.class);
// 定义jackson对象
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 将对象转换成json字符串。
*
* @param data
* @return
* @throws IOException
*/
public static String objectToJson(Object data) {
try {
String string = MAPPER.writeValueAsString(data);
return string;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将json结果集转化为对象
*
* @param jsonData
* json数据
* @param beanType
* 对象中的object类型
* @return
*/
public static <T> T jsonToPojo(String jsonData, Class<T> beanType) {
try {
T t = MAPPER.readValue(jsonData, beanType);
return t;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将json数据转换成pojo对象list
*
* @param jsonData
* @param beanType
* @return
*/
public static <T> List<T> jsonToList(String jsonData, Class<T> beanType) {
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(
List.class, beanType);
try {
List<T> list = MAPPER.readValue(jsonData, javaType);
return list;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* json string convert to map with javaBean
*/
public static <T> Map<String, T> jsonTomap(String jsonStr, Class<T> clazz)
throws Exception {
Map<String, Map<String, Object>> map = MAPPER.readValue(jsonStr,
new TypeReference<Map<String, T>>() {
});
Map<String, T> result = new HashMap<String, T>();
for (Entry<String, Map<String, Object>> entry : map.entrySet()) {
result.put(entry.getKey(), mapTopojo(entry.getValue(), clazz));
}
return result;
}
/**
* json string convert to map
*/
@SuppressWarnings("unchecked")
public static <T> Map<String, Object> jsonTomap(String jsonStr) {
try {
return MAPPER.readValue(jsonStr, Map.class);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
* map convert to javaBean
*/
@SuppressWarnings("rawtypes")
public static <T> T mapTopojo(Map map, Class<T> clazz) {
try {
return MAPPER.convertValue(map, clazz);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
JedisClient
public interface JedisClient {
String get(String key);
byte[] get(byte[] key);
String set(String key, String value);
String set(byte[] key, byte[] value);
String hget(String hkey, String key);
long hset(String hkey, String key, String value);
long incr(String key);
long expire(String key, int second);
long ttl(String key);
long del(String key);
long hdel(String hkey, String key);
}
JedisClientSingle
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
public class JedisClientSingle implements JedisClient {
@Autowired
private ShardedJedisPool shardedJedisPool;
@Override
public String get(String key) {
ShardedJedis jedis = shardedJedisPool.getResource();
// Jedis jedis = jedisPool.getResource();
String string = jedis.get(key);
jedis.close();
return string;
}
@Override
public String set(String key, String value) {
ShardedJedis jedis = shardedJedisPool.getResource();
// Jedis jedis = jedisPool.getResource();
String string = jedis.set(key, value);
jedis.close();
return string;
}
@Override
public String hget(String hkey, String key) {
ShardedJedis jedis = shardedJedisPool.getResource();
// Jedis jedis = jedisPool.getResource();
String string = jedis.hget(hkey, key);
jedis.close();
return string;
}
@Override
public long hset(String hkey, String key, String value) {
ShardedJedis jedis = shardedJedisPool.getResource();
// Jedis jedis = jedisPool.getResource();
Long result = jedis.hset(hkey, key, value);
jedis.close();
return result;
}
@Override
public long incr(String key) {
ShardedJedis jedis = shardedJedisPool.getResource();
// Jedis jedis = jedisPool.getResource();
Long result = jedis.incr(key);
jedis.close();
return result;
}
@Override
public long expire(String key, int second) {
ShardedJedis jedis = shardedJedisPool.getResource();
// Jedis jedis = jedisPool.getResource();
Long result = jedis.expire(key, second);
jedis.close();
return result;
}
@Override
public long ttl(String key) {
ShardedJedis jedis = shardedJedisPool.getResource();
// Jedis jedis = jedisPool.getResource();
Long result = jedis.ttl(key);
jedis.close();
return result;
}
@Override
public long del(String key) {
ShardedJedis jedis = shardedJedisPool.getResource();
// Jedis jedis = jedisPool.getResource();
Long result = jedis.del(key);
jedis.close();
return result;
}
@Override
public long hdel(String hkey, String key) {
ShardedJedis jedis = shardedJedisPool.getResource();
// Jedis jedis = jedisPool.getResource();
Long result = jedis.hdel(hkey, key);
jedis.close();
return result;
}
@Override
public byte[] get(byte[] key) {
ShardedJedis jedis = shardedJedisPool.getResource();
// Jedis jedis = jedisPool.getResource();
byte[] result = jedis.get(key);
jedis.close();
return result;
}
@Override
public String set(byte[] key, byte[] value) {
ShardedJedis jedis = shardedJedisPool.getResource();
// Jedis jedis = jedisPool.getResource();
String result = jedis.set(key, value);
jedis.close();
return result;
}
}
CacheInterceptor Spring Aop
aop的处理还是很粗糙,可以自己根据实际需求修改,我觉得删除缓存那块可以优化下
<!-- 开启AOP -->
<aop:aspectj-autoproxy proxy-target-class="true"/>*
//proxy-target-class="true" 没记错的话使用cglib代理,不加他默认的是jdk的动态代理
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class CacheInterceptor {
private static Logger logger = LoggerFactory.getLogger(JsonUtils.class);
@Autowired
private JedisClient jedisClient;
//前置由于数据库数据变更 清理redis缓存
@Before("@annotation(redisEvict)")
public void doBefore (JoinPoint jp,RedisEvict redisEvict){
try{
String modelName = redisEvict.type().getName();
// 清除对应缓存
jedisClient.del(modelName);
}catch (Exception e) {
logger.info("缓存服务器出现问题,发邮箱,发信息..."+e);
}
}
// 配置环绕方法
@Around("@annotation(redisCache)")
public Object doAround(ProceedingJoinPoint pjp, RedisCache redisCache)
throws Throwable {
//得到注解上类型
@SuppressWarnings("rawtypes")
Class modelType = redisCache.type();
// 去Redis中看看有没有我们的数据 类名 + 方法名 + 参数(多个)
String cacheKey = redisCache.cacheKey();
if(cacheKey.equals("key")){
cacheKey = this.getCacheKey(pjp);
}
String value = null;
try {//当取redis发生异常时,为了不影响程序正常执行,需要try..catch()...
//检查redis中是否有缓存
value = jedisClient.hget(modelType.getName(),cacheKey);
// System.out.println(jedisClient.hget(modelType.getName(),cacheKey));
} catch (Exception e) {
logger.info("缓存服务器出现问题,发邮箱,发信息..."+e);
}
// result是方法的最终返回结果
Object result = null;
if (null == value) {
// 缓存未命中
// System.out.println("缓存未命中");
// 后端查询数据
result = pjp.proceed();
try {//当取redis发生异常时,为了不影响程序正常执行,需要try..catch()...
String json = serialize(result);
//判断结果是否为空,不为空则返回
if(!(json.equals("[]"))){
// 序列化结果放入缓存
jedisClient.hset(modelType.getName(), cacheKey, json);
int times = redisCache.expire();
if(times < 60){
times = 120;
}
if(redisCache.expire()>0) {
jedisClient.expire(cacheKey, times);//设置缓存时间
}
}
} catch (Exception e) {
logger.info("缓存服务器出现问题,发邮箱,发信息..."+e);
}
} else {
try{//当数据转换失败发生异常时,为了不影响程序正常执行,需要try..catch()...
// int i =1/0;
// 得到被代理方法的返回值类型
@SuppressWarnings("rawtypes")
Class returnType = ((MethodSignature) pjp.getSignature()).getReturnType();
//把json反序列化
result = deserialize(value, returnType, modelType);
// 缓存命中
} catch (Exception e) {
//数据转换失败,到后端查询数据
result = pjp.proceed();
logger.info("缓存命中,但数据转换失败..."+e);
}
}
return result;
}
protected String serialize(Object target) {
return JsonUtils.objectToJson(target);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected Object deserialize(String jsonString, Class clazz, Class modelType) {
// 序列化结果应该是List对象
if (clazz.isAssignableFrom(List.class)) {
return JsonUtils.jsonToList(jsonString, modelType);
}
// 序列化结果是普通对象
return JsonUtils.jsonToPojo(jsonString, clazz);
}
// 类名 + 方法名 + 参数(多个) 生成Key
public String getCacheKey(ProceedingJoinPoint pjp) {
StringBuffer key = new StringBuffer();
// 类名 cn.core.serice.product.ProductServiceImpl.productList
String packageName = pjp.getTarget().getClass().getSimpleName();
key.append(packageName);
// 方法名
String methodName = pjp.getSignature().getName();
key.append(".").append(methodName);
// 参数(多个)
Object[] args = pjp.getArgs();
for (Object arg : args) {
// 参数
key.append(".").append(arg.toString());
}
return key.toString();
}
}
在service层,注入注解
@RedisCache(type = Bim114CategoryLevel2.class)
public List<Bim114CategoryLevel2> getlevel2List() {
return null;
}
开启redis,运行项目就可以了
欢迎加我QQ 971492191 讨论,一起向前