项目中有些业务方法希望在有缓存的时候直接从缓存获取,不再执行方法,来提高吞吐率。而且这种情况有很多。如果为每一个方法都写一段if else的代码,导致耦合非常大,不方便后期的修改。

思来想去,决定使用自动注解+Spring AOP来实现。

直接贴代码。

自定义注解类:

package com.ns.annotation;

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;
import java.util.concurrent.TimeUnit;
/**
*
* ---------
* @author Han
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisCached {
/**
* redis key
* @return
*/
String value();
/**
* 过期时间,默认为1分钟,如果要设置永不过期,请设置小于等于0的值
* @return
*/
long timeout() default 1;
/**
* 时间单位,默认为分钟
* @return
*/
TimeUnit timeunit() default TimeUnit.MINUTES;
/**
* 额外定义一个空方法,调用该方法来对之前的缓存进行更新
* @return
*/
boolean forDelete() default false;
}

这个注解方便我们标志那个方法需要作为AOP的切入点。

AOP实现:

package com.ns.redis.aop;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit; import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.hibernate.metamodel.binding.Caching;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component; import com.ns.annotation.RedisCached;
import com.ns.redis.dao.base.BaseRedisDao;
/**
* 对dao的getbean的缓存处理
* order保证优先执行此切面
* @author Han
*/
@Aspect
public class AutoRedisCached extends BaseRedisDao<Object, Object> implements Ordered{
private static final Logger log = LoggerFactory.getLogger(RedisLockAspect.class);
/*
* 约束任意包下的包含Dao的类的任意方法,并且被cached注解
*/
@Pointcut("execution(* *..*(..)) && @annotation(com.ns.annotation.RedisCached)")
private void cacheMethod(){} @Around("cacheMethod()")
public Object doArround(ProceedingJoinPoint pjp) throws Throwable{
Object[] args = pjp.getArgs(); MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Method method = methodSignature.getMethod();
final RedisCached cacheinfo = method.getAnnotation(RedisCached.class); //定义序列化器
final RedisSerializer<String> keySerializer = getStringSerializer();
final RedisSerializer valueSerializer = new Jackson2JsonRedisSerializer(method.getReturnType()); //序列化参数,作为hashkey
byte [] keyBytesTemp = keySerializer.serialize(cacheinfo.value());
for(Object arg : args){
keyBytesTemp = ArrayUtils.addAll(keyBytesTemp, getDefaultSerializer().serialize(arg));
}
//取md5后key
final byte [] keyBytes = keySerializer.serialize(DigestUtils.md5Hex(keyBytesTemp)); //是删除方法
if(cacheinfo.forDelete()){
execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
return connection.del(keyBytes);
}
});
return null;
} Object obj= null;
log.info("方法"+method.getName()+"切面,尝试从缓存获取...");
obj = execute(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
byte [] tmp = connection.get(keyBytes);
return valueSerializer.deserialize(tmp);
}
});
if(obj == null){
log.info("方法"+method.getName()+"切面,缓存未找到...");
final Object objReturn = pjp.proceed();
if(objReturn != null){
execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
if(cacheinfo.timeout() > 0){
connection.setEx(keyBytes, TimeUnit.SECONDS.convert(cacheinfo.timeout(), cacheinfo.timeunit()), valueSerializer.serialize(objReturn));
}else{
connection.set(keyBytes,valueSerializer.serialize(objReturn));
}
return true;
}
});
}
obj = objReturn;
}else{
log.info("方法"+method.getName()+"切面,缓存命中...");
}
//从dao获取
return obj;
} @Override
public int getOrder() {
return -1;
}
}

注:Orderd接口是为了保证此代码优先于其他切面执行

