使用AOP 实现Redis缓存注解,支持SPEL
公司项目对Redis使用比较多,因为之前没有做AOP,所以缓存逻辑和业务逻辑交织在一起,维护比较艰难
所以最近实现了针对于Redis的@Cacheable,把缓存的对象依照类别分别存放到redis的Hash中,对于key也实现了SPEL支持。
1.applicationContext.xml,配置JedisPool
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="50" />
<property name="maxIdle" value="10" />
<property name="maxWaitMillis" value="1000" />
<property name="testOnBorrow" value="true" />
</bean> <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg index="0" ref="jedisPoolConfig" />
<constructor-arg index="1" value="127.0.0.1" />
<constructor-arg index="2" value="6379" />
</bean>
2.Redis的封装类,使用FastJSON进行JSON和Object的转化,这里只用到了hset,hget,hdel,其他省略了
@Component
public class RedisCacheBean {
@Resource
JedisPool jedisPool; /**
* 把对象放入Hash中
*/
public void hset(String key,String field,Object o){
Jedis jedis =jedisPool.getResource();
jedis.hset(key,field, JsonUtil.toJSONString(o));
jedisPool.returnResource(jedis);
}
/**
* 从Hash中获取对象
*/
public String hget(String key,String field){
Jedis jedis =jedisPool.getResource();
String text=jedis.hget(key,field);
jedisPool.returnResource(jedis);
return text;
}
/**
* 从Hash中获取对象,转换成制定类型
*/
public <T> T hget(String key,String field,Class<T> clazz){
String text=hget(key, field);
T result=JsonUtil.parseObject(text, clazz);
return result;
}
/**
* 从Hash中删除对象
*/
public void hdel(String key,String ... field){
Jedis jedis =jedisPool.getResource();
Object result=jedis.hdel(key,field);
jedisPool.returnResource(jedis);
} }
3.创建注解,其实大部分数据都是以hash形式存储的(使的key易于管理),所以,注解中定义了fieldKey,用作Hash的field。
/**
* 缓存注解
* @author liudajiang
*
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Cacheable {
String key();
String fieldKey() ;
int expireTime() default 3600;
}
4.定义切面,定义PointCut 表达式为注解
@Component
@Aspect
public class CacheAspect {
@Resource RedisCacheBean redis; /**
* 定义缓存逻辑
*/
@Around("@annotation(org.myshop.cache.annotation.Cacheable)")
public Object cache(ProceedingJoinPoint pjp ) {
Object result=null;
Boolean cacheEnable=SystemConfig.getInstance().getCacheEnabled();
//判断是否开启缓存
if(!cacheEnable){
try {
result= pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
return result;
} Method method=getMethod(pjp);
Cacheable cacheable=method.getAnnotation(org.myshop.cache.annotation.Cacheable.class); String fieldKey =parseKey(cacheable.fieldKey(),method,pjp.getArgs()); //获取方法的返回类型,让缓存可以返回正确的类型
Class returnType=((MethodSignature)pjp.getSignature()).getReturnType(); //使用redis 的hash进行存取,易于管理
result= redis.hget(cacheable.key(), fieldKey,returnType); if(result==null){
try {
result=pjp.proceed();
Assert.notNull(fieldKey);
redis.hset(cacheable.key(),fieldKey, result);
} catch (Throwable e) {
e.printStackTrace();
}
}
return result;
} /** * 定义清除缓存逻辑 */
@Around(value="@annotation(org.myshop.cache.annotation.CacheEvict)")
public Object evict(ProceedingJoinPoint pjp ){
//和cache类似,使用Jedis.hdel()删除缓存即可...
} /**
* 获取被拦截方法对象
*
* 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);
}
}
5.使用
@Transactional
@Cacheable(key="getAdminByName",fieldKey="#name")
public Admin getByName(String name) {
return adminDao.getByUsername(name);
}
@Transactional
@CacheEvict(key="getAdminByName",fieldKey="#admin.username")
public void update(Admin admin){
adminDao.update(admin);
}
效果:

使用AOP 实现Redis缓存注解,支持SPEL的更多相关文章
- ssm+redis 如何更简洁的利用自定义注解+AOP实现redis缓存
基于 ssm + maven + redis 使用自定义注解 利用aop基于AspectJ方式 实现redis缓存 如何能更简洁的利用aop实现redis缓存,话不多说,上demo 需求: 数据查询时 ...
- spring aop搭建redis缓存
SpringAOP与Redis搭建缓存 近期项目查询数据库太慢,持久层也没有开启二级缓存,现希望采用Redis作为缓存.为了不改写原来代码,在此采用AOP+Redis实现. 目前由于项目需要,只需要做 ...
- 【原】spring redis 缓存注解使用
由于最近新上的项目很多模块没有做数据缓存,大量的请求都会到数据库去查询,为了减轻数据库的压力以及提高网站响应速度,所以在这里采用了spring 提供的注解+redis实现对数据的缓存,主要针对非热点数 ...
- 分布式限流组件-基于Redis的注解支持的Ratelimiter
原文:https://juejin.im/entry/5bd491c85188255ac2629bef?utm_source=coffeephp.com 在分布式领域,我们难免会遇到并发量突增,对后端 ...
- redis缓存切面实现(支持缓存key的spel表达式)
1.定义注解 package com.g2.order.server.annotation; import java.lang.annotation.ElementType; import java. ...
- SpringBoot AOP控制Redis自动缓存和更新
导入redis的jar包 <!-- redis --> <dependency> <groupId>org.springframework.boot</gro ...
- Spring 缓存注解 SpEL 表达式解析
缓存注解上 key.condition.unless 等 SpEL 表达式的解析 SpEl 支持的计算变量: 1)#ai.#pi.#命名参数[i 表示参数下标,从 0 开始] 2)#result:Ca ...
- SpringBoot集成Redis分布式锁以及Redis缓存
https://blog.csdn.net/qq_26525215/article/details/79182687 集成Redis 首先在pom.xml中加入需要的redis依赖和缓存依赖 < ...
- Spring boot AOP 实现Redis 存储
package com.carloan.common.web.annotation; import java.lang.annotation.*; /** * 自定义redis缓存注解.只要在serv ...
随机推荐
- BGP详解
相信各位站长在托管服务器或者选择虚拟主机的时候,提供商都会说他们的机房是双线机房,保证你的站点访问速度,那么这里所谓的双线机房到底是何意思,它又为何能提升站点的访问速度呢? 一遍小型机房的所谓双线路其 ...
- 百万程序员的苦恼-选择VB.NET还是C#
在过去的一年中,互联网上的各大讨论区或者电子邮件的讨论列表都对微软的VB.NET以及C#的各种优越性做了探讨.这些讨论围绕的主要问题就是,我应该先学哪一个,VB.NET还是C#?? 我写这篇文章的目的 ...
- Linux Kernel KVM 'apic_get_tmcct()'函数拒绝服务漏洞
漏洞版本: Linux Kernel 漏洞描述: Bugtraq ID:64270 CVE ID:CVE-2013-6367 Linux Kernel是一款开源的操作系统. Linux KVM LAP ...
- NOI2014 动物园
3670: [Noi2014]动物园 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 174 Solved: 92[Submit][Status] D ...
- ArrayList源码解析
ArrayList简介 ArrayList定义 1 public class ArrayList<E> extends AbstractList<E> implements L ...
- spring--JDBC的支持--7
7.1 概述 7.1.1 JDBC回顾 传统应用程序开发中,进行JDBC编程是相当痛苦的,如下所示: java代码: 以上代码片段具有冗长.重复.容易忘记某一步骤从而导致出错.显示控制事务.显示处 ...
- Add controls dynamically in flowlayoutpanel
For a FlowLayoutPanel, you don't need to specify a location since the controls are arranged for you. ...
- Http get,post,soap协议的区别
转自:http://www.cnblogs.com/c2303191/articles/1107027.html 1.Http作为web服务的首选协议,具有4大优点:1)http非常简单,以纯文本(超 ...
- javaweb之servlet 全解
①Servlet概述 ⑴什么是Servlet Servlet是JavaWeb的三大组件之一,它属于动态资源.Servlet的作用是处理请求, 服务器会把接收到的请求交给Servlet来处理,在Serv ...
- 【JS】Intermediate4:JSON
1. JSON(JavaScript Object Notation) A set of text formatting rules for storing and transferring data ...