深入理解Spring Redis的使用 (八)、Spring Redis实现 注解 自动缓存
项目中有些业务方法希望在有缓存的时候直接从缓存获取,不再执行方法,来提高吞吐率。而且这种情况有很多。如果为每一个方法都写一段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实现 注解 自动缓存的更多相关文章
- redis学习(八)——redis应用场景
毫无疑问,Redis开创了一种新的数据存储思路,使用Redis,我们不用在面对功能单调的数据库时,把精力放在如何把大象放进冰箱这样的问题上,而是利用Redis灵活多变的数据结构和数据操作,为不同的大象 ...
- Redis笔记(八)Redis的持久化
Redis相比Memcached的很大一个优势是支持数据的持久化, 通常持久化的场景一个是做数据库使用,另一个是Redis在做缓存服务器时,防止缓存失效. Redis的持久化主要有快照Snapshot ...
- Spring Boot2 系列教程(八)Spring Boot 中配置 Https
https 现在已经越来越普及了,特别是做一些小程序或者公众号开发的时候,https 基本上都是刚需了. 不过一个 https 证书还是挺费钱的,个人开发者可以在各个云服务提供商那里申请一个免费的证书 ...
- redis之(八)redis的有序集合类型的命令
[一]增加元素 --->命令:ZADD key score member [score member] --->向有序集合放入一个分数为score的member元素 --->元素存在 ...
- Redis集群(八):Redis Sharding集群
一.Redis目前的集群方案主要有两种:Redis Sharding和Redis Cluster 1.Redis Sharding:3.0以前基本上使用分片实现集群,目前主流方案,客户端实现 2.Re ...
- redis(十八):Redis 配置
#redis.conf# Redis configuration file example.# ./redis-server /path/to/redis.conf ################# ...
- Spring操作指南-IoC基础环境配置(基于注解自动装配)
项目源码:http://code.taobao.org/p/LearningJavaEE/src/LearningSpring001%20-%20Automatically%20wiring%20be ...
- Redis实战(八)Redis的配置文件介绍
https://www.cnblogs.com/ysocean/p/9074787.html
- spring boot / cloud (十八) 使用docker快速搭建本地环境
spring boot / cloud (十八) 使用docker快速搭建本地环境 在平时的开发中工作中,环境的搭建其实一直都是一个很麻烦的事情 特别是现在,系统越来越复杂,所需要连接的一些中间件也越 ...
随机推荐
- unity iOS本地代码总结(一)
1. 项目能直接运行了,但是代码的实际数据流动任然会有问题. 2. unity的代码能这么简单的被调用简直是奇迹一样,不需要大的改动就能够使用. 3. 目前需要注意的问题就是,unity的内容还太少, ...
- APP测试点注意事项汇总
1.异常测试:包括业务流程的异常情况:业务场景的异常:操作习惯的异常(比如答题过程中会出现声音干扰这样很不友好喔~) 2.网络测试:网络切换的过程中,APP会不会异常:断网情况进行一些操作,APP会不 ...
- oracle数据库学习
trunc(number[,decimals])--number 待做截取处理的数值:decimals 指明需保留小数点后面的位数 CREATE PUBLIC DATABASE LINK Co ...
- docker-compose yaml mysql和wordpress 一行命令搞定~~~
version: '3.1' services: db: container_name: db image: mysql/mysql-server restart: always networks: ...
- 记录NPOI使用方法
DLL 下载地址:https://files.cnblogs.com/files/xujunbao/NPOI.rar using NPOI.HSSF.UserModel; using NPOI.SS. ...
- tensorflow 使用 4 非线性回归
# 输入一个 x 会计算出 y 值 y 是预测值,如果与 真的 y 值(y_data)接近就成功了 import tensorflow as tf import numpy as np # py 的画 ...
- 重启Oracle 服务
1.oracle用户进入sqlplus sqlplus /nolog connect /as sysdba startup exit 2.进去操作系统启动监听 lsnrctl start 3.使用we ...
- ES6学习
一.ES6的特点 1.let(变量),const(常量) 2.在ES6中不能重复定义 3.块级作用域 普通作用域 if(true){ var test =1; } console.log(test); ...
- SharePoint Column Format
https://docs.microsoft.com/en-us/sharepoint/dev/declarative-customization/column-formatting . { &quo ...
- java代码的编译、执行过程
Java代码编译是由Java源码编译器来完成,流程图如下所示: Java字节码的执行是由JVM执行引擎来完成,流程图如下所示: Java代码编译和执行的整个过程包含了以下三个重要的机制: Java源码 ...