最近在做一個項目的時候,需要用到spring的aop切面方法整合redis緩存,從而提高數據庫的效率,這個開始對於我來說是個挑戰,開始對這個不是太瞭解,不過了解了redis之後,發現這是一個非常好的方法,可以利用redis作爲數據庫的緩存,從而提高數據庫的執行效率,現在就利用aop思想來整合一下redis緩存,實現一下redis在具體web開發中的應用。
第一步,考入redis需要的jar包,靠操作json的jar包
<!-- Redis客戶端 -->
<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>
第二步,在spring的配置文件裏添加spring識別切面的配置
<!-- 開啓切面代理 使得spring認識 @Aspect -->
<aop:aspectj-autoproxy/>
第三步,然後定義兩個標註在Service實現方法上的註解,用於傳遞類型參數:
package com.fupin.redis.Service;
import java.lang.annotation.Documented;
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)
@Documented
public @interface RedisCache {
@SuppressWarnings("rawtypes")
Class type();
public int expire() default 0; //緩存多少秒,默認無限期
public String cacheKey() ;
}
package com.fupin.redis.Service;
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();
}
註解使用方式如下:
@RedisCache(type = User.class, expire = 200000000)
public List<User> findUsersByGroupId(Integer group_id) {
return groupDao.findUsersByGroupId(group_id);
}
@RedisEvict(type = User.class)// 表示該方法需要執行清除緩存邏輯
public void updateUserByPhone(String nickName,String phone) {
UserDao.updateUserByPhone(nickName, phone);
}
第四步:創建JsonUtils類,實現json與類對象的互相轉化
package com.fupin.redis.Service;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.log4j.Logger;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonUtils {
@SuppressWarnings("unused")
private static Logger logger = Logger.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 clazz
* 對象中的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;
}
}
第五部:創建aop切面方法,實現業務邏輯
package com.fupin.redis.Service;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.fupin.redis.Service.RedisEvict;
import com.fupin.redis.Service.JedisClient;
import com.fupin.redis.Service.JsonUtils;
@Aspect
@Component
public class CacheInterceptor {
@Autowired
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) {
e.printStackTrace();
System.out.println("緩存服務器出現問題,發郵箱,發信息...");
}
}
// 配置環繞方法
@Around("@annotation(redisCache)")
public Object doAround(ProceedingJoinPoint pjp, RedisCache redisCache)
throws Throwable {
//得到註解上類型
@SuppressWarnings("rawtypes")
Class modelType = redisCache.type();
//System.out.println(modelType.getName());
// 去Redis中看看有沒有我們的數據 包名+ 類名 + 方法名 + 參數(多個)
String cacheKey = redisCache.cacheKey();
System.out.println(cacheKey);
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) {
e.printStackTrace();
System.out.println("緩存服務器出現問題,發郵箱,發信息...");
}
// result是方法的最終返回結果
Object result = null;
if (null == value) {
// 緩存未命中
System.out.println("緩存未命中");
// 後端查詢數據
result = pjp.proceed();
try {//當取redis發生異常時,爲了不影響程序正常執行,需要try..catch()...
// 序列化結果放入緩存
String json = serialize(result);
jedisClient.hset(modelType.getName(), cacheKey, json);
if(redisCache.expire()>0) {
jedisClient.expire(cacheKey, redisCache.expire());//設置緩存時間
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("緩存服務器出現問題,發郵箱,發信息...");
}
} else {
try{//當數據轉換失敗發生異常時,爲了不影響程序正常執行,需要try..catch()...
// int i =1/0;
// 得到被代理方法的返回值類型
@SuppressWarnings("rawtypes")
Class returnType = ((MethodSignature) pjp.getSignature()).getReturnType();
//把json反序列化
result = deserialize(value, returnType, modelType);
// 緩存命中
System.out.println("緩存命中");
} catch (Exception e) {
//數據轉換失敗,到後端查詢數據
result = pjp.proceed();
e.printStackTrace();
System.out.println("緩存命中,但數據轉換失敗...");
}
}
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().getName();
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();
}
}
第六步:創建JedisClient方法,定義一下redis需要用到的方法
package com.fupin.redis.Service;
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);
}
第七步:創建擴展類,實現這些方法
package com.fupin.redis.Service;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import com.fupin.redis.Service.JedisClient;
public class JedisClientSingle implements JedisClient{
@Autowired
private JedisPool jedisPool;
@Override
public String get(String key) {
Jedis jedis = jedisPool.getResource();
String string = jedis.get(key);
jedis.close();
return string;
}
@Override
public String set(String key, String value) {
Jedis jedis = jedisPool.getResource();
String string = jedis.set(key, value);
jedis.close();
return string;
}
@Override
public String hget(String hkey, String key) {
Jedis jedis = jedisPool.getResource();
String string = jedis.hget(hkey, key);
jedis.close();
return string;
}
@Override
public long hset(String hkey, String key, String value) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.hset(hkey, key, value);
jedis.close();
return result;
}
@Override
public long incr(String key) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.incr(key);
jedis.close();
return result;
}
@Override
public long expire(String key, int second) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.expire(key, second);
jedis.close();
return result;
}
@Override
public long ttl(String key) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.ttl(key);
jedis.close();
return result;
}
@Override
public long del(String key) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.del(key);
jedis.close();
return result;
}
@Override
public long hdel(String hkey, String key) {
Jedis jedis = jedisPool.getResource();
Long result = jedis.hdel(hkey, key);
jedis.close();
return result;
}
@Override
public byte[] get(byte[] key) {
Jedis jedis = jedisPool.getResource();
byte[] result = jedis.get(key);
jedis.close();
return result;
}
@Override
public String set(byte[] key, byte[] value) {
Jedis jedis = jedisPool.getResource();
String result = jedis.set(key, value);
jedis.close();
return result;
}
}
第八步。創建JedisClientCluster類
package com.fupin.redis.Service;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.JedisCluster;
public class JedisClientCluster implements JedisClient {
@Autowired
private JedisCluster jedisCluster;
@Override
public String get(String key) {
return jedisCluster.get(key);
}
@Override
public String set(String key, String value) {
return jedisCluster.set(key, value);
}
@Override
public String hget(String hkey, String key) {
return jedisCluster.hget(hkey, key);
}
@Override
public long hset(String hkey, String key, String value) {
return jedisCluster.hset(hkey, key, value);
}
@Override
public long incr(String key) {
return jedisCluster.incr(key);
}
@Override
public long expire(String key, int second) {
return jedisCluster.expire(key, second);
}
@Override
public long ttl(String key) {
return jedisCluster.ttl(key);
}
@Override
public long del(String key) {
return jedisCluster.del(key);
}
@Override
public long hdel(String hkey, String key) {
return jedisCluster.hdel(hkey, key);
}
@Override
public byte[] get(byte[] key) {
// TODO Auto-generated method stub
return null;
}
@Override
public String set(byte[] key, byte[] value) {
// TODO Auto-generated method stub
return null;
}
}
第九步:在配置文件裏配置redis連接,並同時注入JedisClientSingle
<bean id="redisClient" class="redis.clients.jedis.JedisPool">
<constructor-arg name="host" value="127.0.0.1"></constructor-arg>
<constructor-arg name="port" value="6379"></constructor-arg>
</bean>
<bean id="jedisClient" class="com.fupin.redis.Service.JedisClientSingle" >
</bean>
以上就完成了所有的配置,大家注意,在配置配置文件的時候,不要忘了掃描包,具體還有什麼不明白的,可以關注我的微博‘莫失XGYL’,可以私信我啊!!!!!