Spring Aopで注記Daoレイヤの自動Spring Redisキャッシュを実現

12156 ワード

要旨:主にDao層のいくつかのデータベースクエリーの操作に対して、データのリアルタイム性は強くなくて、直接キャッシュに参加します.キャッシュにある場合は、キャッシュ内のデータを使用します.このような方法は,最終的には1つの注釈のみを用いて実現される.以前のhibernate 2次キャッシュの使用については、よく知られていません.例えば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;
    }
}