深入理解Spring Redis的使用 (八)、Spring Redis实现 注解 自动缓存的更多相关文章

  1. redis学习(八)——redis应用场景

    毫无疑问,Redis开创了一种新的数据存储思路,使用Redis,我们不用在面对功能单调的数据库时,把精力放在如何把大象放进冰箱这样的问题上,而是利用Redis灵活多变的数据结构和数据操作,为不同的大象 ...

  2. Redis笔记(八)Redis的持久化

    Redis相比Memcached的很大一个优势是支持数据的持久化, 通常持久化的场景一个是做数据库使用,另一个是Redis在做缓存服务器时,防止缓存失效. Redis的持久化主要有快照Snapshot ...

  3. Spring Boot2 系列教程(八)Spring Boot 中配置 Https

    https 现在已经越来越普及了,特别是做一些小程序或者公众号开发的时候,https 基本上都是刚需了. 不过一个 https 证书还是挺费钱的,个人开发者可以在各个云服务提供商那里申请一个免费的证书 ...

  4. redis之(八)redis的有序集合类型的命令

    [一]增加元素 --->命令:ZADD key score member [score member] --->向有序集合放入一个分数为score的member元素 --->元素存在 ...

  5. Redis集群(八):Redis Sharding集群

    一.Redis目前的集群方案主要有两种:Redis Sharding和Redis Cluster 1.Redis Sharding:3.0以前基本上使用分片实现集群,目前主流方案,客户端实现 2.Re ...

  6. redis(十八):Redis 配置

    #redis.conf# Redis configuration file example.# ./redis-server /path/to/redis.conf ################# ...

  7. Spring操作指南-IoC基础环境配置(基于注解自动装配)

    项目源码:http://code.taobao.org/p/LearningJavaEE/src/LearningSpring001%20-%20Automatically%20wiring%20be ...

  8. Redis实战(八)Redis的配置文件介绍

    https://www.cnblogs.com/ysocean/p/9074787.html

  9. spring boot / cloud (十八) 使用docker快速搭建本地环境

    spring boot / cloud (十八) 使用docker快速搭建本地环境 在平时的开发中工作中,环境的搭建其实一直都是一个很麻烦的事情 特别是现在,系统越来越复杂,所需要连接的一些中间件也越 ...

随机推荐

  1. OpenStack--glance组件镜像服务

    glance介绍 Glance 是 OpenStack 项目中负责镜像管理的模块,其功能包括虚拟机镜像的查找,注册和检索等.Glance 提供 Restful API 可以查询虚拟机镜像的 metad ...

  2. python requests模拟登陆正方教务管理系统,并爬取成绩

    最近模拟带账号登陆,查看了一些他人的博客,发现正方教务已经更新了,所以只能自己探索了. 登陆: 通过抓包,发现需要提交的值 需要值lt,这是个啥,其实他在访问登陆页面时就产生了 session=req ...

  3. js逗号表达式

    在js中的某些场景,","是一种运算符号,只不过他的优先级要低于普通的原酸符,在变量声明或者return中,经常看到逗号表达式. 声明变量: var a=1,b=2,c=3; co ...

  4. node 和 npm 常用命令

    npm node 简述 快速入门 安装npm和管理npm版本 npm安装 更新npm npm -v npm install npm@latest -g npm install npm@next -g ...

  5. Windows下安装Redis客户端

    Redis是有名的NoSql数据库,一般Linux都会默认支持.但在Windows环境中,可能需要手动安装设置才能有效使用.这里就简单介绍一下Windows下Redis服务的安装方法,希望能够帮到你. ...

  6. 烽火2640路由器命令行手册-11-IP语音配置命令

    IP语音配置命令 目  录 第1章 配置拨号对命令... 1 1.1 配置拨号对命令... 1 1.1.1 dial-peer voice. 1 1.1.2 application. 2 1.1.3 ...

  7. CF987B - High School: Become Human

    Year 2118. Androids are in mass production for decades now, and they do all the work for humans. But ...

  8. 三、自动化测试平台搭建-django-如何用mysql数据库做web项目

    从这节开始到后面说的大概内容如下: 这里说的是Django做一个web项目的大概框架,从下篇具体说Django中的模型(查询..),视图(请求,响应,cookie,session..),模板(验证码, ...

  9. java位移运算符3 转

    https://www.cnblogs.com/winsker/p/6728672.html 移位运算符操作的对象就是二进制的位,可以单独用移位运算符来处理int型整数. 理解java移位运算符 运算 ...

  10. java位移运算符2 转

    https://blog.csdn.net/xxx134617/article/details/7454774 java中int类型占4个字节,二进制用补码表示: 3的二进制表示: 00000000 ...