实际开发中我们不需要统一的去加 缓存处理,只需要 部分功能去加缓存处理,而且还是在 后期的开发中需要添加,在此提供一个 利用spring aop做切点 去加入缓存处理。只需要在需要加入缓存的地方 以注解的方式添加即可
1.SpringRedis 配置文件spring-redis.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.1.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-4.1.xsd">
<!-- 读取redis 配置文件 -->
<context:property-placeholder location="classpath*:/config/redis.properties" ignore-unresolvable="true"/>
<!-- 注解扫描 -->
<context:component-scan base-package="cn.com.teacher.redis"></context:component-scan>
<!--设置数据池-->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}"></property>
<property name="minIdle" value="${redis.minIdle}"></property>
<property name="maxTotal" value="${redis.maxTotal}"></property>
<property name="maxWaitMillis" value="${redis.maxWaitMillis}"></property>
<property name="testOnBorrow" value="${redis.testOnBorrow}"></property>
</bean>
<!--链接redis-->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}"></property>
<property name="port" value="${redis.port}"></property>
<property name="password" value="${redis.password}"></property>
<property name="poolConfig" ref="poolConfig"></property>
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="connectionFactory" >
<!--以下针对各种数据进行序列化方式的选择-->
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
</bean>
</beans>
2.Redis属性配置文件 redis.properties
redis.maxIdle=300
redis.minIdle=100
redis.maxWaitMillis=3000
redis.testOnBorrow=true
redis.maxTotal=500
redis.host=127.0.0.1
redis.port=6379
redis.password=
3.SpringMVC配置文件中添加以下代码 spring-mvc.xml
<!-- 启用切面 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
4.pom.xml 添加以下依赖
<!-- spring集成redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.1.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.3</version>
</dependency>
<!-- aspectj AOP切面 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
5.添加自定义 入缓存注解
package cn.com.teacher.redis;
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 RedisCache {
public String fieldKey(); //缓存key
public Class type();//类型
public int expire() default 0; //缓存多少秒,默认无限期
}
6.添加自定义 删除缓存注解
package cn.com.teacher.redis;
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 {
public String fieldKey(); //缓存key
public Class type();//类型
}
7.添加缓存切点
package cn.com.teacher.redis;
import java.lang.reflect.Method;
import java.util.List;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
/**
* redis切面处理
* @ClassName: CacheAspect
* @author caixl
* @date 2016-6-14 上午10:14:08
*
*/
@Component
@Aspect
public class CacheAspect {
public static final Logger infoLog = Logger.getLogger(CacheAspect.class);
@Autowired
private RedisTemplate redisTemplate;
/**
* 获取或添加缓存
* @param jp
* @param cache
* @return
* @throws Throwable
*/
@SuppressWarnings("unchecked")
@Around("@annotation(cn.com.teacher.redis.RedisCache)")
public Object RedisCache(final ProceedingJoinPoint jp)
throws Throwable {
Method method=getMethod(jp);
RedisCache cache = method.getAnnotation(RedisCache.class);
// 得到类名、方法名和参数
Object[] args = jp.getArgs();
// 根据类名,方法名和参数生成key
final String key = parseKey(cache.fieldKey(),method,jp.getArgs());
if (infoLog.isDebugEnabled()) {
infoLog.debug("生成key:" + key);
}
// 得到被代理的方法
//Method me = ((MethodSignature) jp.getSignature()).getMethod();
// 得到被代理的方法上的注解
Class modelType = method.getAnnotation(RedisCache.class).type();
// 检查redis中是否有缓存
String value = (String) redisTemplate.opsForHash().get(
modelType.getName(), key);
// result是方法的最终返回结果
Object result = null;
if (null == value) {
// 缓存未命中
if (infoLog.isDebugEnabled()) {
infoLog.debug("缓存未命中");
}
// 调用数据库查询方法
result = jp.proceed(args);
// 序列化查询结果
final String json = serialize(result);
final String hashName = modelType.getName();
final int expire = cache.expire();
// 序列化结果放入缓存
/*redisTemplate.execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection redisConn) throws DataAccessException {
// 配置文件中指定了这是一个String类型的连接
// 所以这里向下强制转换一定是安全的
StringRedisConnection conn = (StringRedisConnection) redisConn;
// 判断hash名是否存在
// 如果不存在,创建该hash并设置过期时间
if (false == conn.exists(hashName) ){
conn.hSet(hashName, key, json);
conn.expire(hashName, expire);
} else {
conn.hSet(hashName, key, json);
}
return null;
}
});*/
// 序列化结果放入缓存
redisTemplate.opsForHash().put(modelType.getName(), key, json);
} else {
// 缓存命中
if (infoLog.isDebugEnabled()) {
infoLog.debug("缓存命中, value = " + value);
}
// 得到被代理方法的返回值类型
Class returnType = ((MethodSignature) jp.getSignature())
.getReturnType();
// 反序列化从缓存中拿到的json
result = deserialize(value, returnType, modelType);
if (infoLog.isDebugEnabled()) {
infoLog.debug("反序列化结果 = {}" + result);
}
}
return result;
}
/**
* 删除缓存
* @param jp
* @param cache
* @return
* @throws Throwable
*/
@Around("@annotation(cn.com.teacher.redis.RedisEvict)")
public Object RedisEvict(final ProceedingJoinPoint jp)
throws Throwable {
// 得到被代理的方法
Method me = ((MethodSignature) jp.getSignature()).getMethod();
// 得到被代理的方法上的注解
Class modelType = me.getAnnotation(RedisEvict.class).type();
if (infoLog.isDebugEnabled()) {
infoLog.debug("清空缓存:" + modelType.getName());
}
// 清除对应缓存
/* redisTemplate.opsForHash().delete(paramH, paramArrayOfObject);*/
redisTemplate.delete(modelType.getName());
return jp.proceed(jp.getArgs());
}
/**
* 获取被拦截方法对象
*
* MethodSignature.getMethod() 获取的是顶层接口或者父类的方法对象
* 而缓存的注解在实现类的方法上
* 所以应该使用反射获取当前对象的方法对象
*/
public Method getMethod(ProceedingJoinPoint pjp){
//获取参数的类型
Object [] args=pjp.getArgs();
Class [] argTypes=new Class[pjp.getArgs().length];
for(int i=0;i<args.length;i++){
argTypes[i]=args[i].getClass();
}
Method method=null;
try {
method=pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(),argTypes);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
return method;
}
/**
* 获取缓存的key
* key 定义在注解上,支持SPEL表达式
* @param pjp
* @return
*/
private String parseKey(String key,Method method,Object [] args){
//获取被拦截方法参数名列表(使用Spring支持类库)
LocalVariableTableParameterNameDiscoverer u =
new LocalVariableTableParameterNameDiscoverer();
String [] paraNameArr=u.getParameterNames(method);
//使用SPEL进行key的解析
ExpressionParser parser = new SpelExpressionParser();
//SPEL上下文
StandardEvaluationContext context = new StandardEvaluationContext();
//把方法参数放入SPEL上下文中
for(int i=0;i<paraNameArr.length;i++){
context.setVariable(paraNameArr[i], args[i]);
}
return parser.parseExpression(key).getValue(context,String.class);
}
/**
* 序列化
* @param target
* @return
*/
protected String serialize(Object target) {
return JSON.toJSONString(target);
}
/**
* 反序列化
* @param jsonString
* @param clazz
* @param modelType
* @return
*/
protected Object deserialize(String jsonString, Class clazz, Class modelType) {
// 序列化结果应该是List对象
if (clazz.isAssignableFrom(List.class)) {
return JSON.parseArray(jsonString, modelType);
}
// 序列化结果是普通对象
return JSON.parseObject(jsonString, clazz);
}
}
8.使用方式:
@Override
@RedisCache(type=UserTest.class,fieldKey="#id")
public UserTest selectById(String id, String name) {
System.out.println(id);
System.out.println(id);
System.out.println(id);
return userTestMapper.selectById(id);
}
@Override
@RedisEvict(type=UserTest.class,fieldKey="#yhIds")
public void deleteByIds(String[] yhIds) {
if(yhIds==null||yhIds.length<=0){
throw new ServiceException("参数为空,删除用户信息失败");
}else{
System.out.println("删除缓存成功");
}
}