基于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. 1
  3. 2
  4. 3

其余均只打印123

是不是很方便?

 

posted on 2017-09-14 16:49  itliucheng  阅读(2118)  评论(0编辑  收藏  举报