1. 程式人生 > >深入理解Spring Redis的使用 (六)、用Spring Aop 實現註解Dao層的自動Spring Redis快取

深入理解Spring Redis的使用 (六)、用Spring Aop 實現註解Dao層的自動Spring Redis快取

摘要: 主要針對Dao層的一些資料庫查詢的操作,資料實時性不強,直接加入快取。當快取中有的時候,就使用快取中的資料。這樣的方法,最終僅僅使用一個註解實現。對於之前的hibernate二級快取使用,比較陌生。比如是否支援Redis或者可以自己開發支援。是否支援針對部分需要加入快取的方法配置,而不是所有的hibernate實體都加入快取。可能我這種方法對於二級快取來說,拋開程式碼差距,也是殊途同歸的東西。

這幾天工作中,突然遇到了對於有些個實體類,需要被快取起來。但是這些個實體類數目龐大, 初始化載入的話,太耗費時間。所以初步的方案就是先查快取,快取沒有就查詢資料庫,查完資料庫再放入快取。同時也方便設定過期時間。

但是針對目前的專案來說,Dao是作為獨立的Maven Module,Redis也是獨立的Maven Module,相互耦合的話,程式碼變得難以維護,結構不清晰。所以引入了註解,然後在Redis專案中,針對註解做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; /** * 只能註解dao裡面對應的get方法,傳遞的引數作為hashkey,返回的值作為value * @author Han */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Cached { /** * redis key *
@return */ String key(); /** * 過期時間,預設為0即永不過期 * @return */ long timeout() default 0L; /** * 時間單位,預設為秒 * @return */ TimeUnit timeunit() default TimeUnit.SECONDS; }
複製程式碼

Aop切面程式碼

複製程式碼
package com.ns.redis.aop;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.commons.collections.MapUtils;
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.springframework.beans.factory.annotation.Autowired;
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.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import com.ns.annotation.Cached;
import com.ns.redis.dao.base.BaseRedisDao;
/**
 * 對dao的getbean的快取處理
 * @author Han
 */
@Aspect
@Component
public class AutoRedisCached extends BaseRedisDao<String, Object>{
    
    /*
     * 約束任意包下的包含Dao的類的任意方法,並且被cached註解
     */
    @Pointcut("execution(* *..*Dao*.*(*,..) && @annotation(com.ns.annotation.Cached))")
    private void cacheMethod(){}
    
    @Around("cacheMethod()")
    public Object doArround(ProceedingJoinPoint pjp) throws Throwable{
        Object[] args = pjp.getArgs();
        //定義序列化器
        final RedisSerializer<String> keySerializer = getKeySerializer();
        final RedisSerializer<Object> hashValueSerializer = getHashValueSerializer();
        final RedisSerializer<Object> hashKeySerializer = getHashKeySerializer();
        
        //序列化引數,作為hashkey
        byte [] hashkeyBytesTmp = null;
        if(args.length == 1){
            hashkeyBytesTmp = hashKeySerializer.serialize(args[0]);
        }else{
            hashkeyBytesTmp = new byte[0];
            for(Object arg : args){
                hashkeyBytesTmp = ArrayUtils.addAll(hashkeyBytesTmp, hashKeySerializer.serialize(arg));
            }
        }
        
        final byte [] hashkeyBytes = hashkeyBytesTmp;
        
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        Method method = methodSignature.getMethod();
        final Cached cacheinfo = method.getAnnotation(Cached.class);
        Object obj= null;
        
        obj = execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                byte [] tmp = connection.hGet(keySerializer.serialize(cacheinfo.key()), hashkeyBytes);
                return hashValueSerializer.deserialize(tmp);
            }
        });
        if(obj == null){
            final Object objReturn = pjp.proceed();
            if(objReturn != null){
                execute(new RedisCallback<Boolean>() {
                    @Override
                    public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                        return connection.hSet(keySerializer.serialize(cacheinfo.key()), hashkeyBytes,hashValueSerializer.serialize(objReturn));
                    }
                });
                
                if(cacheinfo.timeout()>0){
                    expire(cacheinfo.key(), cacheinfo.timeout(), cacheinfo.timeunit());
                }
            }
            obj = objReturn;
        }
        //從dao獲取
        return obj;
    }
}