项目中有些业务方法希望在有缓存的时候直接从缓存获取,不再执行方法,来提高吞吐率。而且这种情况有很多。如果为每一个方法都写一段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. C++中的继承(2)类的默认成员

    在继承关系里面, 在派生类中如果没有显示定义这六个成员函数, 编译系统则会默认合成这六个默认的成员函数. 1.构造与析构函数的调用关系 调用关系先看一段代码: class Base { public ...

  2. WordPress 文章点赞

    Installation 上传 wp-zan目录 到 /wp-content/plugins/ 目录 在后台插件菜单激活该插件 添加 <?php wp_zan();?> 到需要的位置 De ...

  3. python-装饰器&生成器&迭代器&推导式

    一:普通装饰器 概念:在不改变原函数内部代码的基础上,在函数执行之前和之后自动执行某个功能,为已存在的对象添加某个功能 普通装饰器编写的格式 def 外层函数(参数) def 内层函数(*args,* ...

  4. Oracle 去特殊字符

    Create Or Replace Function Zl_Fun_去特殊字符(内容_In In Varchar2) Return Varchar2 IsBegin Return Replace(Re ...

  5. 自己动手写Redis客户端(C#实现)3 - GET请求和批量回复

    实现代码(C#) 1.发送GET指令 string keyGet = "SetKeyTest"; // 设置 的key StringBuilder sbSendGet = new ...

  6. Redis持久化之RDB

    本文及后续文章,Redis版本均是v3.2.8 上篇文章介绍了RDB的优缺点,我们先来回顾下RDB的主要原理,在某个时间点把内存中所有数据保存到磁盘文件中,这个过程既可以通过人工输入命令执行,也可以让 ...

  7. PostGIS导出SHP中文乱码

    设置系统的环境变量 PGCLIENTENCODING=GBK,退出PostgreSQL重新登錄,执行成功!

  8. 利用easygui模块编写的华氏温度与摄氏温度转换的小程序

    -*- coding:utf-8 -*- #Author:'Lmc' #DATE: 2019/4/23/0023 下午 4:23:08 #FileName:tem_compare_gui.PY imp ...

  9. AnjularJs教程

    原文地址:https://www.angular.cn/guide/quickstart#step-1-install-the-angular-cli

  10. UICollectionView使用相关博文链接

    有关UICollectionView的几篇文章:1.UICollectionView简介及简单示例: http://puttin.github.io/blog/2013/04/08/a-simple- ...