redis caching aspect realization (cache key expressions of support spel)

1. Definitions comment

Package com.g2.order.server.annotation; 

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

/ ** 
 * annotations Redis cache 
 * only methods 
 * / 
@Target (ElementType.METHOD) 
@Retention (RetentionPolicy.RUNTIME) 
public @ interface RedisCachetAttribute {
     / ** 
     * @return cache key value 
     * corresponding to the return value must method implements Serializable 
     * 
     * / 
    String Key (); 

    / ** 
     * expire in seconds 
     *
     *@return expiration seconds
      * / 
    int expireSeconds () default 20 is ; 
}

 

2. Definitions section

package com.g2.order.server.aspect;

import com.g2.order.server.annotation.RedisCachetAttribute;
import com.g2.order.server.utils.ObjectUtils;

import org.aspectj.lang.JoinPoint;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

importredis.clients.jedis.JedisCluster; 

// Open AspectJ automatic proxy mode, if not fill proxyTargetClass = true, defaults to false, 
@EnableAspectJAutoProxy (= the proxyTargetClass to true ) 
@Component 
the @Order ( -1 ) 
@Aspect 
public  class RedisCacheAspect {
     / * * 
     * log 
     * / 
    Private  static Logger Logger = LoggerFactory.getLogger (RedisCacheAspect. class ); 

    / ** 
     * the SPEL expression parser 
     * / 
    Private  static  Final ExpressionParser is EXPRESSION_PARSER = new new SpelExpressionParser (); 

    / ** 
     * Get parameter name discovery method device
     */
    private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();

    /**
     * Redis集群
     */
    @Autowired
    private JedisCluster jedisCluster;

    /**
     * 切面切入点
     */
    @Pointcut("@annotation(com.g2.order.server.annotation.RedisCachetAttribute)")
    public void mergeDuplicationRequest() {

    }

    /**
     * 环绕切面
     */
    @Around("mergeDuplicationRequest()")
    publicHandleControllerMethod Object (ProceedingJoinPoint ProceedingJoinPoint) throws the Throwable {
         // Get the corresponding method of the controller. 
        MethodSignature MethodSignature = (MethodSignature) proceedingJoinPoint.getSignature ();
         // Get method 
        Method, Method = methodSignature.getMethod (); 

        // Get annotation 
        RedisCachetAttribute annotation = method .getAnnotation (RedisCachetAttribute. class );
         // Get cache key expression, and the like based on the context calculation expression data 
        String = cacheKey parseKey (annotation.key (), ProceedingJoinPoint);
         int seconds the = annotation.expireSeconds ();
        // first tries to get the data (bytes) in the redis 
        byte [] = redisKey cacheKey.getBytes ();
         byte [] = redisValue jedisCluster.get (redisKey); 

        IF (! RedisValue = null && redisValue.length> 0 ) {
             // Redis data directly back 
            return ObjectUtils.toObject (redisValue); 
        } 

        // Redis no data, the original method is called, the result value acquiring 
        Object result = proceedingJoinPoint.proceed ();
         // the return value sequence into Byte [ ], to save Redis 
        redisValue = ObjectUtils.toByteArray (Result); 
        jedisCluster.setex (redisKey, seconds the, redisValue); 

        return Result;
    } 

    / ** 
     * Calculation spel expression 
     * 
     * @param expression The expression 
     * @param context context 
     * @return String Cache Key
      * / 
    Private  static String parseKey (expression The String, the JoinPoint context) {
         // Get the information entry point method 
        MethodSignature = MethodSignature (MethodSignature) context.getSignature (); 
        Method, Method = methodSignature.getMethod (); 

        // Get the value of the parameter passed 
        Object [] args = context.getArgs ();
         IF (args == null || = args.length = 0) {
             // no arguments passed directly to the calculation expression (no context parameters) 
            return EXPRESSION_PARSER.parseExpression (expression The) .getValue (String. Class ); 
        } 

        // Get Parameter Name 
        String [] = parameterNames PARAMETER_NAME_DISCOVERER.getParameterNames (Method) ;
         IF (parameterNames.length> args.length) {
             // Since java does not allow anonymous parameters, the more so if the parameter name parameter value, certainly illegal 
            logger.error ( "length parameter name parameter value is less than the length, method: {}, the parameter name length: {}, the value of the length parameter: {} " , method, parameterNames.length, args.length);
             the throw  new new an IllegalArgumentException (" argument to inadequate " ); 
        } 

        // parameter name and context parameter values into the parameter
        EvaluationContext evaluationContext = new StandardEvaluationContext();
        for (int i = 0; i < parameterNames.length; i++) {
            evaluationContext.setVariable(parameterNames[i], args[i]);
        }

        // 计算表达式(根据参数上下文)
        return EXPRESSION_PARSER.parseExpression(expression).getValue(evaluationContext, String.class);
    }
}

 

3. the reference code

package com.g2.order.server.business;

import com.google.common.collect.ImmutableMap;
import com.g2.order.server.annotation.RedisCachetAttribute;
import com.g2.order.server.business.vo.JsonResult;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * SettingBusiness.
 */
@Component
public class SettingBusiness {

