基于aop的redis自动缓存实现
目的:
对于查询接口所得到的数据,只需要配置注解,就自动存入redis!此后一定时间内,都从redis中获取数据,从而减轻数据库压力。
示例:
package com.itliucheng.biz; import com.itliucheng.annotation.CacheKey; import com.itliucheng.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; /** * Created by wangliucheng on 2017/9/14 0014. */ @Service public class DemoAnnotationService { @Cacheable(expire = 300) public List<Integer> getList(@CacheKey(key = "anyParameter") String anyParameter) { List<Integer> list = new ArrayList(); //模拟数据库操作 list.add(1); list.add(2); list.add(3); return list; } }
对于getList方法,只需要第一次查询,然后存入redis,以后的300s之内都从redis中获取数据
此示例需要了解注解,不了解的可以先看一下 注解 这篇
接下来就是具体的操作过程
学习时只关注技术,所以项目就以简单为例
1.创建maven项目,配置pom
只贴出pom,其他的都懂
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.itliucheng</groupId> <artifactId>annotation</artifactId> <version>1.0-SNAPSHOT</version> <properties> <java.version>1.8</java.version> <spring-framework.version>4.1.5.RELEASE</spring-framework.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring-framework.version}</version> </dependency> <!-- spring aop支持 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring-framework.version}</version> </dependency> <!-- aspectj支持 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.6</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.5</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.32</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> </plugins> </build> </project>
项目的结构基本是这样
2.代码部分
方法上的注解,默认永久缓存
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Cacheable { int expire() default 0; }
参数的注解,默认无参
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface CacheKey { String key() default ""; }
没有配置文件,我们都用注解的形式
@Configuration//声明当前类是一个配置类 @ComponentScan("com.itliucheng.biz")//自动扫描包下面所有@Service @Component @Repository和@Controller的类,并注册为bean @EnableAspectJAutoProxy//注解开启Spring对AspectJ的支持 public class AopConfig { }
获取jedis实例
public class JedisClient { private static class JedisHolder{ private static Jedis jedis = new Jedis("10.0.0.32",6279); } private JedisClient(){} public static Jedis getInstance(){ return JedisHolder.jedis; } }
接下来是aop的环绕通知
package com.itliucheng.biz; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; import com.itliucheng.JedisClient; import com.itliucheng.annotation.CacheKey; import com.itliucheng.annotation.Cacheable; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import redis.clients.jedis.Jedis; import java.lang.annotation.Annotation; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; /** * Created by wangliucheng on 2017/9/14 0014. */ @Aspect @Component public class CacheAspect { private static final Jedis jedis = JedisClient.getInstance(); @Around("@annotation(cache)") public Object cached(ProceedingJoinPoint pjp, Cacheable cache) { String key = this.getCacheKey(pjp); String value = jedis.get(key); if(!StringUtils.isEmpty(value)) { return this.isJSONArray(value, pjp); } Object obj = null; try { obj = pjp.proceed(); if(obj == null) { return obj; } value = JSON.toJSONString(obj); } catch (Throwable throwable) { throwable.printStackTrace(); } if(cache.expire() <= 0) { jedis.set(key, value); } else { jedis.setex(key,cache.expire(),value); } if(obj instanceof List) { if(((List)obj).size() > 0) { Class<?> entityClazz = ((List)obj).toArray()[0].getClass(); return JSONArray.parseArray(value, entityClazz); } return JSONArray.parseArray(value); } return this.isJSONArray(value, pjp); } private Object isJSONArray(String value, ProceedingJoinPoint pjp) { Object obj = JSONObject.parse(value); MethodSignature signature = (MethodSignature) pjp.getSignature(); Type type = signature.getMethod().getGenericReturnType(); try { if (obj instanceof JSONArray) { if (ParameterizedType.class.isAssignableFrom(type.getClass())) { Type[] var7 = ((ParameterizedType) type).getActualTypeArguments(); int var8 = var7.length; byte var9 = 0; if (var9 < var8) { Type t1 = var7[var9]; return JSONArray.parseArray(value, (Class) t1); } } return null; } else { return JSON.parseObject(value, Class.forName(type.getTypeName())); } } catch (ClassNotFoundException | JSONException var11) { return null; } } private String getCacheKey(ProceedingJoinPoint pjp) { StringBuilder buf = new StringBuilder(); Signature signature = pjp.getSignature(); String declaringTypeName = signature.getDeclaringTypeName(); String name = signature.getName(); buf.append(declaringTypeName).append(".").append(name); Object[] args = pjp.getArgs(); Annotation[][] pas = ((MethodSignature)signature).getMethod().getParameterAnnotations(); int length = pas.length; for(int i = 0; i < length; ++i) { Annotation[] var1 = pas[i]; int var2 = var1.length; for(int var3 = 0; var3 < var2; ++var3) { Annotation an = var1[var3]; if(an instanceof CacheKey) { buf.append("|").append(((CacheKey)an).key()).append("=").append(args[i].toString()); break; } } } return buf.toString(); } }
业务层的某个方法
package com.itliucheng.biz; import com.itliucheng.annotation.CacheKey; import com.itliucheng.annotation.Cacheable; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; /** * Created by wangliucheng on 2017/9/14 0014. */ @Service public class DemoAnnotationService { @Cacheable(expire = 300) public List<Integer> getList(@CacheKey(key = "anyParameter") String anyParameter) { List<Integer> list = new ArrayList(); //模拟数据库操作 System.out.println("我在查数据库呢!"); list.add(1); list.add(2); list.add(3); return list; } }
最后是main方法
public class test { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class); DemoAnnotationService demoAnnotationService = context.getBean(DemoAnnotationService.class); List<Integer> abc = demoAnnotationService.getList("abc"); abc.forEach(System.out::println); } }
这一套完成之后启动main方法,多次请求发现,300s内只有第一次会打印
我在查数据库呢!
1
2
3
其余均只打印123
是不是很方便?