1. 程式人生 > >手寫[email protected]註解 引數java物件作為ke

手寫[email protected]註解 引數java物件作為ke

1.實現方式說明

本文在---- 手寫redis @ Cacheable註解支援過期時間設定   的基礎之上進行擴充套件。

1.1問題說明:

@ Cacheable(key = “'leader'+#p0 +#p1 +#p2” )一般用法,#p0表示方法的第一個引數,#p1表示第二個引數,以此類推。

目前方法的第一個引數為Java的物件,但是原註解只支援Java的的基本資料型別。

1.2實現步驟:

1.在原註解中加入新的引數,

 objectIndexArray表示哪幾個角標引數(從0開始)為java物件,objectFieldArray表示對應位置該物件的欄位值作為key

2.如何獲取引數的物件以及該欄位的值

 使用的java的反射,拼接get方法獲取該欄位值。

2.原始碼

修改java註解@ExtCacheable,本文中使用@NewCacheable

package com.huajie.annotation;

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 NewCacheable {
	
	String key() default "";
	
	int[] objectIndexArray();

	String[] objectFieldArray();

	int expireTime() default 1800;//30分鐘
	
}

SpringAop切面NewCacheableAspect

獲取AOP整體流程沒有任何變化

主要是關鍵值獲取的方式,發生了變化

使用Java的反射技術

完整程式碼如下:

package com.huajie.aspect;

import com.huajie.annotation.NewCacheable;
import com.huajie.utils.RedisUtil;
import com.huajie.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
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.stereotype.Component;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * redis快取處理 不適用與內部方法呼叫(this.)或者private
 */
@Component
@Aspect
@Slf4j
public class NewCacheableAspect {

    @Autowired
    private RedisUtil redisUtil;

    @Pointcut("@annotation(com.huajie.annotation.NewCacheable)")
    public void annotationPointcut() {
    }

    @Around("annotationPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 獲得當前訪問的class
        Class<?> className = joinPoint.getTarget().getClass();
        // 獲得訪問的方法名
        String methodName = joinPoint.getSignature().getName();
        // 得到方法的引數的型別
        Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        Object[] args = joinPoint.getArgs();
        String key = "";
        int expireTime = 3600;
        try {
            // 得到訪問的方法物件
            Method method = className.getMethod(methodName, argClass);
            method.setAccessible(true);
            // 判斷是否存在@ExtCacheable註解
            if (method.isAnnotationPresent(NewCacheable.class)) {
                NewCacheable annotation = method.getAnnotation(NewCacheable.class);
                key = getRedisKey(args, annotation);
                expireTime = getExpireTime(annotation);
            }
        } catch (Exception e) {
            throw new RuntimeException("redis快取註解引數異常", e);
        }
        log.info(key);
        boolean hasKey = redisUtil.hasKey(key);
        if (hasKey) {
            return redisUtil.get(key);
        } else {
            Object res = joinPoint.proceed();
            redisUtil.set(key, res);
            redisUtil.expire(key, expireTime);
            return res;
        }
    }

    private int getExpireTime(NewCacheable annotation) {
        return annotation.expireTime();
    }

    private String getRedisKey(Object[] args, NewCacheable annotation) throws Exception{
        String primalKey = annotation.key();
        // 獲取#p0...集合
        List<String> keyList = getKeyParsList(primalKey);
        for (String keyName : keyList) {
            int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", ""));
            Object parValue = getParValue(annotation, keyIndex, args);
            primalKey = primalKey.replace(keyName, String.valueOf(parValue));
        }
        return primalKey.replace("+", "").replace("'", "");
    }

    private Object getParValue(NewCacheable annotation, int keyIndex, Object[] args) throws Exception{
        int[] objectIndexArray = annotation.objectIndexArray();
        String[] objectFieldArray = annotation.objectFieldArray();
        if (existsObject(keyIndex, objectIndexArray)) {
            return getParValueByObject(args, keyIndex, objectFieldArray);
        } else {
            return args[keyIndex];
        }
    }

    private Object getParValueByObject(Object[] args, int keyIndex, String[] objectFieldArray) throws Exception {
        Class cls = args[keyIndex].getClass();
        Method method;
        if(objectFieldArray!=null&&objectFieldArray.length>=keyIndex){
             method = cls.getMethod("get" + StringUtil.firstCharToUpperCase(objectFieldArray[keyIndex]));
        }else{
             method = cls.getMethod("get" + StringUtil.firstCharToUpperCase(cls.getFields()[0].getName()));
        }
        method.setAccessible(true);
        log.info(method.getName());
        return method.invoke(args[keyIndex]);
    }

    private boolean existsObject(int keyIndex, int[] objectIndexArray) {
        if (objectIndexArray == null || objectIndexArray.length <= 0) {
            return false;
        }
        for (int i = 0; i < objectIndexArray.length; i++) {
            if (keyIndex == objectIndexArray[i]) {
                return true;
            }
        }
        return false;
    }

    // 獲取key中#p0中的引數名稱
    private static List<String> getKeyParsList(String key) {
        List<String> ListPar = new ArrayList<String>();
        if (key.indexOf("#") >= 0) {
            int plusIndex = key.substring(key.indexOf("#")).indexOf("+");
            int indexNext = 0;
            String parName = "";
            int indexPre = key.indexOf("#");
            if (plusIndex > 0) {
                indexNext = key.indexOf("#") + key.substring(key.indexOf("#")).indexOf("+");
                parName = key.substring(indexPre, indexNext);
            } else {
                parName = key.substring(indexPre);
            }
            ListPar.add(parName.trim());
            key = key.substring(indexNext + 1);
            if (key.indexOf("#") >= 0) {
                ListPar.addAll(getKeyParsList(key));
            }
        }
        return ListPar;
    }

}

3.測試

業務模組使用方法controller

@RequestMapping("queryQuotaTreeData")
	@ResponseBody
	public List<TreeNode> getTreeData() {
		QuotaManage quotaManage = new QuotaManage();
		quotaManage.setQuotaName("測試22222");
		List<TreeNode> list  = this.quotaManageService.queryQuotaTreeData(quotaManage);
		return list;
	}

實現層objectIndexArray中的{0}表示第0個引數,objectFieldArray中的“quotaName”表示對應物件中的欄位名稱

@Override
	@NewCacheable(key="test+#p0",objectIndexArray = {0},objectFieldArray = {"quotaName"})
	public List<TreeNode> queryQuotaTreeData(QuotaManage quotaManage) {
		List<TreeNode> returnNodesList = new ArrayList<TreeNode>();
		List<TreeNode> nodeList = this.mapper.queryQuotaTreeData();
		returnNodesList = treeUtils.getParentList(nodeList);
		log.info(nodeList.size()+"");
		return returnNodesList;
		
	}

控制檯截圖拼接的get方法名稱和獲取的欄位值

Redis的截圖