java 自定义注解_Java自定义aop实现Cacheable注解(零拷贝)

开发背景:

针对Cacheable的痛点,缓存的数据在客户端中查看,如果遇到错误很难排查错误。

Cacheable不方便使用

指向针对Map类型做处理,并且Redis中也想存储成Map集合。

针对Redis的Set集合也可以开发类似的组件

1、定义注解

1)、获取map中某一项值的注解

package com.biubiu.annotation; import java.lang.annotation.*; @Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface CacheItemGet {    String key();    String hKey();}

2)、获取map集合的注解

package com.biubiu.annotation; import java.lang.annotation.*; @Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface CacheMapGet {    String key();    long expire() default 12 * 60 * 60L;    boolean parse() default false;}

3)、更新map的注解

package com.biubiu.annotation; import java.lang.annotation.*; @Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface CacheMapPut {     String key();    long expire() default 12 * 60 * 60L;    boolean parse() default false;}

2、redisTemplate

package com.biubiu;   @Configurationpublic class Config extends WebMvcConfigurationSupport {      @Bean(name = "hashRedisTemplate")    public RedisTemplate hashRedisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {        RedisTemplate template = new RedisTemplate<>();        template.setConnectionFactory(lettuceConnectionFactory);        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);        ObjectMapper om = new ObjectMapper();        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);        jackson2JsonRedisSerializer.setObjectMapper(om);        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();        // key采用String的序列化方式        template.setKeySerializer(stringRedisSerializer);        // hash的key也采用String的序列化方式        template.setHashKeySerializer(stringRedisSerializer);        // value序列化方式采用jackson        template.setValueSerializer(jackson2JsonRedisSerializer);        // hash的value序列化方式采用jackson        template.setHashValueSerializer(jackson2JsonRedisSerializer);        template.afterPropertiesSet();        return template;    }    }

3、aop切面

