一个基于注解实现的数据缓存

使用spring的面向切面变成,只需使用注解和切面,就可以实现对数据的缓存;

 


import lombok.Data;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
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.springframework.stereotype.Component;
import java.lang.ref.SoftReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicReference;

@Aspect
@Component
public class DBCacheAdvice {
    private static Logger logger = Logger.getLogger(DBCacheAdvice.class);
    private static ConcurrentHashMap<String, FutureTask<CacheObject<Object>>> pools = new ConcurrentHashMap<>();

    //定义切面、拦截规则
    @Pointcut("@annotation(DBCache)") //DBCache是自定义的注解
    public void getData() {
    }

    @Around("getData() && @annotation(dbCache)")
    public Object doAuthority(JoinPoint point, DBCache dbCache) {//使用dbCache获取注解设定的参数值
        MethodSignature signature = (MethodSignature) point.getSignature();
        String cacheName = signature.toString();//使用函数签名作为缓存数据的key,对于特定的需求,做不同的调整吧
        long duration = dbCache.duration();
        CacheObject<Object> cacheObject = null;
        try {
            cacheObject = getCacheObject(point, cacheName, duration);//主要是初始化缓存对象

            //如果缓存数据过期(超过设定有效时间)或者缓存的数据为空(主要是因为缓存的数据被设置成了softReference,有可能被虚拟机回收)
            if (this.isValid(cacheObject, duration)) {
                this.refreshData(point, cacheObject, duration);//重新获取数据
            }
            return cacheObject.getData();//返回数据(要求被拦截的方法具有返回值,这个很容易理解)
        } catch (ExecutionException | InvocationTargetException | IllegalAccessException | InterruptedException e) {
            logger.error(e);
        }
        return null;
    }

    private boolean isValid(CacheObject<Object> cacheObject, long duration) {
        long curTimestamp = Calendar.getInstance().getTimeInMillis();
        long lastTimestamp = cacheObject.getLastTimestamp();
        return duration < curTimestamp - lastTimestamp || cacheObject.getData() == null;
    }

    private void refreshData(JoinPoint point, CacheObject<Object> cacheObject, long duration) throws InvocationTargetException, IllegalAccessException {
        AtomicReference<Thread> casLock = cacheObject.getCas();
        Thread thread = Thread.currentThread();
        while (!casLock.compareAndSet(null, thread)) {//获取自旋锁,非重入锁
            logger.info("CacheObject.status.compareAndSet....");
        }
        try {
            if (this.isValid(cacheObject, duration)) {//成功获取锁后,继续判断缓存数据是否有效(有可能其他线程刚刚重新获取了数据)
                Object data = getData(point);
                cacheObject.setReference(new SoftReference<Object>(data));
                long curTimestamp = Calendar.getInstance().getTimeInMillis();
                cacheObject.setLastTimestamp(curTimestamp);
            }

        } catch (IllegalAccessException | InvocationTargetException e) {
            throw e;
        } finally {
            casLock.compareAndSet(thread, null);//释放自旋锁
        }

    }

    private Object getData(JoinPoint point) throws IllegalAccessException, InvocationTargetException {
        MethodSignature signature = (MethodSignature) point.getSignature();//获取截取的方法签名
        Method method = signature.getMethod(); //获取切面的方法
        Object[] args = point.getArgs(); //获取参数,参数没有类型,都是Object对象,要判断。
        Object obj = point.getTarget();//获取切面对象
        try {
            Object result = method.invoke(obj, args);//执行真正的获取数据的方法
            return result;
        } catch (IllegalAccessException e) {
            throw e;
        } catch (InvocationTargetException e) {
            throw e;
        }
    }

    private CacheObject<Object> getCacheObject(JoinPoint point, String cacheName, long duration) throws ExecutionException, InterruptedException {
        FutureTask<CacheObject<Object>> futureTask = pools.get(cacheName);//获取concurrentHashMap中的FutureTask
        if (futureTask != null) {
            return futureTask.get();
        } else {
            CacheCallable callable = new CacheCallable(point);//创建CacheCallable对象
            FutureTask<CacheObject<Object>> newTask = new FutureTask<>(callable);//创建任务对象
            futureTask = pools.putIfAbsent(cacheName, newTask);//将任务对象放入到concurrentHashMap中,使用putIfAbsent方法,防止多线程访问的情况
            if (futureTask == null) {
                futureTask = newTask;
                futureTask.run();//保证只有一个执行
            }
            return futureTask.get();
        }
    }

    class CacheCallable implements Callable<CacheObject<Object>> {
        private JoinPoint point;
        public CacheCallable(JoinPoint point) {
            this.point = point;
        }

        @Override
        public CacheObject<Object> call() throws InvocationTargetException, IllegalAccessException {
            Object data = getData(point);
            CacheObject<Object> cacheObject = new CacheObject();
            SoftReference<Object> reference = new SoftReference(data);
            cacheObject.setReference(reference);
            return cacheObject;
        }
    }

    @Data
    class CacheObject<T> {
        private long lastTimestamp;
        private AtomicReference<Thread> cas = new AtomicReference<>();
        private SoftReference<T> reference;

        public CacheObject() {
            this.lastTimestamp = Calendar.getInstance().getTimeInMillis();
        }

        public T getData() {
            return reference.get();
        }
    }

被截取的方法,只需要使用注解 DBCache,并设置duration为40s

    @Override
    @DBCache(duration = 20000*2)
    public Map<String, UserProfileItem> findAllUserProfileItem() {
        List<UserProfileItem> list = this.userProfileItemDao.findAllUserProfileItem();
        Map<String,UserProfileItem> map = new HashMap<>();
        for(UserProfileItem item:list){
            String itemName = item.getItemName();//.getName();
            map.put(itemName,item);
        }
        return map;
    }

注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DBCache {
    /**
     * 缓存的有效期,超过有效期,需要刷新数据,默认是1分钟
     * @return
     */
    long duration()  default 60000;
}

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值