    @RedisCachetAttribute(key = "T(com.g2.order.server.vo.CommonConst)" +
            ".SETTING_JSON_KEY" +
            " + #code"
            , expireSeconds = 30
    )
    public JsonResult getSetting(String code) {
        return new JsonResult("234");
    }

    @RedisCachetAttribute(key = "T(com.g2.order.server.vo.CommonConst)" +
            ".SETTING_LIST_KEY" +
            " + #code"
            , expireSeconds = 30
    )
    public List<JsonResult> getSettingList(String code) {
        return Arrays.<JsonResult>asList(new JsonResult("234"), new JsonResult("345"));
    }

    @RedisCachetAttribute(key = "T(com.g2.order.server.vo.CommonConst)" +
            ".SETTING_MAP_KEY" +
            " + #code"
            , expireSeconds = 30
    )
    public Map<String, JsonResult> getSettingMap(String code) {
        return ImmutableMap.of(code, new JsonResult("234"),"abc",new JsonResult("abc234"));
    }
}

 

package com.g2.order.server.business.vo;

import java.io.Serializable;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * Model
 */

@AllArgsConstructor
@NoArgsConstructor
@Data
public class JsonResult implements Serializable {
    private Object data;
}

 

4. Test as follows. (Acquired authentication ordinary POJO, List, Map of the return structure)

package com.g2.order.server.controller;

import com.g2.order.server.business.SettingBusiness;
import com.g2.order.server.business.vo.JsonResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
import io.swagger.annotations.Api;


@Api(value = "H5Controller", description = "H5接口")
@RestController
@RequestMapping("/h5")
public class H5Controller {
    private static Logger logger = LoggerFactory.getLogger(H5Controller.class);

    @Autowired
    private SettingBusiness settingBusiness;

    @ResponseBody
    //@MergeDuplicationRequestAttribute(redisLockKeyTemplate = "A:%s", redisLockKeyObjectFileds = {"code"})
    @RequestMapping(value = "/{code}.jsonp", method = RequestMethod.GET)
    public Object testJsonp1(@PathVariable("code") String code) {
        // return JsonMapper.INSTANCE.toJsonP("callabc111", new JsonResult("234"));
        JsonResult result = settingBusiness.getSetting(code);
        logger.info("获取结果,参数:{},值:{}", code, result);
        return result;
    }

    @ResponseBody
    //@MergeDuplicationRequestAttribute(redisLockKeyTemplate = "A:%s", redisLockKeyObjectFileds = {"code"})
    @RequestMapping(value = "/{code}.LIST", method = RequestMethod.GET)
    public Object testJsonp2(@PathVariable("code") String code) {
        // return JsonMapper.INSTANCE.toJsonP("callabc111", new JsonResult("234"));
        List<JsonResult> result = settingBusiness.getSettingList(code);
        logger.info("获取结果,参数:{},值:{}", code, result);
        return result;
    }

    @ResponseBody
    //@MergeDuplicationRequestAttribute(redisLockKeyTemplate = "A:%s", redisLockKeyObjectFileds = {"code"})
    @RequestMapping(value = "/{code}.MAP", method = RequestMethod.GET)
    public Object testJsonp3(@PathVariable("code") String code) {
        // return JsonMapper.INSTANCE.toJsonP("callabc111", new JsonResult("234"));
        Map<String,JsonResult> result = settingBusiness.getSettingMap(code);
        logger.info("获取结果,参数:{},值:{}", code, result);
        return result;
    }
}

 

The ancillary code

package com.g2.order.server.utils;

import com.google.common.collect.Lists;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Object帮助类
 */
public class ObjectUtils {

   

/**
 * 对象转Byte数组
 */
    public static byte[] toByteArray(Object obj) throws IOException {
        byte[] bytes = null;
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(bos)) {
            oos.writeObject(obj);
            oos.flush();
            bytes = bos.toByteArray();
        }
        return bytes;
    }

    /**
     * Byte数组转对象
     */
    public static Object toObject(byte[] bytes) throws IOException, ClassNotFoundException {
        Object obj = null;
        try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
             ObjectInputStream ois = new ObjectInputStream(bis)) {

            obj = ois.readObject();
        }
        return obj;
    }
}

 

package com.g2.order.server.vo;

/**
 * CommonConst 
 */
public class CommonConst {

    public static final String PREFIX = "g2:";
    public static final String SETTING_PREFIX = PREFIX + "setting:";
    /**
     * JSON_KEY
     */
    public static final String SETTING_JSON_KEY = SETTING_PREFIX + "json:";

    /**
     * LIST_KEY
     */
    public static final String SETTING_LIST_KEY = SETTING_PREFIX + "list:";

    /**
     * MAP_KEY
     */
    public static final String SETTING_MAP_KEY = SETTING_PREFIX + "map:";
}

 

6. Remarks

 

It is only a demo on, If you want to produce, you may need to do the following improvements

1. The section of code to write dead JedisCluster, where you want to modify into a single interface support / Sentinel / cluster, etc.

2. does not support millisecond storage (because jedisCluster does not support ...)

3. When there is no cache acquisition value should be based on distributed lock key to add, or likely to cause the same process multiple times

Guess you like

Origin www.cnblogs.com/zhshlimi/p/11125740.html