package com.biubiu.aop; import com.biubiu.annotation.CacheItemGet;import com.biubiu.util.CommonUtil;import com.biubiu.util.MD5;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.Qualifier;import org.springframework.core.DefaultParameterNameDiscoverer;import org.springframework.data.redis.core.HashOperations;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.expression.EvaluationContext;import org.springframework.expression.Expression;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 javax.annotation.Resource;import java.lang.reflect.Method; @Aspect@Componentpublic class CacheItemGetAspect {     @Qualifier("hashRedisTemplate")    @Resource    private RedisTemplate redisTemplate;     @Around("@annotation(com.biubiu.annotation.CacheItemGet)")    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        Method method = joinPoint.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());        CacheItemGet cacheItemGet = method.getAnnotation(CacheItemGet.class);        String key = cacheItemGet.key();        String hKeyEl = cacheItemGet.hKey();        //创建解析器        ExpressionParser parser = new SpelExpressionParser();        Expression hKeyExpression = parser.parseExpression(hKeyEl);        //设置解析上下文有哪些占位符。        EvaluationContext context = new StandardEvaluationContext();        //获取方法参数        Object[] args = joinPoint.getArgs();        String[] parameterNames = new DefaultParameterNameDiscoverer().getParameterNames(method);        for(int i = 0; i < parameterNames.length; i++) {            context.setVariable(parameterNames[i], args[i]);        }        //解析得到 item的 key        String hKeyValue = hKeyExpression.getValue(context).toString();        String hKey = MD5.getMD5Str(hKeyValue);        HashOperations ops = redisTemplate.opsForHash();        Object value = ops.get(key, hKey);        if(value != null) {            return value.toString();        }        return joinPoint.proceed();    } }
package com.biubiu.aop; import com.biubiu.annotation.CacheMapGet;import com.biubiu.annotation.CacheMapPut;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.Qualifier;import org.springframework.data.redis.core.HashOperations;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Component; import javax.annotation.Resource;import java.lang.reflect.Method;import java.util.Map;import java.util.concurrent.TimeUnit; @Aspect@Componentpublic class CacheMapGetAspect {    @Qualifier("hashRedisTemplate")    @Resource    private RedisTemplate redisTemplate;     @Around("@annotation(com.biubiu.annotation.CacheMapGet)")    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        Method method = joinPoint.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());        CacheMapGet cacheMap = method.getAnnotation(CacheMapGet.class);        String key = cacheMap.key();        //强制刷缓存        HashOperations ops = redisTemplate.opsForHash();        Object val;        Map value = ops.entries(key);        if(value.size() != 0) {            return value;        }        //加锁        synchronized (this) {            value = ops.entries(key);            if(value.size() != 0) {                return value;            }            //执行目标方法            val = joinPoint.proceed();            ops.delete(key);            //把值设置回去            ops.putAll(key, (Map) val);            redisTemplate.expire(key, cacheMap.expire(), TimeUnit.SECONDS);            return val;        }    } }
package com.biubiu.aop; import com.biubiu.annotation.CacheMapPut;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.Qualifier;import org.springframework.data.redis.core.HashOperations;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Component; import javax.annotation.Resource;import java.lang.reflect.Method;import java.util.Map;import java.util.concurrent.TimeUnit; @Aspect@Componentpublic class CacheMapPutAspect {     @Qualifier("hashRedisTemplate")    @Resource    private RedisTemplate redisTemplate;     @Around("@annotation(com.biubiu.annotation.CacheMapPut)")    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {        MethodSignature signature = (MethodSignature) joinPoint.getSignature();        Method method = joinPoint.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes());        CacheMapPut cacheMap = method.getAnnotation(CacheMapPut.class);        String key = cacheMap.key();        //强制刷缓存        HashOperations ops = redisTemplate.opsForHash();        Object val;        //加锁        synchronized (this) {            //执行目标方法            val = joinPoint.proceed();            redisTemplate.delete(key);            //把值设置回去            ops.putAll(key, (Map) val);            redisTemplate.expire(key, cacheMap.expire(), TimeUnit.SECONDS);            return val;        }    } }

4、使用方法

        @CacheItemGet(key = "biubiu-field-hash", hKey = "#tableName+#colName")    public String getValue(String tableName, String colName) {                //省略...    }
    @CacheMapGet(key = "biubiu-field-hash", expire = 6 * 60 * 60L)    public Map getFieldTypeMap() {                //省略...    }
    @CacheMapPut(key = "biubiu-field-hash", expire = 6 * 60 * 60L)    public Map putFieldTypeMap() {        //省略...    }

5、MD5工具类

package com.biubiu.util; import java.nio.charset.Charset;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException; import org.apache.log4j.LogManager;import org.apache.log4j.Logger;  /** * @Author yule.zhang * @CreateTime 2019-6-16下午05:28:11 * @Version 1.0 * @Explanation 用MD5对数据进行加密 */public class MD5 { static final Logger log = LogManager.getLogger(MD5.class); MessageDigest md5; static final char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; public MD5() {try {// 获得MD5摘要算法的 MessageDigest 对象md5 = MessageDigest.getInstance("MD5");} catch (NoSuchAlgorithmException e) {log.error("创建MD5对象出错, ", e);throw new IllegalArgumentException("创建md5对象时出错");}} public synchronized String getMD5(String s) {return this.getMD5(s.getBytes()).toLowerCase();} public synchronized String getMD5(byte[] btInput) {try {// 使用指定的字节更新摘要md5.update(btInput);// 获得密文byte[] md = md5.digest();// 把密文转换成十六进制的字符串形式int j = md.length;char str[] = new char[j * 2];int k = 0;for (int i = 0; i < j; i++) {byte byte0 = md[i];str[k++] = hexDigits[byte0 >>> 4 & 0xf];str[k++] = hexDigits[byte0 & 0xf];}return new String(str).toLowerCase();} catch (Exception e) {log.error("生成MD5码时出错,", e);throw new IllegalArgumentException("生成MD5出错");}} /**  * 获取32位的MD5加密  * @param sourceStr  * @return  */public static String getMD5Str(String sourceStr) {        String result = "";        try {            MessageDigest md = MessageDigest.getInstance("MD5");            md.update(sourceStr.getBytes(Charset.forName("utf-8")));            byte b[] = md.digest();            int i;            StringBuffer buf = new StringBuffer("");            for (int offset = 0; offset < b.length; offset++) {                i = b[offset];                if (i < 0)                    i += 256;                if (i < 16)                    buf.append("0");                buf.append(Integer.toHexString(i));            }            result = buf.toString();        } catch (NoSuchAlgorithmException e) {            System.out.println(e);        }        return result;   } }
d7c544617c76695dddf5acb3ee190488.png

祝您学习愉快

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值