AOPにカスタム注釈を加えてredis注釈キャッシュを実現


かなり前にredisを学び、今回の卒業設計ではspring-data-redisと@Cacheable、@CachePut、@CacheEveictの3つの注釈を使用してredisを使用してホットスポットデータをキャッシュしました.注釈の方式は業務の方法の中で多すぎる非業務の論理コードを雑用することを免れて、例えばログと事務、いずれもAOPで注釈に協力して共用部分を引き出して、同時に業務の方法も更に純粋で簡潔です.使用時にspring-data-redisは強力ですが、多くの機能が使えず、簡単なキャッシュ追加、削除機能しか必要とせず、カスタム注釈とAOPの知識に基づいて3つのキャッシュ注釈を実現しました.
キー(Key)
1、@Around("@annotation(myCacheAnnotation.Cache)")はspringの@Around注釈を使用して、私のカスタマイズした注釈クラスCacheに切り込みます.つまり、@Cacheという注釈を使用する方法では、実行時に@Aroundの下の方法を先に実行します.これにより、以前ビジネス方法に書いたキャッシュロジックをここに移動することができます.例えば、データを取得する前にredisサーバにキャッシュを取得することができます.キャッシュは存在しません.データベースに取得します.2、Method m = ((MethodSignature) pjp.getSignature()).getMethod(); この行のコードのmはエージェントオブジェクトであり、元のメソッドの注釈は含まれていない.Method methodWithAnnotations = pjp.getTarget().getClass().getDeclaredMethod(pjp.getSignature().getName(), m.getParameterTypes());ProceedingJoinPoint(上のpjp)のthis()はspringで生成されたエージェントオブジェクトを返し、target()はエージェントされたターゲットオブジェクトを返し、ターゲットオブジェクトは取得したmethodオブジェクトを反射して注釈を含む.3、java自体の反射機能ではメソッドパラメータ名を取得できません.C o c a l v a r i b eTableParameterName Discovererクラスでは、メソッドパラメータ名をMethodオブジェクトに基づいて取得できます.
コアコード
AOP切麺類
package myCacheAnnotation;

import com.alibaba.fastjson.JSON;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import service.IUserService;

import java.lang.reflect.Method;

/**
 * writer: holien
 * Time: 2018-01-18 11:20
 * Intent: aop  cache      
 */
@Component
@Aspect
public class CacheAspect {

    @Autowired
    private JedisPool jedisPool;

    //    Cache          
    @Around("@annotation(myCacheAnnotation.Cache)")
    private Object handleCache(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("       ");
        //          
        //   m      ,      
        Method m = ((MethodSignature) pjp.getSignature()).getMethod();
        // this()      ,target()      ,         method       
        Method methodWithAnnotations = pjp.getTarget().getClass().getDeclaredMethod(pjp.getSignature().getName(), m.getParameterTypes());
        //               
        Cache cacheAnnotation = methodWithAnnotations.getDeclaredAnnotation(myCacheAnnotation.Cache.class);
        //   key
        String keyExpr = cacheAnnotation.key();
        Object[] as = pjp.getArgs();
        String key = parseKey(methodWithAnnotations, as, keyExpr);
        //                  
//        Method methodOfAnnotation = a.getClass().getMethod("key");
        //                    
//        String key = (String) methodOfAnnotation.invoke(a);
        //  redis     
        Jedis jedis = jedisPool.getResource();
        String cache = jedis.get(key);
        if (cache == null) {
            //     ,         
            Object result = pjp.proceed();
            //          redis
            System.out.println("          JsonString    redis ");
            jedis.set(key, JSON.toJSONString(result));
            //         ,   
            int expireTime = cacheAnnotation.expire();
            if (expireTime != -1) {
                jedis.expire(key, expireTime);
            }
            return result;
        } else {
            return JSON.parse(cache);
        }
    }

    //   2      ,  3          ,   #       
    //    ,  #user.id   
    private String parseKey(Method method, Object[] argValues, String expr) {
        if (expr.contains("#")) {
            String paramName = expr.substring(expr.indexOf('#') + 1);
            //          
            LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
            String[] paramNames = discoverer.getParameterNames(method);
            for (int i = 0; i < paramNames.length; i++) {
                if (paramNames[i].equals(paramName)) {
                    return expr.substring(0, expr.indexOf('#')) + argValues[i].toString();
                }
            }
            throw new IllegalArgumentException("       ,       ");
        } else {
            //      ,    
            return expr;
        }
    }

    //    ,  #user.id   
    @Around("@annotation(myCacheAnnotation.CachePut)")
    private Object handleCachePut(ProceedingJoinPoint pjp) throws Throwable {
        return null;
    }

    @Around("@annotation(myCacheAnnotation.CacheEvict)")
    private Object handleCacheEvict(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("         ");
        //          
        //   m      ,      
        Method m = ((MethodSignature) pjp.getSignature()).getMethod();
        // this()      ,target()      ,         method       
        Method methodWithAnnotations = pjp.getTarget().getClass().getDeclaredMethod(pjp.getSignature().getName(), m.getParameterTypes());
        //               
        CacheEvict cacheEvictAnnotation = methodWithAnnotations.getDeclaredAnnotation(myCacheAnnotation.CacheEvict.class);
        //   key
        String keyExpr = cacheEvictAnnotation.key();
        Object[] as = pjp.getArgs();
        String key = parseKey(methodWithAnnotations, as, keyExpr);
        //                  
        Object result = pjp.proceed();
        Jedis jedis = jedisPool.getResource();
        jedis.del(key);
        System.out.println("          ");
        return result;
    }

    public static void main(String[] args) throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        IUserService userService = (IUserService) context.getBean("userService");
//        System.out.println(userService.getUserInfo(10));
        userService.deleteUserInfo(10);
    }

}

Cache注記クラス
package myCacheAnnotation;

import java.lang.annotation.*;

/**
 * writer: holien
 * Time: 2018-01-18 10:49
 * Intent:        (String、hash)
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Cache {
    String key();
    int expire() default -1;
}

不足と補充
この注記は、Guavaパッケージのブロンフィルタを使用して、データベースとキャッシュに存在しないクエリーをフィルタに入れ、キャッシュ破壊攻撃を防止することもできます.本来ならば、SpELによって注釈パラメータ値を解析しようとしたが、試験に成功せず、自分で簡単なXXX形式のパラメータを解析するしかなかった.XXXは方法のパラメータ名の一つであり、パラメータの属性や方法の戻り値ではなく、すなわちuserを解析できなかった.idまたは#user.getId();まとめは実は車輪を作る必要はありませんが、勉強や特定の業務を目的に小さな車輪を作る価値があり、今回の勉強もAOPと注釈の強さを体得させ、偉人の肩に立ってもっと遠くを見ることができました.