项目中有些业务方法希望在有缓存的时候直接从缓存获取,不再执行方法,来提高吞吐率。而且这种情况有很多。如果为每一个方法都写一段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. (转)前端开发-发布一个NPM包之最简单易懂流程

    原文地址:https://www.cnblogs.com/sghy/p/6829747.html 1.npm官网创建npm账户 npm网站地址:https://www.npmjs.com/ npm网站 ...

  2. BeanUtils.copyProperties缓解代码压力,释放双手

    简单描述:之前在写代码的时候,经常把表单提交到后台的对象的参数,通过getter方法取出来,然后,再通过setter方法传递给需要的对象,代码中写了很多get set这种方法,后来听同事说,sprin ...

  3. 记录下在阿里云linux上安装与配置Mysql

    环境:阿里云ECS服务器,系统为centos7.2 用户:root 参考博客:https://blog.csdn.net/kunzai6/article/details/81938613 师兄的哈哈哈 ...

  4. django信号浅谈

    Django中提供了“信号调度”,用于在框架执行操作时解耦.通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者. 1.Django内置信号 Model signals pre_in ...

  5. javaScript -- touch事件详解(touchstart、touchmove和touchend)

    HTML5中新添加了很多事件,但是由于他们的兼容问题不是很理想,应用实战性不是太强,所以在这里基本省略,咱们只分享应用广泛兼容不错的事件,日后随着兼容情况提升以后再陆续添加分享.今天为大家介绍的事件主 ...

  6. [paper reading] C-MIL: Continuation Multiple Instance Learning for Weakly Supervised Object Detection CVPR2019

    MIL陷入局部最优,检测到局部,无法完整的检测到物体.将instance划分为空间相关和类别相关的子集.在这些子集中定义一系列平滑的损失近似代替原损失函数,优化这些平滑损失. C-MIL learns ...

  7. 关于tomcat WEB-INF/lib下类加载顺序

    关于tomcat WEB-INF/lib下类加载顺序问题 问题描述 smc应用最近碰到一个线上问题,预发机器规则测试接口没问题,但是线上机器就是调用有问题,表面上看,maven模块引用的是自己想要的j ...

  8. git 修改用户名和密码

    初次运行 Git 前的配置 一般在新的系统上,我们都需要先配置下自己的 Git 工作环境.配置工作只需一次,以后升级时还会沿用现在的配置.当然,如果需要,你随时可以用相同的命令修改已有的配置. Git ...

  9. Linux下mysql开机自启动

    1,cd /etc/init.d/ 2,chmod +x mysql 3,chkconfig add mysql 4,chkconfig --list             显示服务列表 如果看到m ...

  10. 2018-4-5-MEMS

    微机电系统,研究生在学习纳米操作方面的知识的时候了解过一些,有时间的话写点东西温故知新.